@rashidee/co2 1.3.5 → 1.3.7
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 +893 -321
- package/drizzle/0017_git_remote_url.sql +3 -0
- package/drizzle/0018_default_branch.sql +3 -0
- package/drizzle/0019_git_provider.sql +3 -0
- package/drizzle/meta/_journal.json +21 -0
- package/package.json +1 -1
- package/plugin/skills/util-gencicdscript/SKILL.md +28 -6
- package/plugin/skills/util-gencicdscript/references/cicd-app-template.md +128 -2
- package/plugin/skills/util-plancicd/SKILL.md +73 -4
- package/static/assets/{abnfDiagram-VRR7QNED-DPeOXHkD.js → abnfDiagram-VRR7QNED-CsyqZblo.js} +1 -1
- package/static/assets/{arc-Bb2ZEtXe.js → arc-it3yvCvj.js} +1 -1
- package/static/assets/{architectureDiagram-ZJ3FMSHR-D4QooL3I.js → architectureDiagram-ZJ3FMSHR-sjNv2MOQ.js} +1 -1
- package/static/assets/{blockDiagram-677ZJIJ3-BBGaxcqB.js → blockDiagram-677ZJIJ3-DW9ZjtwO.js} +1 -1
- package/static/assets/{c4Diagram-LMCZKHZV-BuFUMezL.js → c4Diagram-LMCZKHZV-DP3gJkhN.js} +1 -1
- package/static/assets/channel-C_J_aLZ7.js +1 -0
- package/static/assets/{chunk-2Q5K7J3B-MrR2dFOK.js → chunk-2Q5K7J3B-ByxTr7KB.js} +1 -1
- package/static/assets/{chunk-32BRIVSS-KMxu8q-J.js → chunk-32BRIVSS-vNETwpX4.js} +1 -1
- package/static/assets/{chunk-5VM5RSS4-Dbm1PUMI.js → chunk-5VM5RSS4-DbdsRtpE.js} +1 -1
- package/static/assets/{chunk-EX3LRPZG-C0dIkyxF.js → chunk-EX3LRPZG-DPkDGikN.js} +1 -1
- package/static/assets/{chunk-JWPE2WC7-DbWRh-D_.js → chunk-JWPE2WC7-Ccpx-Rog.js} +1 -1
- package/static/assets/{chunk-MOJQB5TN-Do9cetyI.js → chunk-MOJQB5TN-CnRPtHb3.js} +1 -1
- package/static/assets/{chunk-RYQCIY6F-CyFiM1A0.js → chunk-RYQCIY6F-C--_KRGR.js} +1 -1
- package/static/assets/{chunk-V7JOEXUC-CoFBSdV8.js → chunk-V7JOEXUC-BJQ5fwNa.js} +1 -1
- package/static/assets/{chunk-VR4S4FIN-ZJvwfHHK.js → chunk-VR4S4FIN-BPcVRYqJ.js} +1 -1
- package/static/assets/{chunk-XXDRQBXY-D8HPek0x.js → chunk-XXDRQBXY-BoC3DCKQ.js} +1 -1
- package/static/assets/classDiagram-OUVF2IWQ-yUfBrCjx.js +1 -0
- package/static/assets/classDiagram-v2-EOCWNBFH-yUfBrCjx.js +1 -0
- package/static/assets/{cose-bilkent-JH36ORCC-Cirxlh75.js → cose-bilkent-JH36ORCC-DlKrOw_E.js} +1 -1
- package/static/assets/{cynefin-VYW2F7L2-DJsuSCkt.js → cynefin-VYW2F7L2-CZQhaPM1.js} +1 -1
- package/static/assets/{cynefinDiagram-TSTJHNR4-uBfDZheC.js → cynefinDiagram-TSTJHNR4-cZP74_0I.js} +1 -1
- package/static/assets/{dagre-VKFMJZFB-BI0z1jT0.js → dagre-VKFMJZFB-tB2cBd_e.js} +1 -1
- package/static/assets/{diagram-FQU43EPY-EHIThO2P.js → diagram-FQU43EPY-QwoADT9c.js} +1 -1
- package/static/assets/{diagram-G47NLZAW-Dy2XDYy0.js → diagram-G47NLZAW-Bjz2rwUz.js} +1 -1
- package/static/assets/{diagram-NH7WQ7WH-C4g4puV-.js → diagram-NH7WQ7WH-DS9j6K7F.js} +1 -1
- package/static/assets/{diagram-OA4YK3LP-CQ16Gfax.js → diagram-OA4YK3LP-CUPwlGEi.js} +1 -1
- package/static/assets/{diagram-WEI45ONY-B4FV_zLb.js → diagram-WEI45ONY-iejOZOdV.js} +1 -1
- package/static/assets/{ebnfDiagram-CCIWWBDH-CHmsZ-vG.js → ebnfDiagram-CCIWWBDH-Bg3puNXE.js} +1 -1
- package/static/assets/{erDiagram-Q63AITRT-DCRi5XIy.js → erDiagram-Q63AITRT-DTxdGEtK.js} +1 -1
- package/static/assets/{flowDiagram-23GEKE2U-BtIBkFJl.js → flowDiagram-23GEKE2U-CRl-AJlj.js} +1 -1
- package/static/assets/{ganttDiagram-NO4QXBWP-CL02h1xy.js → ganttDiagram-NO4QXBWP-wCOJ8cfC.js} +1 -1
- package/static/assets/{gitGraphDiagram-IHSO6WYX-v3ovWEkW.js → gitGraphDiagram-IHSO6WYX-CnVtKot-.js} +1 -1
- package/static/assets/{index-BeEg-X0n.js → index-DAL1vVaa.js} +148 -146
- package/static/assets/{index-Dn_JY-18.css → index-xLqMMC0F.css} +1 -1
- package/static/assets/{infoDiagram-FWYZ7A6U-CQqDjZFw.js → infoDiagram-FWYZ7A6U-DmvEl4fi.js} +1 -1
- package/static/assets/{ishikawaDiagram-FXEZZL3T-BbtSBTuq.js → ishikawaDiagram-FXEZZL3T-CjKerAXn.js} +1 -1
- package/static/assets/{journeyDiagram-5HDEW3XC-BYiMuolz.js → journeyDiagram-5HDEW3XC-Bmc4ARKl.js} +1 -1
- package/static/assets/{kanban-definition-HUTT4EX6-ok10yIH4.js → kanban-definition-HUTT4EX6-BZ9I3imN.js} +1 -1
- package/static/assets/{linear-BzIhXjex.js → linear-CrHKLpp_.js} +1 -1
- package/static/assets/{mindmap-definition-LN4V7U3C-ByHM6OsT.js → mindmap-definition-LN4V7U3C-SUk59XXF.js} +1 -1
- package/static/assets/{pegDiagram-2B236MQR-36ZAozwu.js → pegDiagram-2B236MQR-CknFYdh_.js} +1 -1
- package/static/assets/{pieDiagram-ENE6RG2P-CiI5CYaa.js → pieDiagram-ENE6RG2P-B3VlLXXT.js} +1 -1
- package/static/assets/{quadrantDiagram-ABIIQ3AL-CqfESErc.js → quadrantDiagram-ABIIQ3AL-BUkZjvW_.js} +1 -1
- package/static/assets/{railroadDiagram-RFXS5EU6-n-HvWpNy.js → railroadDiagram-RFXS5EU6-LklmPimD.js} +1 -1
- package/static/assets/{requirementDiagram-TGXJPOKE-a4x2DB1M.js → requirementDiagram-TGXJPOKE-DnRFaZbz.js} +1 -1
- package/static/assets/{sankeyDiagram-HTMAVEWB-s-Q6aijB.js → sankeyDiagram-HTMAVEWB-D242DUlQ.js} +1 -1
- package/static/assets/{sequenceDiagram-DBY2YBRQ-DNZkVRTf.js → sequenceDiagram-DBY2YBRQ-DmiiStqo.js} +1 -1
- package/static/assets/{sizeCapture-X5ZJPWSS-B5Ciccrp.js → sizeCapture-X5ZJPWSS-cbMvR047.js} +1 -1
- package/static/assets/{stateDiagram-2N3HPSRC-BlFZbi2g.js → stateDiagram-2N3HPSRC-YIIylI9N.js} +1 -1
- package/static/assets/stateDiagram-v2-6OUMAXLB-m0MSGh0v.js +1 -0
- package/static/assets/{swimlanes-5IMT3BWC-BXRFSwye.js → swimlanes-5IMT3BWC-Ba309Ysu.js} +2 -2
- package/static/assets/swimlanesDiagram-G3AALYLV-DY_J_ih_.js +8 -0
- package/static/assets/{timeline-definition-FHXFAJF6-D9DwlOxs.js → timeline-definition-FHXFAJF6-BdpW3kGY.js} +1 -1
- package/static/assets/{vennDiagram-L72KCM5P-C80mLh7C.js → vennDiagram-L72KCM5P-BiSgQ9Lf.js} +1 -1
- package/static/assets/{wardleyDiagram-EHGQE667-ESSoASoQ.js → wardleyDiagram-EHGQE667-Clr8YWGW.js} +1 -1
- package/static/assets/{xychartDiagram-FW5EYKEG-BHN68FBi.js → xychartDiagram-FW5EYKEG-LsC066MO.js} +1 -1
- package/static/index.html +2 -2
- package/static/assets/channel-8qA4Tgiu.js +0 -1
- package/static/assets/classDiagram-OUVF2IWQ-Q3gj4hh8.js +0 -1
- package/static/assets/classDiagram-v2-EOCWNBFH-Q3gj4hh8.js +0 -1
- package/static/assets/stateDiagram-v2-6OUMAXLB-BclL0t8p.js +0 -1
- package/static/assets/swimlanesDiagram-G3AALYLV-CuNyWLnI.js +0 -8
package/dist/index.js
CHANGED
|
@@ -1267,8 +1267,8 @@ var require_command = __commonJS({
|
|
|
1267
1267
|
* @return {Command} `this` command for chaining
|
|
1268
1268
|
*/
|
|
1269
1269
|
arguments(names) {
|
|
1270
|
-
names.trim().split(/ +/).forEach((
|
|
1271
|
-
this.argument(
|
|
1270
|
+
names.trim().split(/ +/).forEach((detail2) => {
|
|
1271
|
+
this.argument(detail2);
|
|
1272
1272
|
});
|
|
1273
1273
|
return this;
|
|
1274
1274
|
}
|
|
@@ -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.7",
|
|
6813
6813
|
description: "Compound Context Studio \u2014 self-hosted team context authoring for CO2 projects",
|
|
6814
6814
|
type: "module",
|
|
6815
6815
|
license: "MIT",
|
|
@@ -6937,9 +6937,9 @@ ${install.output}`));
|
|
|
6937
6937
|
}
|
|
6938
6938
|
|
|
6939
6939
|
// src/commands/start.ts
|
|
6940
|
-
import { existsSync as
|
|
6940
|
+
import { existsSync as existsSync25 } from "fs";
|
|
6941
6941
|
import { networkInterfaces } from "os";
|
|
6942
|
-
import { dirname as dirname3, join as
|
|
6942
|
+
import { dirname as dirname3, join as join29 } from "path";
|
|
6943
6943
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
6944
6944
|
|
|
6945
6945
|
// ../../node_modules/.pnpm/hono@4.12.27/node_modules/hono/dist/compose.js
|
|
@@ -10241,7 +10241,7 @@ function sql(strings, ...params) {
|
|
|
10241
10241
|
return new SQL([new StringChunk(str)]);
|
|
10242
10242
|
}
|
|
10243
10243
|
sql2.raw = raw2;
|
|
10244
|
-
function
|
|
10244
|
+
function join30(chunks, separator) {
|
|
10245
10245
|
const result = [];
|
|
10246
10246
|
for (const [i, chunk] of chunks.entries()) {
|
|
10247
10247
|
if (i > 0 && separator !== void 0) {
|
|
@@ -10251,7 +10251,7 @@ function sql(strings, ...params) {
|
|
|
10251
10251
|
}
|
|
10252
10252
|
return new SQL(result);
|
|
10253
10253
|
}
|
|
10254
|
-
sql2.join =
|
|
10254
|
+
sql2.join = join30;
|
|
10255
10255
|
function identifier(value) {
|
|
10256
10256
|
return new Name(value);
|
|
10257
10257
|
}
|
|
@@ -12760,7 +12760,7 @@ var SQLiteSelectQueryBuilderBase = class extends TypedQueryBuilder {
|
|
|
12760
12760
|
return (table, on) => {
|
|
12761
12761
|
const baseTableName = this.tableName;
|
|
12762
12762
|
const tableName = getTableLikeName(table);
|
|
12763
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
12763
|
+
if (typeof tableName === "string" && this.config.joins?.some((join30) => join30.alias === tableName)) {
|
|
12764
12764
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
12765
12765
|
}
|
|
12766
12766
|
if (!this.isPartialSelect) {
|
|
@@ -13563,7 +13563,7 @@ var SQLiteUpdateBase = class extends QueryPromise {
|
|
|
13563
13563
|
createJoin(joinType) {
|
|
13564
13564
|
return (table, on) => {
|
|
13565
13565
|
const tableName = getTableLikeName(table);
|
|
13566
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
13566
|
+
if (typeof tableName === "string" && this.config.joins.some((join30) => join30.alias === tableName)) {
|
|
13567
13567
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
13568
13568
|
}
|
|
13569
13569
|
if (typeof on === "function") {
|
|
@@ -14490,6 +14490,17 @@ var projects = sqliteTable("projects", {
|
|
|
14490
14490
|
// existing CO2 repo (no scaffolding, no brainstorm). importedBy is a soft ref → users.id.
|
|
14491
14491
|
importedAt: text("imported_at"),
|
|
14492
14492
|
importedBy: integer("imported_by"),
|
|
14493
|
+
// [v2.2.0] Per-project git remote (push destination). Auto-seeded from an imported repo's
|
|
14494
|
+
// origin; editable in project detail. Never carries an embedded credential.
|
|
14495
|
+
gitRemoteUrl: text("git_remote_url"),
|
|
14496
|
+
// [v2.3.0] Per-project default (trunk) branch — the branch release branches merge back into on
|
|
14497
|
+
// a version cut (runVersionCutGit). Editable in the project detail Configuration tab; defaults
|
|
14498
|
+
// to `main`. Existing rows backfill to `main`.
|
|
14499
|
+
defaultBranch: text("default_branch").notNull().default("main"),
|
|
14500
|
+
// [v2.3.0] Git hosting provider — drives the PAT auth-username convention (GitHub
|
|
14501
|
+
// `x-access-token` vs GitLab `oauth2`) when the acting user has no configured Git name.
|
|
14502
|
+
// Auto-seeded from an imported repo's origin host; defaults to `github`.
|
|
14503
|
+
gitProvider: text("git_provider", { enum: ["github", "gitlab"] }).notNull().default("github"),
|
|
14493
14504
|
createdBy: integer("created_by").notNull(),
|
|
14494
14505
|
createdAt: text("created_at").notNull(),
|
|
14495
14506
|
updatedAt: text("updated_at").notNull()
|
|
@@ -19079,6 +19090,68 @@ var documentSchema = external_exports.object({
|
|
|
19079
19090
|
});
|
|
19080
19091
|
var MAX_DOCUMENT_BYTES = 25 * 1024 * 1024;
|
|
19081
19092
|
|
|
19093
|
+
// ../core/src/schemas/git.ts
|
|
19094
|
+
var gitFileStatusSchema = external_exports.enum([
|
|
19095
|
+
"modified",
|
|
19096
|
+
"added",
|
|
19097
|
+
"deleted",
|
|
19098
|
+
"renamed",
|
|
19099
|
+
"untracked",
|
|
19100
|
+
"conflicted",
|
|
19101
|
+
"typechange"
|
|
19102
|
+
]);
|
|
19103
|
+
var gitFileSchema = external_exports.object({
|
|
19104
|
+
path: external_exports.string(),
|
|
19105
|
+
status: gitFileStatusSchema,
|
|
19106
|
+
staged: external_exports.boolean(),
|
|
19107
|
+
unstaged: external_exports.boolean(),
|
|
19108
|
+
conflicted: external_exports.boolean(),
|
|
19109
|
+
/** Raw porcelain XY codes, retained for display / debugging. */
|
|
19110
|
+
indexStatus: external_exports.string(),
|
|
19111
|
+
worktreeStatus: external_exports.string()
|
|
19112
|
+
});
|
|
19113
|
+
var gitStatusResponseSchema = external_exports.object({
|
|
19114
|
+
/** false when the project folder is missing or has no git repository. */
|
|
19115
|
+
available: external_exports.boolean(),
|
|
19116
|
+
branch: external_exports.string().nullable(),
|
|
19117
|
+
upstream: external_exports.string().nullable(),
|
|
19118
|
+
ahead: external_exports.number(),
|
|
19119
|
+
behind: external_exports.number(),
|
|
19120
|
+
clean: external_exports.boolean(),
|
|
19121
|
+
hasConflicts: external_exports.boolean(),
|
|
19122
|
+
files: external_exports.array(gitFileSchema),
|
|
19123
|
+
/** Convenience: files.length (the dirty count the topbar indicator badges). */
|
|
19124
|
+
changedCount: external_exports.number()
|
|
19125
|
+
});
|
|
19126
|
+
var gitDiffResponseSchema = external_exports.object({
|
|
19127
|
+
path: external_exports.string(),
|
|
19128
|
+
status: gitFileStatusSchema,
|
|
19129
|
+
binary: external_exports.boolean(),
|
|
19130
|
+
conflicted: external_exports.boolean(),
|
|
19131
|
+
/** Non-conflicted compare — HEAD (before) vs working tree (after). null where absent. */
|
|
19132
|
+
oldContent: external_exports.string().nullable(),
|
|
19133
|
+
newContent: external_exports.string().nullable(),
|
|
19134
|
+
/** Conflicted 3-way — base (merged ancestor), ours (local), theirs (remote). null where absent. */
|
|
19135
|
+
base: external_exports.string().nullable(),
|
|
19136
|
+
ours: external_exports.string().nullable(),
|
|
19137
|
+
theirs: external_exports.string().nullable()
|
|
19138
|
+
});
|
|
19139
|
+
var gitCommitRequestSchema = external_exports.object({
|
|
19140
|
+
message: external_exports.string().trim().min(1, "A commit message is required").max(500),
|
|
19141
|
+
paths: external_exports.array(external_exports.string()).optional()
|
|
19142
|
+
});
|
|
19143
|
+
var gitDiscardRequestSchema = external_exports.object({
|
|
19144
|
+
paths: external_exports.array(external_exports.string()).min(1, "Select at least one file to roll back")
|
|
19145
|
+
});
|
|
19146
|
+
var gitResolveRequestSchema = external_exports.object({
|
|
19147
|
+
path: external_exports.string().min(1),
|
|
19148
|
+
resolution: external_exports.enum(["ours", "theirs", "manual"]),
|
|
19149
|
+
content: external_exports.string().optional()
|
|
19150
|
+
}).refine((v) => v.resolution !== "manual" || v.content !== void 0, {
|
|
19151
|
+
message: "Manual resolution requires the merged content",
|
|
19152
|
+
path: ["content"]
|
|
19153
|
+
});
|
|
19154
|
+
|
|
19082
19155
|
// ../core/src/schemas/import.ts
|
|
19083
19156
|
var importCandidateSchema = external_exports.object({
|
|
19084
19157
|
folderName: external_exports.string(),
|
|
@@ -19287,6 +19360,16 @@ var brainstormMdResponseSchema = external_exports.object({
|
|
|
19287
19360
|
var updateBrainstormMdSchema = external_exports.object({
|
|
19288
19361
|
content: external_exports.string().min(1, "Brainstorm content must not be empty")
|
|
19289
19362
|
});
|
|
19363
|
+
var gitUrlOrEmpty = /^$|^https:\/\/[^@/\s]+(\/\S*)?$|^(git@|ssh:\/\/)\S+$/;
|
|
19364
|
+
var branchNameOrEmpty = /^$|^(?!.*\.\.)[A-Za-z0-9][A-Za-z0-9._/-]*$/;
|
|
19365
|
+
var updateProjectGitRemoteSchema = external_exports.object({
|
|
19366
|
+
gitRemoteUrl: external_exports.string().trim().max(500).regex(gitUrlOrEmpty, "Must be empty or an https:// or ssh/git@ URL"),
|
|
19367
|
+
// [v2.3.0] Default (trunk) branch releases merge back into on a version cut. Optional for
|
|
19368
|
+
// backward compatibility; empty/omitted resolves to `main` server-side.
|
|
19369
|
+
defaultBranch: external_exports.string().trim().max(255).regex(branchNameOrEmpty, "Must be a valid branch name").default("main"),
|
|
19370
|
+
// [v2.3.0] Git hosting provider — drives the PAT auth-username convention. Defaults to `github`.
|
|
19371
|
+
gitProvider: external_exports.enum(["github", "gitlab"]).default("github")
|
|
19372
|
+
});
|
|
19290
19373
|
var projectSummarySchema = external_exports.object({
|
|
19291
19374
|
id: external_exports.number(),
|
|
19292
19375
|
name: external_exports.string(),
|
|
@@ -19304,6 +19387,12 @@ var projectDetailSchema = external_exports.object({
|
|
|
19304
19387
|
workflowPhase: workflowPhaseSchema,
|
|
19305
19388
|
creationStatus: creationStatusSchema.nullable(),
|
|
19306
19389
|
isArchived: external_exports.boolean(),
|
|
19390
|
+
// [v2.2.0] Push destination for Source Control (imported repos auto-seed it from origin).
|
|
19391
|
+
gitRemoteUrl: external_exports.string().nullable(),
|
|
19392
|
+
// [v2.3.0] Default (trunk) branch releases merge back into on a version cut (defaults to `main`).
|
|
19393
|
+
defaultBranch: external_exports.string(),
|
|
19394
|
+
// [v2.3.0] Git hosting provider (drives the PAT auth-username convention; defaults to `github`).
|
|
19395
|
+
gitProvider: external_exports.enum(["github", "gitlab"]),
|
|
19307
19396
|
applications: external_exports.array(
|
|
19308
19397
|
external_exports.object({
|
|
19309
19398
|
id: external_exports.number(),
|
|
@@ -19682,11 +19771,11 @@ process.once("SIGINT", () => {
|
|
|
19682
19771
|
function resolveHub(db, workspacePath, projectId) {
|
|
19683
19772
|
const project = db.select().from(projects).where(eq(projects.id, projectId)).limit(1).get();
|
|
19684
19773
|
if (!project) throw svcError("PROJECT_NOT_FOUND", 404);
|
|
19685
|
-
const
|
|
19686
|
-
const hubDir = [join2(
|
|
19774
|
+
const projectDir2 = join2(workspacePath, project.workspaceFolderName);
|
|
19775
|
+
const hubDir = [join2(projectDir2, "mockup"), join2(projectDir2, "source", "mockup")].find(
|
|
19687
19776
|
(d) => existsSync2(join2(d, "server.js"))
|
|
19688
19777
|
);
|
|
19689
|
-
if (!hubDir) return { hubDir: join2(
|
|
19778
|
+
if (!hubDir) return { hubDir: join2(projectDir2, "mockup"), present: false, port: 3e3 };
|
|
19690
19779
|
let port = 3e3;
|
|
19691
19780
|
try {
|
|
19692
19781
|
const config = JSON.parse(readFileSync2(join2(hubDir, "mockup.config.json"), "utf8"));
|
|
@@ -19782,17 +19871,17 @@ function registerArtifact(db, conductorRunId, applicationId, artifactType, fileP
|
|
|
19782
19871
|
updatedAt: now
|
|
19783
19872
|
}).returning({ id: artifacts.id }).get().id;
|
|
19784
19873
|
}
|
|
19785
|
-
function resolveAppFolderName(
|
|
19874
|
+
function resolveAppFolderName(projectDir2, appName) {
|
|
19786
19875
|
const candidates = [appName, appName.replace(/-/g, "_"), appName.replace(/_/g, "-")];
|
|
19787
19876
|
for (const candidate of candidates) {
|
|
19788
|
-
if (existsSync3(join3(
|
|
19789
|
-
if (existsSync3(join3(
|
|
19877
|
+
if (existsSync3(join3(projectDir2, candidate))) return candidate;
|
|
19878
|
+
if (existsSync3(join3(projectDir2, "source", candidate))) return `source/${candidate}`;
|
|
19790
19879
|
}
|
|
19791
19880
|
const normalize2 = (s) => s.replace(/^\d+[_-]/, "").toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
19792
19881
|
const target = normalize2(appName);
|
|
19793
19882
|
const prefixMatches = [];
|
|
19794
19883
|
for (const base of ["", "source"]) {
|
|
19795
|
-
const dir = base ? join3(
|
|
19884
|
+
const dir = base ? join3(projectDir2, base) : projectDir2;
|
|
19796
19885
|
if (!existsSync3(dir)) continue;
|
|
19797
19886
|
for (const entry of readdirSync(dir)) {
|
|
19798
19887
|
try {
|
|
@@ -19847,9 +19936,9 @@ function registerConductorArtifacts(db, workspacePath, runId) {
|
|
|
19847
19936
|
const project = db.select().from(projects).where(eq(projects.id, run.projectId)).get();
|
|
19848
19937
|
const app = db.select().from(applications).where(eq(applications.id, run.applicationId)).get();
|
|
19849
19938
|
if (!project || !app) return;
|
|
19850
|
-
const
|
|
19851
|
-
const appRel = resolveAppFolderName(
|
|
19852
|
-
const contextDir = join3(
|
|
19939
|
+
const projectDir2 = join3(workspacePath, project.workspaceFolderName);
|
|
19940
|
+
const appRel = resolveAppFolderName(projectDir2, app.name);
|
|
19941
|
+
const contextDir = join3(projectDir2, appRel, "context");
|
|
19853
19942
|
const slugs = /* @__PURE__ */ new Set();
|
|
19854
19943
|
for (const sub of ["model", "specification", "test"]) {
|
|
19855
19944
|
const dir = join3(contextDir, sub);
|
|
@@ -19891,7 +19980,7 @@ function registerConductorArtifacts(db, workspacePath, runId) {
|
|
|
19891
19980
|
}
|
|
19892
19981
|
}
|
|
19893
19982
|
if (run.conductorType !== "prepare") {
|
|
19894
|
-
for (const srcRel of sweepSourceFiles(join3(
|
|
19983
|
+
for (const srcRel of sweepSourceFiles(join3(projectDir2, appRel))) {
|
|
19895
19984
|
registerArtifact(db, runId, app.id, "source_code", `${appRel}/${srcRel}`, srcRel);
|
|
19896
19985
|
}
|
|
19897
19986
|
}
|
|
@@ -19949,9 +20038,9 @@ function getModuleModel(db, workspacePath, projectId, applicationId, moduleName)
|
|
|
19949
20038
|
const app = db.select().from(applications).where(eq(applications.id, applicationId)).get();
|
|
19950
20039
|
if (!app) throw svcError2("APP_NOT_FOUND", 404);
|
|
19951
20040
|
const kebab = moduleName.toLowerCase().replace(/\s+/g, "-");
|
|
19952
|
-
const
|
|
19953
|
-
const appRel = resolveAppFolderName(
|
|
19954
|
-
const modelPath = join3(
|
|
20041
|
+
const projectDir2 = join3(workspacePath, project.workspaceFolderName);
|
|
20042
|
+
const appRel = resolveAppFolderName(projectDir2, app.name);
|
|
20043
|
+
const modelPath = join3(projectDir2, appRel, "context", "model", kebab, "model.md");
|
|
19955
20044
|
if (!existsSync3(modelPath)) throw svcError2("MODEL_NOT_FOUND", 404);
|
|
19956
20045
|
return { content: readFileSync3(modelPath, "utf8") };
|
|
19957
20046
|
}
|
|
@@ -20777,17 +20866,101 @@ function writeSectionToDisk(db, workspacePath, projectId, applicationId, section
|
|
|
20777
20866
|
import { execFileSync } from "child_process";
|
|
20778
20867
|
import { existsSync as existsSync6 } from "fs";
|
|
20779
20868
|
import { join as join6 } from "path";
|
|
20780
|
-
function
|
|
20781
|
-
return
|
|
20782
|
-
|
|
20783
|
-
|
|
20784
|
-
|
|
20785
|
-
|
|
20786
|
-
}
|
|
20869
|
+
function defaultAuthUsername(provider) {
|
|
20870
|
+
return provider === "gitlab" ? "oauth2" : "x-access-token";
|
|
20871
|
+
}
|
|
20872
|
+
function buildPushAuthArgs(auth) {
|
|
20873
|
+
if (!auth) return [];
|
|
20874
|
+
const b64 = Buffer.from(`${auth.username}:${auth.token}`).toString("base64");
|
|
20875
|
+
return ["-c", `http.extraHeader=AUTHORIZATION: basic ${b64}`];
|
|
20787
20876
|
}
|
|
20788
|
-
function
|
|
20877
|
+
function redactAuthHeaderFromError(e, b64) {
|
|
20878
|
+
const err = e;
|
|
20879
|
+
if (typeof err.message === "string") err.message = err.message.split(b64).join("***");
|
|
20880
|
+
if (typeof err.cmd === "string") err.cmd = err.cmd.split(b64).join("***");
|
|
20881
|
+
if (Array.isArray(err.spawnargs)) {
|
|
20882
|
+
err.spawnargs = err.spawnargs.map((a) => typeof a === "string" ? a.split(b64).join("***") : a);
|
|
20883
|
+
}
|
|
20884
|
+
}
|
|
20885
|
+
function git(cwd, args, auth) {
|
|
20886
|
+
try {
|
|
20887
|
+
return execFileSync("git", [...buildPushAuthArgs(auth), ...args], {
|
|
20888
|
+
cwd,
|
|
20889
|
+
stdio: "pipe",
|
|
20890
|
+
timeout: 6e4,
|
|
20891
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
20892
|
+
}).toString().trim();
|
|
20893
|
+
} catch (e) {
|
|
20894
|
+
if (auth) {
|
|
20895
|
+
const b64 = Buffer.from(`${auth.username}:${auth.token}`).toString("base64");
|
|
20896
|
+
redactAuthHeaderFromError(e, b64);
|
|
20897
|
+
}
|
|
20898
|
+
throw e;
|
|
20899
|
+
}
|
|
20900
|
+
}
|
|
20901
|
+
function readCurrentBranch(projectDir2) {
|
|
20902
|
+
try {
|
|
20903
|
+
return git(projectDir2, ["branch", "--show-current"]);
|
|
20904
|
+
} catch {
|
|
20905
|
+
return "";
|
|
20906
|
+
}
|
|
20907
|
+
}
|
|
20908
|
+
function readOriginUrl(projectDir2) {
|
|
20909
|
+
try {
|
|
20910
|
+
const url = git(projectDir2, ["config", "--get", "remote.origin.url"]);
|
|
20911
|
+
return url === "" ? void 0 : url;
|
|
20912
|
+
} catch {
|
|
20913
|
+
return void 0;
|
|
20914
|
+
}
|
|
20915
|
+
}
|
|
20916
|
+
function stripHttpsCredentials(url) {
|
|
20917
|
+
const scheme = url.startsWith("https://") ? "https://" : url.startsWith("http://") ? "http://" : null;
|
|
20918
|
+
if (!scheme) return url;
|
|
20919
|
+
const rest = url.slice(scheme.length);
|
|
20920
|
+
const slash = rest.indexOf("/");
|
|
20921
|
+
const authority = slash === -1 ? rest : rest.slice(0, slash);
|
|
20922
|
+
const at = authority.indexOf("@");
|
|
20923
|
+
if (at === -1) return url;
|
|
20924
|
+
return scheme + rest.slice(at + 1);
|
|
20925
|
+
}
|
|
20926
|
+
function ensureOrigin(projectDir2, url) {
|
|
20927
|
+
const remotes = git(projectDir2, ["remote"]).split("\n");
|
|
20928
|
+
if (remotes.includes("origin")) git(projectDir2, ["remote", "set-url", "origin", url]);
|
|
20929
|
+
else git(projectDir2, ["remote", "add", "origin", url]);
|
|
20930
|
+
}
|
|
20931
|
+
function resolveGitPushContext(db, projectId, userId) {
|
|
20932
|
+
const project = db.select({
|
|
20933
|
+
gitRemoteUrl: projects.gitRemoteUrl,
|
|
20934
|
+
defaultBranch: projects.defaultBranch,
|
|
20935
|
+
gitProvider: projects.gitProvider
|
|
20936
|
+
}).from(projects).where(eq(projects.id, projectId)).get();
|
|
20937
|
+
const remoteUrl = project?.gitRemoteUrl ?? void 0;
|
|
20938
|
+
const defaultBranch = project?.defaultBranch ?? void 0;
|
|
20939
|
+
const provider = project?.gitProvider ?? void 0;
|
|
20940
|
+
if (!userId) return { remoteUrl, defaultBranch, provider };
|
|
20941
|
+
const user = db.select({ gitName: users.gitName, gitPat: users.gitPat }).from(users).where(eq(users.id, userId)).get();
|
|
20942
|
+
if (!user?.gitPat) return { remoteUrl, defaultBranch, provider };
|
|
20943
|
+
return {
|
|
20944
|
+
remoteUrl,
|
|
20945
|
+
defaultBranch,
|
|
20946
|
+
provider,
|
|
20947
|
+
auth: { username: user.gitName || defaultAuthUsername(provider), token: user.gitPat }
|
|
20948
|
+
};
|
|
20949
|
+
}
|
|
20950
|
+
function deriveVersionBranch(semver, trunk = "main") {
|
|
20789
20951
|
const s = semver ?? "v1.0.0";
|
|
20790
|
-
return s === "v1.0.0" ?
|
|
20952
|
+
return s === "v1.0.0" ? trunk : `release-${s.replace(/^v/, "")}`;
|
|
20953
|
+
}
|
|
20954
|
+
function detectTrunkBranch(projectDir2, startingBranch) {
|
|
20955
|
+
try {
|
|
20956
|
+
const ref = git(projectDir2, ["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"]);
|
|
20957
|
+
const name = ref.startsWith("origin/") ? ref.slice("origin/".length) : "";
|
|
20958
|
+
if (name) return name;
|
|
20959
|
+
} catch {
|
|
20960
|
+
}
|
|
20961
|
+
if (branchExists(projectDir2, "main")) return "main";
|
|
20962
|
+
if (branchExists(projectDir2, "master")) return "master";
|
|
20963
|
+
return startingBranch || "main";
|
|
20791
20964
|
}
|
|
20792
20965
|
var GitCutError = class extends Error {
|
|
20793
20966
|
code = "GIT_OPERATION_FAILED";
|
|
@@ -20795,117 +20968,120 @@ var GitCutError = class extends Error {
|
|
|
20795
20968
|
};
|
|
20796
20969
|
function gitErrorDetail(e) {
|
|
20797
20970
|
const stderr = e.stderr?.toString().trim();
|
|
20798
|
-
const
|
|
20799
|
-
return
|
|
20971
|
+
const detail2 = stderr || e.message || String(e);
|
|
20972
|
+
return detail2.replace(/(https?:\/\/)[^@/\s]+@/gi, "$1***@").replace(/authorization:\s*basic\s+\S+/gi, "AUTHORIZATION: basic ***");
|
|
20800
20973
|
}
|
|
20801
|
-
function branchExists(
|
|
20974
|
+
function branchExists(projectDir2, branch) {
|
|
20802
20975
|
try {
|
|
20803
|
-
git(
|
|
20976
|
+
git(projectDir2, ["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`]);
|
|
20804
20977
|
return true;
|
|
20805
20978
|
} catch {
|
|
20806
20979
|
return false;
|
|
20807
20980
|
}
|
|
20808
20981
|
}
|
|
20809
|
-
function runVersionCutGit(
|
|
20810
|
-
const previousBranch = deriveVersionBranch(prevSemver);
|
|
20982
|
+
function runVersionCutGit(projectDir2, prevSemver, newSemver, opts) {
|
|
20811
20983
|
const newBranch = `release-${newSemver.replace(/^v/, "")}`;
|
|
20812
|
-
if (!existsSync6(join6(
|
|
20984
|
+
if (!existsSync6(join6(projectDir2, ".git"))) {
|
|
20813
20985
|
throw new GitCutError(
|
|
20814
|
-
`${
|
|
20986
|
+
`${projectDir2} has no git repository \u2014 run "git init" in the project folder before adding a version`
|
|
20815
20987
|
);
|
|
20816
20988
|
}
|
|
20817
20989
|
const fail = (step, e) => {
|
|
20818
20990
|
throw new GitCutError(`${step} failed: ${gitErrorDetail(e)}`);
|
|
20819
20991
|
};
|
|
20820
|
-
const startingBranch = git(
|
|
20992
|
+
const startingBranch = git(projectDir2, ["branch", "--show-current"]);
|
|
20821
20993
|
if (startingBranch === "") {
|
|
20822
20994
|
throw new GitCutError(
|
|
20823
20995
|
"the repository is in detached HEAD state \u2014 check out a branch before adding a version"
|
|
20824
20996
|
);
|
|
20825
20997
|
}
|
|
20998
|
+
const trunk = opts?.defaultBranch?.trim() || detectTrunkBranch(projectDir2, startingBranch);
|
|
20999
|
+
const previousBranch = deriveVersionBranch(prevSemver, trunk);
|
|
20826
21000
|
try {
|
|
20827
|
-
if (git(
|
|
20828
|
-
git(
|
|
20829
|
-
git(
|
|
21001
|
+
if (git(projectDir2, ["status", "--porcelain"]) !== "") {
|
|
21002
|
+
git(projectDir2, ["add", "-A"]);
|
|
21003
|
+
git(projectDir2, ["commit", "-m", `chore(version): finalize ${prevSemver}`]);
|
|
20830
21004
|
}
|
|
20831
21005
|
} catch (e) {
|
|
20832
21006
|
fail("committing pending changes", e);
|
|
20833
21007
|
}
|
|
20834
21008
|
try {
|
|
20835
|
-
if (branchExists(
|
|
20836
|
-
else git(
|
|
21009
|
+
if (branchExists(projectDir2, trunk)) git(projectDir2, ["checkout", trunk]);
|
|
21010
|
+
else git(projectDir2, ["checkout", "-b", trunk]);
|
|
20837
21011
|
} catch (e) {
|
|
20838
|
-
fail(
|
|
21012
|
+
fail(`checking out ${trunk}`, e);
|
|
20839
21013
|
}
|
|
20840
|
-
const mergeSource = branchExists(
|
|
20841
|
-
if (mergeSource !==
|
|
21014
|
+
const mergeSource = branchExists(projectDir2, previousBranch) ? previousBranch : startingBranch;
|
|
21015
|
+
if (mergeSource !== trunk) {
|
|
20842
21016
|
try {
|
|
20843
|
-
git(
|
|
21017
|
+
git(projectDir2, ["merge", mergeSource, "-X", "theirs", "--no-edit"]);
|
|
20844
21018
|
} catch (e) {
|
|
20845
21019
|
try {
|
|
20846
|
-
git(
|
|
21020
|
+
git(projectDir2, ["merge", "--abort"]);
|
|
20847
21021
|
} catch {
|
|
20848
21022
|
}
|
|
20849
21023
|
try {
|
|
20850
|
-
git(
|
|
21024
|
+
git(projectDir2, ["checkout", startingBranch]);
|
|
20851
21025
|
} catch {
|
|
20852
21026
|
}
|
|
20853
|
-
fail(`merging ${mergeSource} into
|
|
21027
|
+
fail(`merging ${mergeSource} into ${trunk}`, e);
|
|
20854
21028
|
}
|
|
20855
21029
|
}
|
|
20856
|
-
if (branchExists(
|
|
21030
|
+
if (branchExists(projectDir2, newBranch)) {
|
|
20857
21031
|
let newBranchMerged = true;
|
|
20858
21032
|
try {
|
|
20859
|
-
git(
|
|
21033
|
+
git(projectDir2, ["merge-base", "--is-ancestor", newBranch, trunk]);
|
|
20860
21034
|
} catch {
|
|
20861
21035
|
newBranchMerged = false;
|
|
20862
21036
|
}
|
|
20863
21037
|
if (!newBranchMerged) {
|
|
20864
21038
|
try {
|
|
20865
|
-
git(
|
|
21039
|
+
git(projectDir2, ["merge", newBranch, "-X", "theirs", "--no-edit"]);
|
|
20866
21040
|
} catch (e) {
|
|
20867
21041
|
try {
|
|
20868
|
-
git(
|
|
21042
|
+
git(projectDir2, ["merge", "--abort"]);
|
|
20869
21043
|
} catch {
|
|
20870
21044
|
}
|
|
20871
|
-
fail(`merging ${newBranch} into
|
|
21045
|
+
fail(`merging ${newBranch} into ${trunk}`, e);
|
|
20872
21046
|
}
|
|
20873
21047
|
}
|
|
20874
21048
|
}
|
|
20875
21049
|
let commitHash = "";
|
|
20876
21050
|
try {
|
|
20877
|
-
git(
|
|
20878
|
-
commitHash = git(
|
|
21051
|
+
git(projectDir2, ["checkout", "-B", newBranch]);
|
|
21052
|
+
commitHash = git(projectDir2, ["rev-parse", "--short", "HEAD"]);
|
|
20879
21053
|
} catch (e) {
|
|
20880
21054
|
fail(`creating ${newBranch}`, e);
|
|
20881
21055
|
}
|
|
20882
21056
|
let pushed = false;
|
|
20883
21057
|
try {
|
|
20884
|
-
|
|
21058
|
+
if (opts?.remoteUrl) ensureOrigin(projectDir2, opts.remoteUrl);
|
|
21059
|
+
const remotes = git(projectDir2, ["remote"]);
|
|
20885
21060
|
if (remotes.split("\n").includes("origin")) {
|
|
20886
|
-
git(
|
|
21061
|
+
git(projectDir2, ["push", "--set-upstream", "origin", trunk, newBranch], opts?.auth);
|
|
20887
21062
|
pushed = true;
|
|
20888
21063
|
}
|
|
20889
21064
|
} catch (e) {
|
|
20890
|
-
fail(`pushing
|
|
21065
|
+
fail(`pushing ${trunk} and ${newBranch}`, e);
|
|
20891
21066
|
}
|
|
20892
21067
|
return { previousBranch, newBranch, commitHash, pushed };
|
|
20893
21068
|
}
|
|
20894
|
-
function runProjectGit(
|
|
20895
|
-
if (!existsSync6(join6(
|
|
21069
|
+
function runProjectGit(projectDir2, message, opts) {
|
|
21070
|
+
if (!existsSync6(join6(projectDir2, ".git"))) {
|
|
20896
21071
|
return { branch: "", commitHash: "", status: "skipped" };
|
|
20897
21072
|
}
|
|
20898
21073
|
try {
|
|
20899
|
-
git(
|
|
21074
|
+
git(projectDir2, ["add", "-A"]);
|
|
20900
21075
|
try {
|
|
20901
|
-
git(
|
|
21076
|
+
git(projectDir2, ["commit", "-m", message]);
|
|
20902
21077
|
} catch {
|
|
20903
21078
|
}
|
|
20904
|
-
const branch = git(
|
|
20905
|
-
const commitHash = git(
|
|
20906
|
-
|
|
21079
|
+
const branch = git(projectDir2, ["branch", "--show-current"]);
|
|
21080
|
+
const commitHash = git(projectDir2, ["rev-parse", "--short", "HEAD"]);
|
|
21081
|
+
if (opts?.remoteUrl) ensureOrigin(projectDir2, opts.remoteUrl);
|
|
21082
|
+
const remotes = git(projectDir2, ["remote"]);
|
|
20907
21083
|
if (remotes.split("\n").includes("origin")) {
|
|
20908
|
-
git(
|
|
21084
|
+
git(projectDir2, ["push", "--set-upstream", "origin", "HEAD"], opts?.auth);
|
|
20909
21085
|
}
|
|
20910
21086
|
return { branch, commitHash, status: "completed" };
|
|
20911
21087
|
} catch {
|
|
@@ -20933,7 +21109,12 @@ function runPostConductorGit(db, workspacePath, workflowMarkdownId) {
|
|
|
20933
21109
|
};
|
|
20934
21110
|
const cwd = join6(workspacePath, project.workspaceFolderName);
|
|
20935
21111
|
const semver = wfm.versionLabel ?? "latest";
|
|
20936
|
-
const
|
|
21112
|
+
const pushCtx = resolveGitPushContext(db, project.id, wfm.createdBy);
|
|
21113
|
+
const result = runProjectGit(
|
|
21114
|
+
cwd,
|
|
21115
|
+
`chore(${wfm.conductorType}): ${semver} conductor output`,
|
|
21116
|
+
pushCtx
|
|
21117
|
+
);
|
|
20937
21118
|
finish(result.status, result.branch || null, result.commitHash || null);
|
|
20938
21119
|
return result;
|
|
20939
21120
|
}
|
|
@@ -21027,7 +21208,13 @@ function addVersion(db, projectId, versionType, createdBy, workspacePath) {
|
|
|
21027
21208
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
21028
21209
|
const project = db.select().from(projects).where(eq(projects.id, projectId)).get();
|
|
21029
21210
|
if (!project) throw svcError4("PROJECT_NOT_FOUND", 404);
|
|
21030
|
-
|
|
21211
|
+
const pushCtx = resolveGitPushContext(db, projectId, createdBy);
|
|
21212
|
+
runVersionCutGit(
|
|
21213
|
+
join7(workspacePath, project.workspaceFolderName),
|
|
21214
|
+
currentSemver,
|
|
21215
|
+
newSemver,
|
|
21216
|
+
pushCtx
|
|
21217
|
+
);
|
|
21031
21218
|
const apps = db.select({ id: applications.id }).from(applications).where(and(eq(applications.projectId, projectId), eq(applications.isDeleted, 0))).all();
|
|
21032
21219
|
const snapshot = {};
|
|
21033
21220
|
for (const app of apps) {
|
|
@@ -21174,7 +21361,8 @@ function syncFromDisk(db, workspacePath, projectFolder, session) {
|
|
|
21174
21361
|
try {
|
|
21175
21362
|
const result = runProjectGit(
|
|
21176
21363
|
join8(workspacePath, projectFolder),
|
|
21177
|
-
`chore(brainstorm): session "${session.title ?? session.id}" completed
|
|
21364
|
+
`chore(brainstorm): session "${session.title ?? session.id}" completed`,
|
|
21365
|
+
resolveGitPushContext(db, session.projectId, session.createdBy)
|
|
21178
21366
|
);
|
|
21179
21367
|
if (session.createdBy) {
|
|
21180
21368
|
createNotification(
|
|
@@ -21515,8 +21703,8 @@ function cicdGuardTokenValid(projectId, token) {
|
|
|
21515
21703
|
function resolveCicdApp(db, workspacePath, projectId) {
|
|
21516
21704
|
const project = db.select().from(projects).where(eq(projects.id, projectId)).get();
|
|
21517
21705
|
if (!project) throw svcError6("PROJECT_NOT_FOUND", 404);
|
|
21518
|
-
const
|
|
21519
|
-
const appDir = join9(
|
|
21706
|
+
const projectDir2 = join9(workspacePath, project.workspaceFolderName);
|
|
21707
|
+
const appDir = join9(projectDir2, "cicd", "app");
|
|
21520
21708
|
const present = existsSync8(join9(appDir, "server.js"));
|
|
21521
21709
|
let port = DEFAULT_CICD_APP_PORT;
|
|
21522
21710
|
try {
|
|
@@ -21524,7 +21712,7 @@ function resolveCicdApp(db, workspacePath, projectId) {
|
|
|
21524
21712
|
if (typeof config.port === "number") port = config.port;
|
|
21525
21713
|
} catch {
|
|
21526
21714
|
}
|
|
21527
|
-
return { appDir, projectDir, present, port };
|
|
21715
|
+
return { appDir, projectDir: projectDir2, present, port };
|
|
21528
21716
|
}
|
|
21529
21717
|
function probeApp(port, timeoutMs = 1e3) {
|
|
21530
21718
|
return new Promise((resolve5) => {
|
|
@@ -21703,7 +21891,8 @@ function generatePrdWizardMarkdown(db, workspacePath, requestId, targetModule) {
|
|
|
21703
21891
|
try {
|
|
21704
21892
|
const result = runProjectGit(
|
|
21705
21893
|
join10(workspacePath, project.workspaceFolderName),
|
|
21706
|
-
`chore(prd-wizard): ${filename} generated
|
|
21894
|
+
`chore(prd-wizard): ${filename} generated`,
|
|
21895
|
+
resolveGitPushContext(db, project.id, req.createdBy)
|
|
21707
21896
|
);
|
|
21708
21897
|
createNotification(db, req.createdBy, "conductor_progress", "PRD Wizard markdown committed", {
|
|
21709
21898
|
body: result.status === "completed" ? `git commit ${result.commitHash} on ${result.branch}` : `git ${result.status}`,
|
|
@@ -21721,12 +21910,12 @@ function snapshotPrdFiles(db, workspacePath, projectId) {
|
|
|
21721
21910
|
const project = db.select().from(projects).where(eq(projects.id, projectId)).get();
|
|
21722
21911
|
const snapshot = /* @__PURE__ */ new Map();
|
|
21723
21912
|
if (!project) return snapshot;
|
|
21724
|
-
const
|
|
21913
|
+
const projectDir2 = join10(workspacePath, project.workspaceFolderName);
|
|
21725
21914
|
const apps = db.select({ name: applications.name }).from(applications).where(and(eq(applications.projectId, projectId), eq(applications.isDeleted, 0))).all();
|
|
21726
21915
|
for (const app of apps) {
|
|
21727
|
-
const appRel = resolveAppFolderName(
|
|
21916
|
+
const appRel = resolveAppFolderName(projectDir2, app.name);
|
|
21728
21917
|
for (const rel of ["context/PRD.md", "CHANGELOG.md"]) {
|
|
21729
|
-
const abs = join10(
|
|
21918
|
+
const abs = join10(projectDir2, appRel, ...rel.split("/"));
|
|
21730
21919
|
if (!existsSync9(abs)) continue;
|
|
21731
21920
|
const stat = statSync2(abs);
|
|
21732
21921
|
snapshot.set(abs, `${stat.mtimeMs}:${stat.size}`);
|
|
@@ -21766,7 +21955,8 @@ function pollWizardApply(db, workspacePath, requestId, state, nowMs) {
|
|
|
21766
21955
|
if (project) {
|
|
21767
21956
|
const result = runProjectGit(
|
|
21768
21957
|
join10(workspacePath, project.workspaceFolderName),
|
|
21769
|
-
`chore(prd-wizard): applied PRD updates (request ${requestId})
|
|
21958
|
+
`chore(prd-wizard): applied PRD updates (request ${requestId})`,
|
|
21959
|
+
resolveGitPushContext(db, project.id, req.createdBy)
|
|
21770
21960
|
);
|
|
21771
21961
|
createNotification(db, req.createdBy, "conductor_complete", "PRD updates applied", {
|
|
21772
21962
|
body: result.status === "completed" ? `git commit ${result.commitHash} on ${result.branch}` : `git ${result.status}`,
|
|
@@ -21820,20 +22010,20 @@ function git2(cwd, args) {
|
|
|
21820
22010
|
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
21821
22011
|
}).toString().trim();
|
|
21822
22012
|
}
|
|
21823
|
-
function checkGitState(
|
|
21824
|
-
if (!existsSync10(join11(
|
|
22013
|
+
function checkGitState(projectDir2) {
|
|
22014
|
+
if (!existsSync10(join11(projectDir2, ".git"))) {
|
|
21825
22015
|
return { hasRepo: false, clean: false, pushed: false, currentBranch: null };
|
|
21826
22016
|
}
|
|
21827
22017
|
let currentBranch = null;
|
|
21828
22018
|
let clean = false;
|
|
21829
22019
|
let pushed = true;
|
|
21830
22020
|
try {
|
|
21831
|
-
currentBranch = git2(
|
|
21832
|
-
clean = git2(
|
|
21833
|
-
const remotes = git2(
|
|
22021
|
+
currentBranch = git2(projectDir2, ["branch", "--show-current"]) || null;
|
|
22022
|
+
clean = git2(projectDir2, ["status", "--porcelain"]) === "";
|
|
22023
|
+
const remotes = git2(projectDir2, ["remote"]).split("\n").filter(Boolean);
|
|
21834
22024
|
if (remotes.includes("origin")) {
|
|
21835
22025
|
try {
|
|
21836
|
-
pushed = git2(
|
|
22026
|
+
pushed = git2(projectDir2, ["rev-list", "--count", "@{u}..HEAD"]) === "0";
|
|
21837
22027
|
} catch {
|
|
21838
22028
|
pushed = false;
|
|
21839
22029
|
}
|
|
@@ -21845,12 +22035,12 @@ function checkGitState(projectDir) {
|
|
|
21845
22035
|
}
|
|
21846
22036
|
var GIT_STATE_TTL_MS = 3e3;
|
|
21847
22037
|
var gitStateCache = /* @__PURE__ */ new Map();
|
|
21848
|
-
function cachedCheckGitState(
|
|
21849
|
-
const cached = gitStateCache.get(
|
|
22038
|
+
function cachedCheckGitState(projectDir2) {
|
|
22039
|
+
const cached = gitStateCache.get(projectDir2);
|
|
21850
22040
|
const now = Date.now();
|
|
21851
22041
|
if (cached && now - cached.at < GIT_STATE_TTL_MS) return cached.state;
|
|
21852
|
-
const state = checkGitState(
|
|
21853
|
-
gitStateCache.set(
|
|
22042
|
+
const state = checkGitState(projectDir2);
|
|
22043
|
+
gitStateCache.set(projectDir2, { state, at: now });
|
|
21854
22044
|
return state;
|
|
21855
22045
|
}
|
|
21856
22046
|
function evaluateBuildGuard(db, workspacePath, projectId, versionId) {
|
|
@@ -21875,8 +22065,8 @@ function evaluateBuildGuard(db, workspacePath, projectId, versionId) {
|
|
|
21875
22065
|
const applying = db.select({ id: prdWizardRequests.id }).from(prdWizardRequests).where(and(eq(prdWizardRequests.projectId, projectId), inArray(prdWizardRequests.id, applyIds))).limit(1).get();
|
|
21876
22066
|
if (applying) reasons.push("a PRD Wizard update is being applied");
|
|
21877
22067
|
}
|
|
21878
|
-
const
|
|
21879
|
-
const gitState = cachedCheckGitState(
|
|
22068
|
+
const projectDir2 = join11(workspacePath, project.workspaceFolderName);
|
|
22069
|
+
const gitState = cachedCheckGitState(projectDir2);
|
|
21880
22070
|
if (!gitState.hasRepo) reasons.push("the project folder has no git repository");
|
|
21881
22071
|
else {
|
|
21882
22072
|
if (!gitState.clean) reasons.push("uncommitted changes in the working tree");
|
|
@@ -22061,15 +22251,19 @@ function requestPlan(db, workspacePath, projectId, userId) {
|
|
|
22061
22251
|
stopCicdWatcher(setup.id, "generation");
|
|
22062
22252
|
const project = requireProject(db, projectId);
|
|
22063
22253
|
const apps = db.select({ name: applications.name }).from(applications).where(and(eq(applications.projectId, projectId), eq(applications.isDeleted, 0))).all();
|
|
22064
|
-
const
|
|
22065
|
-
const executionDir = join13(
|
|
22254
|
+
const projectDir2 = join13(workspacePath, project.workspaceFolderName);
|
|
22255
|
+
const executionDir = join13(projectDir2, "execution");
|
|
22066
22256
|
mkdirSync4(executionDir, { recursive: true });
|
|
22067
22257
|
const filename = `cicd-plan-${setup.id}-${Date.now()}.md`;
|
|
22068
22258
|
const filePath = join13(executionDir, filename);
|
|
22069
22259
|
const relative3 = `execution/${filename}`;
|
|
22070
22260
|
writeFileSync5(
|
|
22071
22261
|
filePath,
|
|
22072
|
-
buildPlanRequestMarkdown(
|
|
22262
|
+
buildPlanRequestMarkdown(
|
|
22263
|
+
project.name,
|
|
22264
|
+
apps.map((a) => a.name),
|
|
22265
|
+
relative3
|
|
22266
|
+
),
|
|
22073
22267
|
"utf8"
|
|
22074
22268
|
);
|
|
22075
22269
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -22080,7 +22274,11 @@ function requestPlan(db, workspacePath, projectId, userId) {
|
|
|
22080
22274
|
relatedEntityId: setup.id
|
|
22081
22275
|
});
|
|
22082
22276
|
try {
|
|
22083
|
-
runProjectGit(
|
|
22277
|
+
runProjectGit(
|
|
22278
|
+
projectDir2,
|
|
22279
|
+
`chore(cicd): plan request ${filename}`,
|
|
22280
|
+
resolveGitPushContext(db, projectId, userId)
|
|
22281
|
+
);
|
|
22084
22282
|
} catch {
|
|
22085
22283
|
}
|
|
22086
22284
|
startCicdWatcher(db, workspacePath, setup.id, "plan", userId);
|
|
@@ -22104,9 +22302,9 @@ function pollCicdTracking(db, workspacePath, setupId, kind, notifyUserId) {
|
|
|
22104
22302
|
if (setup.status !== expected) return "stopped";
|
|
22105
22303
|
const project = db.select().from(projects).where(eq(projects.id, setup.projectId)).get();
|
|
22106
22304
|
if (!project) return "stopped";
|
|
22107
|
-
const
|
|
22305
|
+
const projectDir2 = join13(workspacePath, project.workspaceFolderName);
|
|
22108
22306
|
const trackingPath = join13(
|
|
22109
|
-
|
|
22307
|
+
projectDir2,
|
|
22110
22308
|
...(kind === "plan" ? CICD_PLAN_RELATIVE : CICD_GENERATION_RELATIVE).split("/")
|
|
22111
22309
|
);
|
|
22112
22310
|
const requestedAt = kind === "plan" ? setup.planRequestedAt : setup.generateRequestedAt;
|
|
@@ -22136,7 +22334,11 @@ function pollCicdTracking(db, workspacePath, setupId, kind, notifyUserId) {
|
|
|
22136
22334
|
relatedEntityId: setupId
|
|
22137
22335
|
});
|
|
22138
22336
|
try {
|
|
22139
|
-
runProjectGit(
|
|
22337
|
+
runProjectGit(
|
|
22338
|
+
projectDir2,
|
|
22339
|
+
"chore(cicd): CI/CD app generated",
|
|
22340
|
+
resolveGitPushContext(db, setup.projectId, notifyUserId)
|
|
22341
|
+
);
|
|
22140
22342
|
} catch {
|
|
22141
22343
|
}
|
|
22142
22344
|
}
|
|
@@ -22172,8 +22374,8 @@ function approvePlan(db, workspacePath, projectId, userId) {
|
|
|
22172
22374
|
const setup = getOrCreateSetup(db, projectId, userId);
|
|
22173
22375
|
if (setup.status !== "planned") throw svcError8("CICD_INVALID_STATE", 409);
|
|
22174
22376
|
const project = requireProject(db, projectId);
|
|
22175
|
-
const
|
|
22176
|
-
const planPath = join13(
|
|
22377
|
+
const projectDir2 = join13(workspacePath, project.workspaceFolderName);
|
|
22378
|
+
const planPath = join13(projectDir2, ...CICD_PLAN_RELATIVE.split("/"));
|
|
22177
22379
|
if (parseTrackingStatus(planPath) !== "COMPLETED") throw svcError8("CICD_PLAN_NOT_READY", 409);
|
|
22178
22380
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
22179
22381
|
appendFileSync(planPath, `
|
|
@@ -22181,7 +22383,11 @@ function approvePlan(db, workspacePath, projectId, userId) {
|
|
|
22181
22383
|
`);
|
|
22182
22384
|
db.update(cicdSetups).set({ status: "approved", approvedBy: userId, approvedAt: now, updatedAt: now }).where(eq(cicdSetups.id, setup.id)).run();
|
|
22183
22385
|
try {
|
|
22184
|
-
runProjectGit(
|
|
22386
|
+
runProjectGit(
|
|
22387
|
+
projectDir2,
|
|
22388
|
+
"chore(cicd): deployment plan approved",
|
|
22389
|
+
resolveGitPushContext(db, projectId, userId)
|
|
22390
|
+
);
|
|
22185
22391
|
} catch {
|
|
22186
22392
|
}
|
|
22187
22393
|
}
|
|
@@ -22490,12 +22696,12 @@ function createProject(db, workspacePath, name, code, baseIdea, createdBy) {
|
|
|
22490
22696
|
return projectId;
|
|
22491
22697
|
}
|
|
22492
22698
|
function scaffoldProjectFolder(workspacePath, folderName, name, baseIdea) {
|
|
22493
|
-
const
|
|
22494
|
-
mkdirSync6(
|
|
22495
|
-
mkdirSync6(join16(
|
|
22496
|
-
mkdirSync6(join16(
|
|
22497
|
-
mkdirSync6(join16(
|
|
22498
|
-
mkdirSync6(join16(
|
|
22699
|
+
const projectDir2 = join16(workspacePath, folderName);
|
|
22700
|
+
mkdirSync6(projectDir2, { recursive: true });
|
|
22701
|
+
mkdirSync6(join16(projectDir2, "execution"), { recursive: true });
|
|
22702
|
+
mkdirSync6(join16(projectDir2, "context"), { recursive: true });
|
|
22703
|
+
mkdirSync6(join16(projectDir2, "context", "references"), { recursive: true });
|
|
22704
|
+
mkdirSync6(join16(projectDir2, "shared_context"), { recursive: true });
|
|
22499
22705
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
22500
22706
|
const template = [
|
|
22501
22707
|
"---",
|
|
@@ -22535,9 +22741,9 @@ function scaffoldProjectFolder(workspacePath, folderName, name, baseIdea) {
|
|
|
22535
22741
|
"# Feedback",
|
|
22536
22742
|
"<!-- Human answers appear here -->"
|
|
22537
22743
|
].join("\n");
|
|
22538
|
-
writeFileSync7(join16(
|
|
22744
|
+
writeFileSync7(join16(projectDir2, `${folderName}.brainstorm.md`), template, "utf8");
|
|
22539
22745
|
writeFileSync7(
|
|
22540
|
-
join16(
|
|
22746
|
+
join16(projectDir2, "context", "references", "brainstorm-protocol.md"),
|
|
22541
22747
|
"# Brainstorm Protocol\n\n[Installed by the co2 plugin]\n",
|
|
22542
22748
|
"utf8"
|
|
22543
22749
|
);
|
|
@@ -22636,6 +22842,9 @@ function getProjectDetail(db, workspacePath, projectId) {
|
|
|
22636
22842
|
workflowPhase: project.workflowPhase,
|
|
22637
22843
|
creationStatus,
|
|
22638
22844
|
isArchived: project.isArchived,
|
|
22845
|
+
gitRemoteUrl: project.gitRemoteUrl ?? null,
|
|
22846
|
+
defaultBranch: project.defaultBranch ?? "main",
|
|
22847
|
+
gitProvider: project.gitProvider ?? "github",
|
|
22639
22848
|
applications: apps,
|
|
22640
22849
|
createdAt: project.createdAt
|
|
22641
22850
|
};
|
|
@@ -22691,11 +22900,11 @@ function getProjectDetailSummary(db, projectId) {
|
|
|
22691
22900
|
function regenerateCO2Files(db, workspacePath, projectId) {
|
|
22692
22901
|
const project = db.select().from(projects).where(eq(projects.id, projectId)).limit(1).get();
|
|
22693
22902
|
if (!project) throw svcError10("NOT_FOUND", 404);
|
|
22694
|
-
const
|
|
22695
|
-
mkdirSync6(
|
|
22903
|
+
const projectDir2 = join16(workspacePath, project.workspaceFolderName);
|
|
22904
|
+
mkdirSync6(projectDir2, { recursive: true });
|
|
22696
22905
|
const apps = db.select().from(applications).where(and(eq(applications.projectId, projectId), eq(applications.isDeleted, 0))).all();
|
|
22697
22906
|
writeFileSync7(
|
|
22698
|
-
join16(
|
|
22907
|
+
join16(projectDir2, "CLAUDE.md"),
|
|
22699
22908
|
project.rawClaudeMd ?? buildClaudeMd(project.name, apps),
|
|
22700
22909
|
"utf8"
|
|
22701
22910
|
);
|
|
@@ -22708,8 +22917,8 @@ function regenerateCO2Files(db, workspacePath, projectId) {
|
|
|
22708
22917
|
}
|
|
22709
22918
|
for (const app of apps) {
|
|
22710
22919
|
const mods = db.select().from(projectModules).where(and(eq(projectModules.applicationId, app.id), eq(projectModules.isDeleted, 0))).all();
|
|
22711
|
-
const appRel = resolveAppFolderName(
|
|
22712
|
-
const appDir = join16(
|
|
22920
|
+
const appRel = resolveAppFolderName(projectDir2, app.name);
|
|
22921
|
+
const appDir = join16(projectDir2, appRel, "context");
|
|
22713
22922
|
mkdirSync6(appDir, { recursive: true });
|
|
22714
22923
|
writeFileSync7(
|
|
22715
22924
|
join16(appDir, "PRD.md"),
|
|
@@ -22766,10 +22975,10 @@ function toKebabCase(name) {
|
|
|
22766
22975
|
function rescanClaudeMd(db, workspacePath, projectId) {
|
|
22767
22976
|
const project = db.select().from(projects).where(eq(projects.id, projectId)).limit(1).get();
|
|
22768
22977
|
if (!project) throw svcError10("NOT_FOUND", 404);
|
|
22769
|
-
const
|
|
22978
|
+
const projectDir2 = join16(workspacePath, project.workspaceFolderName);
|
|
22770
22979
|
const claudeMdPath = [
|
|
22771
|
-
join16(
|
|
22772
|
-
join16(
|
|
22980
|
+
join16(projectDir2, "CLAUDE.md"),
|
|
22981
|
+
join16(projectDir2, "source", "CLAUDE.md")
|
|
22773
22982
|
].find((p) => existsSync14(p));
|
|
22774
22983
|
if (!claudeMdPath) throw svcError10("CLAUDE_MD_MISSING", 404);
|
|
22775
22984
|
const content = readFileSync9(claudeMdPath, "utf8");
|
|
@@ -22860,6 +23069,18 @@ function deriveAppCode(name) {
|
|
|
22860
23069
|
const initials = name.split(/[\s_-]+/).filter(Boolean).map((w) => w[0].toUpperCase()).join("");
|
|
22861
23070
|
return (initials || name.slice(0, 2)).slice(0, 4);
|
|
22862
23071
|
}
|
|
23072
|
+
function updateProjectGitRemote(db, projectId, gitRemoteUrl, opts = {}) {
|
|
23073
|
+
const project = db.select({ id: projects.id }).from(projects).where(eq(projects.id, projectId)).limit(1).get();
|
|
23074
|
+
if (!project) throw svcError10("NOT_FOUND", 404);
|
|
23075
|
+
const trimmed = gitRemoteUrl.trim();
|
|
23076
|
+
const set = { gitRemoteUrl: trimmed === "" ? null : trimmed, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
23077
|
+
if (opts.defaultBranch !== void 0) {
|
|
23078
|
+
const b = opts.defaultBranch.trim();
|
|
23079
|
+
set.defaultBranch = b === "" ? "main" : b;
|
|
23080
|
+
}
|
|
23081
|
+
if (opts.gitProvider !== void 0) set.gitProvider = opts.gitProvider;
|
|
23082
|
+
db.update(projects).set(set).where(eq(projects.id, projectId)).run();
|
|
23083
|
+
}
|
|
22863
23084
|
function archiveProject(db, projectId, archivedBy) {
|
|
22864
23085
|
const project = db.select({ id: projects.id }).from(projects).where(eq(projects.id, projectId)).limit(1).get();
|
|
22865
23086
|
if (!project) throw svcError10("NOT_FOUND", 404);
|
|
@@ -23520,9 +23741,9 @@ function writeBugMdFile(db, workspacePath, app, projectFolderName) {
|
|
|
23520
23741
|
db.select({ id: prdVersions.id, semver: prdVersions.semver }).from(prdVersions).where(eq(prdVersions.projectId, app.projectId)).all().map((v) => [v.id, v.semver])
|
|
23521
23742
|
);
|
|
23522
23743
|
const content = buildBugMd(modules, bugs, semverByVersionId);
|
|
23523
|
-
const
|
|
23524
|
-
const appRel = resolveAppFolderName(
|
|
23525
|
-
const appContextDir = join17(
|
|
23744
|
+
const projectDir2 = join17(workspacePath, projectFolderName);
|
|
23745
|
+
const appRel = resolveAppFolderName(projectDir2, app.name);
|
|
23746
|
+
const appContextDir = join17(projectDir2, appRel, "context");
|
|
23526
23747
|
mkdirSync7(appContextDir, { recursive: true });
|
|
23527
23748
|
writeFileSync8(join17(appContextDir, "BUG.md"), content, "utf8");
|
|
23528
23749
|
return { content, bugCount: bugs.length };
|
|
@@ -23557,8 +23778,8 @@ function syncBugMdToDisk(db, workspacePath, applicationId, bugId, mode = "replac
|
|
|
23557
23778
|
const project = db.select({ workspaceFolderName: projects.workspaceFolderName }).from(projects).where(eq(projects.id, app.projectId)).get();
|
|
23558
23779
|
if (!project) return;
|
|
23559
23780
|
if (bugId !== void 0) {
|
|
23560
|
-
const
|
|
23561
|
-
const file = join17(
|
|
23781
|
+
const projectDir2 = join17(workspacePath, project.workspaceFolderName);
|
|
23782
|
+
const file = join17(projectDir2, resolveAppFolderName(projectDir2, app.name), "context", "BUG.md");
|
|
23562
23783
|
if (existsSync15(file)) {
|
|
23563
23784
|
const bug = db.select().from(bugReports).where(and(eq(bugReports.id, bugId), eq(bugReports.isDeleted, 0))).get();
|
|
23564
23785
|
if (bug) {
|
|
@@ -23800,6 +24021,332 @@ function documentRoutes(db, dataDir) {
|
|
|
23800
24021
|
return app;
|
|
23801
24022
|
}
|
|
23802
24023
|
|
|
24024
|
+
// ../server/src/features/git/service.ts
|
|
24025
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
24026
|
+
import { existsSync as existsSync17, readFileSync as readFileSync12, rmSync, writeFileSync as writeFileSync10 } from "fs";
|
|
24027
|
+
import { join as join19 } from "path";
|
|
24028
|
+
var GitServiceError = class extends Error {
|
|
24029
|
+
code;
|
|
24030
|
+
status;
|
|
24031
|
+
constructor(code, status, message) {
|
|
24032
|
+
super(message ?? code);
|
|
24033
|
+
this.code = code;
|
|
24034
|
+
this.status = status;
|
|
24035
|
+
}
|
|
24036
|
+
};
|
|
24037
|
+
function git3(cwd, args) {
|
|
24038
|
+
return execFileSync3("git", args, {
|
|
24039
|
+
cwd,
|
|
24040
|
+
stdio: "pipe",
|
|
24041
|
+
timeout: 3e4,
|
|
24042
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
24043
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
24044
|
+
}).toString().trim();
|
|
24045
|
+
}
|
|
24046
|
+
function gitOrNull(cwd, args) {
|
|
24047
|
+
try {
|
|
24048
|
+
return execFileSync3("git", args, {
|
|
24049
|
+
cwd,
|
|
24050
|
+
stdio: "pipe",
|
|
24051
|
+
timeout: 3e4,
|
|
24052
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
24053
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
24054
|
+
}).toString();
|
|
24055
|
+
} catch {
|
|
24056
|
+
return null;
|
|
24057
|
+
}
|
|
24058
|
+
}
|
|
24059
|
+
function projectDir(db, workspacePath, projectId) {
|
|
24060
|
+
const project = db.select({ folder: projects.workspaceFolderName }).from(projects).where(eq(projects.id, projectId)).get();
|
|
24061
|
+
if (!project) throw new GitServiceError("PROJECT_NOT_FOUND", 404);
|
|
24062
|
+
return join19(workspacePath, project.folder);
|
|
24063
|
+
}
|
|
24064
|
+
function isGitRepo(dir) {
|
|
24065
|
+
return existsSync17(join19(dir, ".git"));
|
|
24066
|
+
}
|
|
24067
|
+
function classifyStatus(x, y) {
|
|
24068
|
+
const conflictPairs = /* @__PURE__ */ new Set(["DD", "AU", "UD", "UA", "DU", "AA", "UU"]);
|
|
24069
|
+
const xy = `${x}${y}`;
|
|
24070
|
+
if (conflictPairs.has(xy)) {
|
|
24071
|
+
return { status: "conflicted", staged: false, unstaged: true, conflicted: true };
|
|
24072
|
+
}
|
|
24073
|
+
if (x === "?" && y === "?") {
|
|
24074
|
+
return { status: "untracked", staged: false, unstaged: true, conflicted: false };
|
|
24075
|
+
}
|
|
24076
|
+
const staged = x !== " " && x !== "?";
|
|
24077
|
+
const unstaged = y !== " " && y !== "?";
|
|
24078
|
+
const code = x !== " " && x !== "?" ? x : y;
|
|
24079
|
+
const status = code === "A" ? "added" : code === "D" ? "deleted" : code === "R" ? "renamed" : code === "T" ? "typechange" : "modified";
|
|
24080
|
+
return { status, staged, unstaged, conflicted: false };
|
|
24081
|
+
}
|
|
24082
|
+
function parseStatus2(raw2) {
|
|
24083
|
+
const parsed = { branch: null, upstream: null, ahead: 0, behind: 0, files: [] };
|
|
24084
|
+
const records = raw2.split("\0");
|
|
24085
|
+
for (let i = 0; i < records.length; i++) {
|
|
24086
|
+
const rec = records[i];
|
|
24087
|
+
if (rec === "" || rec === void 0) continue;
|
|
24088
|
+
if (rec.startsWith("## ")) {
|
|
24089
|
+
const info = rec.slice(3);
|
|
24090
|
+
const noCommits = info.match(/^No commits yet on (.+)$/);
|
|
24091
|
+
const head = info.split(" ")[0] ?? "";
|
|
24092
|
+
const dots = head.indexOf("...");
|
|
24093
|
+
parsed.branch = noCommits ? noCommits[1] ?? null : dots === -1 ? head : head.slice(0, dots);
|
|
24094
|
+
if (dots !== -1) parsed.upstream = head.slice(dots + 3);
|
|
24095
|
+
parsed.ahead = Number(info.match(/ahead (\d+)/)?.[1] ?? 0);
|
|
24096
|
+
parsed.behind = Number(info.match(/behind (\d+)/)?.[1] ?? 0);
|
|
24097
|
+
continue;
|
|
24098
|
+
}
|
|
24099
|
+
const x = rec[0] ?? " ";
|
|
24100
|
+
const y = rec[1] ?? " ";
|
|
24101
|
+
const path = rec.slice(3);
|
|
24102
|
+
if (x === "R" || x === "C" || y === "R" || y === "C") i++;
|
|
24103
|
+
const cls = classifyStatus(x, y);
|
|
24104
|
+
parsed.files.push({
|
|
24105
|
+
path,
|
|
24106
|
+
status: cls.status,
|
|
24107
|
+
staged: cls.staged,
|
|
24108
|
+
unstaged: cls.unstaged,
|
|
24109
|
+
conflicted: cls.conflicted,
|
|
24110
|
+
indexStatus: x,
|
|
24111
|
+
worktreeStatus: y
|
|
24112
|
+
});
|
|
24113
|
+
}
|
|
24114
|
+
return parsed;
|
|
24115
|
+
}
|
|
24116
|
+
function getGitStatus(db, workspacePath, projectId) {
|
|
24117
|
+
const dir = projectDir(db, workspacePath, projectId);
|
|
24118
|
+
const empty = {
|
|
24119
|
+
available: false,
|
|
24120
|
+
branch: null,
|
|
24121
|
+
upstream: null,
|
|
24122
|
+
ahead: 0,
|
|
24123
|
+
behind: 0,
|
|
24124
|
+
clean: true,
|
|
24125
|
+
hasConflicts: false,
|
|
24126
|
+
files: [],
|
|
24127
|
+
changedCount: 0
|
|
24128
|
+
};
|
|
24129
|
+
if (!existsSync17(dir) || !isGitRepo(dir)) return empty;
|
|
24130
|
+
let raw2;
|
|
24131
|
+
try {
|
|
24132
|
+
raw2 = execFileSync3("git", ["status", "--porcelain=v1", "-b", "-z"], {
|
|
24133
|
+
cwd: dir,
|
|
24134
|
+
stdio: "pipe",
|
|
24135
|
+
timeout: 3e4,
|
|
24136
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
24137
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
24138
|
+
}).toString();
|
|
24139
|
+
} catch {
|
|
24140
|
+
return empty;
|
|
24141
|
+
}
|
|
24142
|
+
const p = parseStatus2(raw2);
|
|
24143
|
+
return {
|
|
24144
|
+
available: true,
|
|
24145
|
+
branch: p.branch,
|
|
24146
|
+
upstream: p.upstream,
|
|
24147
|
+
ahead: p.ahead,
|
|
24148
|
+
behind: p.behind,
|
|
24149
|
+
clean: p.files.length === 0,
|
|
24150
|
+
hasConflicts: p.files.some((f) => f.conflicted),
|
|
24151
|
+
files: p.files,
|
|
24152
|
+
changedCount: p.files.length
|
|
24153
|
+
};
|
|
24154
|
+
}
|
|
24155
|
+
function looksBinary(s) {
|
|
24156
|
+
return s?.includes("\0") ?? false;
|
|
24157
|
+
}
|
|
24158
|
+
function getFileDiff(db, workspacePath, projectId, path) {
|
|
24159
|
+
const dir = projectDir(db, workspacePath, projectId);
|
|
24160
|
+
if (!existsSync17(dir) || !isGitRepo(dir)) {
|
|
24161
|
+
throw new GitServiceError("NO_GIT_REPO", 404, "No git repository for this project");
|
|
24162
|
+
}
|
|
24163
|
+
const status = getGitStatus(db, workspacePath, projectId);
|
|
24164
|
+
const file = status.files.find((f) => f.path === path);
|
|
24165
|
+
const cls = file?.status ?? "modified";
|
|
24166
|
+
const conflicted = file?.conflicted ?? false;
|
|
24167
|
+
if (conflicted) {
|
|
24168
|
+
const base = gitOrNull(dir, ["show", `:1:${path}`]);
|
|
24169
|
+
const ours = gitOrNull(dir, ["show", `:2:${path}`]);
|
|
24170
|
+
const theirs = gitOrNull(dir, ["show", `:3:${path}`]);
|
|
24171
|
+
const working = readWorking(dir, path);
|
|
24172
|
+
const binary2 = looksBinary(base) || looksBinary(ours) || looksBinary(theirs);
|
|
24173
|
+
return {
|
|
24174
|
+
path,
|
|
24175
|
+
status: cls,
|
|
24176
|
+
binary: binary2,
|
|
24177
|
+
conflicted: true,
|
|
24178
|
+
oldContent: ours,
|
|
24179
|
+
// pre-populate the merge editor with our side by default
|
|
24180
|
+
newContent: working,
|
|
24181
|
+
base,
|
|
24182
|
+
ours,
|
|
24183
|
+
theirs
|
|
24184
|
+
};
|
|
24185
|
+
}
|
|
24186
|
+
const oldContent = gitOrNull(dir, ["show", `HEAD:${path}`]);
|
|
24187
|
+
const newContent = cls === "deleted" ? null : readWorking(dir, path);
|
|
24188
|
+
const binary = looksBinary(oldContent) || looksBinary(newContent);
|
|
24189
|
+
return {
|
|
24190
|
+
path,
|
|
24191
|
+
status: cls,
|
|
24192
|
+
binary,
|
|
24193
|
+
conflicted: false,
|
|
24194
|
+
oldContent: cls === "added" || cls === "untracked" ? null : oldContent,
|
|
24195
|
+
newContent,
|
|
24196
|
+
base: null,
|
|
24197
|
+
ours: null,
|
|
24198
|
+
theirs: null
|
|
24199
|
+
};
|
|
24200
|
+
}
|
|
24201
|
+
function readWorking(dir, path) {
|
|
24202
|
+
const abs = join19(dir, path);
|
|
24203
|
+
if (!existsSync17(abs)) return null;
|
|
24204
|
+
try {
|
|
24205
|
+
return readFileSync12(abs, "utf8");
|
|
24206
|
+
} catch {
|
|
24207
|
+
return null;
|
|
24208
|
+
}
|
|
24209
|
+
}
|
|
24210
|
+
function commitChanges(db, workspacePath, projectId, input) {
|
|
24211
|
+
const dir = projectDir(db, workspacePath, projectId);
|
|
24212
|
+
if (!existsSync17(dir) || !isGitRepo(dir)) {
|
|
24213
|
+
throw new GitServiceError("NO_GIT_REPO", 404, "No git repository for this project");
|
|
24214
|
+
}
|
|
24215
|
+
try {
|
|
24216
|
+
if (input.paths && input.paths.length > 0) git3(dir, ["add", "--", ...input.paths]);
|
|
24217
|
+
else git3(dir, ["add", "-A"]);
|
|
24218
|
+
} catch (e) {
|
|
24219
|
+
throw new GitServiceError("GIT_ADD_FAILED", 409, detail(e));
|
|
24220
|
+
}
|
|
24221
|
+
try {
|
|
24222
|
+
git3(dir, ["diff", "--cached", "--quiet"]);
|
|
24223
|
+
throw new GitServiceError("NOTHING_TO_COMMIT", 400, "No staged changes to commit");
|
|
24224
|
+
} catch (e) {
|
|
24225
|
+
if (e instanceof GitServiceError) throw e;
|
|
24226
|
+
}
|
|
24227
|
+
try {
|
|
24228
|
+
git3(dir, ["commit", "-m", input.message]);
|
|
24229
|
+
} catch (e) {
|
|
24230
|
+
throw new GitServiceError("GIT_COMMIT_FAILED", 409, detail(e));
|
|
24231
|
+
}
|
|
24232
|
+
const branch = safe(() => git3(dir, ["branch", "--show-current"])) ?? "";
|
|
24233
|
+
const commitHash = safe(() => git3(dir, ["rev-parse", "--short", "HEAD"])) ?? "";
|
|
24234
|
+
return { committed: true, branch, commitHash };
|
|
24235
|
+
}
|
|
24236
|
+
function discardChanges(db, workspacePath, projectId, paths) {
|
|
24237
|
+
const dir = projectDir(db, workspacePath, projectId);
|
|
24238
|
+
if (!existsSync17(dir) || !isGitRepo(dir)) {
|
|
24239
|
+
throw new GitServiceError("NO_GIT_REPO", 404, "No git repository for this project");
|
|
24240
|
+
}
|
|
24241
|
+
for (const path of paths) {
|
|
24242
|
+
safe(() => git3(dir, ["reset", "-q", "HEAD", "--", path]));
|
|
24243
|
+
const inHead = safe(() => git3(dir, ["cat-file", "-e", `HEAD:${path}`])) !== null;
|
|
24244
|
+
if (inHead) {
|
|
24245
|
+
try {
|
|
24246
|
+
git3(dir, ["checkout", "--", path]);
|
|
24247
|
+
} catch (e) {
|
|
24248
|
+
throw new GitServiceError("GIT_DISCARD_FAILED", 409, detail(e));
|
|
24249
|
+
}
|
|
24250
|
+
} else {
|
|
24251
|
+
const abs = join19(dir, path);
|
|
24252
|
+
if (existsSync17(abs)) rmSync(abs, { force: true });
|
|
24253
|
+
}
|
|
24254
|
+
}
|
|
24255
|
+
}
|
|
24256
|
+
function resolveConflict(db, workspacePath, projectId, input) {
|
|
24257
|
+
const dir = projectDir(db, workspacePath, projectId);
|
|
24258
|
+
if (!existsSync17(dir) || !isGitRepo(dir)) {
|
|
24259
|
+
throw new GitServiceError("NO_GIT_REPO", 404, "No git repository for this project");
|
|
24260
|
+
}
|
|
24261
|
+
try {
|
|
24262
|
+
if (input.resolution === "manual") {
|
|
24263
|
+
if (input.content === void 0) {
|
|
24264
|
+
throw new GitServiceError("CONTENT_REQUIRED", 400, "Manual resolution requires content");
|
|
24265
|
+
}
|
|
24266
|
+
writeFileSync10(join19(dir, input.path), input.content, "utf8");
|
|
24267
|
+
} else {
|
|
24268
|
+
git3(dir, ["checkout", `--${input.resolution}`, "--", input.path]);
|
|
24269
|
+
}
|
|
24270
|
+
git3(dir, ["add", "--", input.path]);
|
|
24271
|
+
} catch (e) {
|
|
24272
|
+
if (e instanceof GitServiceError) throw e;
|
|
24273
|
+
throw new GitServiceError("GIT_RESOLVE_FAILED", 409, detail(e));
|
|
24274
|
+
}
|
|
24275
|
+
}
|
|
24276
|
+
function detail(e) {
|
|
24277
|
+
const stderr = e.stderr?.toString().trim();
|
|
24278
|
+
return stderr || e.message || String(e);
|
|
24279
|
+
}
|
|
24280
|
+
function safe(fn) {
|
|
24281
|
+
try {
|
|
24282
|
+
return fn();
|
|
24283
|
+
} catch {
|
|
24284
|
+
return null;
|
|
24285
|
+
}
|
|
24286
|
+
}
|
|
24287
|
+
|
|
24288
|
+
// ../server/src/features/git/routes.ts
|
|
24289
|
+
function errorResponse7(c, e) {
|
|
24290
|
+
const err = e;
|
|
24291
|
+
const status = err.status ?? 500;
|
|
24292
|
+
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
24293
|
+
}
|
|
24294
|
+
function gitRoutes(db, workspacePath) {
|
|
24295
|
+
const app = new Hono2();
|
|
24296
|
+
app.get("/status/:projectId", (c) => {
|
|
24297
|
+
const projectId = Number(c.req.param("projectId"));
|
|
24298
|
+
if (Number.isNaN(projectId)) return c.json({ code: "INVALID_ID" }, 400);
|
|
24299
|
+
try {
|
|
24300
|
+
return c.json(getGitStatus(db, workspacePath, projectId));
|
|
24301
|
+
} catch (e) {
|
|
24302
|
+
return errorResponse7(c, e);
|
|
24303
|
+
}
|
|
24304
|
+
});
|
|
24305
|
+
app.get("/diff/:projectId", (c) => {
|
|
24306
|
+
const projectId = Number(c.req.param("projectId"));
|
|
24307
|
+
if (Number.isNaN(projectId)) return c.json({ code: "INVALID_ID" }, 400);
|
|
24308
|
+
const path = c.req.query("path");
|
|
24309
|
+
if (!path) return c.json({ code: "PATH_REQUIRED" }, 400);
|
|
24310
|
+
try {
|
|
24311
|
+
return c.json(getFileDiff(db, workspacePath, projectId, path));
|
|
24312
|
+
} catch (e) {
|
|
24313
|
+
return errorResponse7(c, e);
|
|
24314
|
+
}
|
|
24315
|
+
});
|
|
24316
|
+
app.post("/commit/:projectId", zValidator("json", gitCommitRequestSchema), (c) => {
|
|
24317
|
+
const projectId = Number(c.req.param("projectId"));
|
|
24318
|
+
if (Number.isNaN(projectId)) return c.json({ code: "INVALID_ID" }, 400);
|
|
24319
|
+
try {
|
|
24320
|
+
const body = c.req.valid("json");
|
|
24321
|
+
return c.json(commitChanges(db, workspacePath, projectId, body));
|
|
24322
|
+
} catch (e) {
|
|
24323
|
+
return errorResponse7(c, e);
|
|
24324
|
+
}
|
|
24325
|
+
});
|
|
24326
|
+
app.post("/discard/:projectId", zValidator("json", gitDiscardRequestSchema), (c) => {
|
|
24327
|
+
const projectId = Number(c.req.param("projectId"));
|
|
24328
|
+
if (Number.isNaN(projectId)) return c.json({ code: "INVALID_ID" }, 400);
|
|
24329
|
+
try {
|
|
24330
|
+
discardChanges(db, workspacePath, projectId, c.req.valid("json").paths);
|
|
24331
|
+
return c.json({ ok: true });
|
|
24332
|
+
} catch (e) {
|
|
24333
|
+
return errorResponse7(c, e);
|
|
24334
|
+
}
|
|
24335
|
+
});
|
|
24336
|
+
app.post("/resolve/:projectId", zValidator("json", gitResolveRequestSchema), (c) => {
|
|
24337
|
+
const projectId = Number(c.req.param("projectId"));
|
|
24338
|
+
if (Number.isNaN(projectId)) return c.json({ code: "INVALID_ID" }, 400);
|
|
24339
|
+
try {
|
|
24340
|
+
resolveConflict(db, workspacePath, projectId, c.req.valid("json"));
|
|
24341
|
+
return c.json({ ok: true });
|
|
24342
|
+
} catch (e) {
|
|
24343
|
+
if (e instanceof GitServiceError) return errorResponse7(c, e);
|
|
24344
|
+
return errorResponse7(c, e);
|
|
24345
|
+
}
|
|
24346
|
+
});
|
|
24347
|
+
return app;
|
|
24348
|
+
}
|
|
24349
|
+
|
|
23803
24350
|
// ../server/src/features/notification/routes.ts
|
|
23804
24351
|
function notificationRoutes(db) {
|
|
23805
24352
|
const app = new Hono2();
|
|
@@ -23830,7 +24377,7 @@ function notificationRoutes(db) {
|
|
|
23830
24377
|
}
|
|
23831
24378
|
|
|
23832
24379
|
// ../server/src/features/prd-wizard/routes.ts
|
|
23833
|
-
function
|
|
24380
|
+
function errorResponse8(c, e) {
|
|
23834
24381
|
const err = e;
|
|
23835
24382
|
const status = err.status ?? 500;
|
|
23836
24383
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -23859,7 +24406,7 @@ function prdWizardRoutes(db, workspacePath) {
|
|
|
23859
24406
|
startWizardApplyWatcher(db, workspacePath, requestId);
|
|
23860
24407
|
return c.json({ requestId, filePath, status: "generated" }, 201);
|
|
23861
24408
|
} catch (e) {
|
|
23862
|
-
return
|
|
24409
|
+
return errorResponse8(c, e);
|
|
23863
24410
|
}
|
|
23864
24411
|
});
|
|
23865
24412
|
app.get("/:projectId", (c) => {
|
|
@@ -23871,14 +24418,14 @@ function prdWizardRoutes(db, workspacePath) {
|
|
|
23871
24418
|
try {
|
|
23872
24419
|
return c.json(listPrdWizardRequests(db, projectId, prdVersionId));
|
|
23873
24420
|
} catch (e) {
|
|
23874
|
-
return
|
|
24421
|
+
return errorResponse8(c, e);
|
|
23875
24422
|
}
|
|
23876
24423
|
});
|
|
23877
24424
|
return app;
|
|
23878
24425
|
}
|
|
23879
24426
|
|
|
23880
24427
|
// ../server/src/features/project-config/routes.ts
|
|
23881
|
-
function
|
|
24428
|
+
function errorResponse9(c, e) {
|
|
23882
24429
|
const err = e;
|
|
23883
24430
|
const status = err.status ?? 500;
|
|
23884
24431
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -23895,7 +24442,7 @@ function projectConfigRoutes(db, workspacePath) {
|
|
|
23895
24442
|
try {
|
|
23896
24443
|
return c.json(getProjectEnvironment(db, projectId));
|
|
23897
24444
|
} catch (e) {
|
|
23898
|
-
return
|
|
24445
|
+
return errorResponse9(c, e);
|
|
23899
24446
|
}
|
|
23900
24447
|
});
|
|
23901
24448
|
app.patch("/:projectId/environment", zValidator("json", saveWorkspaceConfigSchema), (c) => {
|
|
@@ -23906,7 +24453,7 @@ function projectConfigRoutes(db, workspacePath) {
|
|
|
23906
24453
|
saveProjectEnvironment(db, workspacePath, projectId, c.req.valid("json").content)
|
|
23907
24454
|
);
|
|
23908
24455
|
} catch (e) {
|
|
23909
|
-
return
|
|
24456
|
+
return errorResponse9(c, e);
|
|
23910
24457
|
}
|
|
23911
24458
|
});
|
|
23912
24459
|
app.post(
|
|
@@ -23929,7 +24476,7 @@ function projectConfigRoutes(db, workspacePath) {
|
|
|
23929
24476
|
)
|
|
23930
24477
|
);
|
|
23931
24478
|
} catch (e) {
|
|
23932
|
-
return
|
|
24479
|
+
return errorResponse9(c, e);
|
|
23933
24480
|
}
|
|
23934
24481
|
}
|
|
23935
24482
|
);
|
|
@@ -23939,7 +24486,7 @@ function projectConfigRoutes(db, workspacePath) {
|
|
|
23939
24486
|
try {
|
|
23940
24487
|
return c.json(getProjectDevtool(db, projectId));
|
|
23941
24488
|
} catch (e) {
|
|
23942
|
-
return
|
|
24489
|
+
return errorResponse9(c, e);
|
|
23943
24490
|
}
|
|
23944
24491
|
});
|
|
23945
24492
|
app.patch("/:projectId/devtool", zValidator("json", saveWorkspaceConfigSchema), (c) => {
|
|
@@ -23948,7 +24495,7 @@ function projectConfigRoutes(db, workspacePath) {
|
|
|
23948
24495
|
try {
|
|
23949
24496
|
return c.json(saveProjectDevtool(db, workspacePath, projectId, c.req.valid("json").content));
|
|
23950
24497
|
} catch (e) {
|
|
23951
|
-
return
|
|
24498
|
+
return errorResponse9(c, e);
|
|
23952
24499
|
}
|
|
23953
24500
|
});
|
|
23954
24501
|
app.post(
|
|
@@ -23965,7 +24512,7 @@ function projectConfigRoutes(db, workspacePath) {
|
|
|
23965
24512
|
importProjectDevtool(db, workspacePath, projectId, c.req.valid("json").content, user.id)
|
|
23966
24513
|
);
|
|
23967
24514
|
} catch (e) {
|
|
23968
|
-
return
|
|
24515
|
+
return errorResponse9(c, e);
|
|
23969
24516
|
}
|
|
23970
24517
|
}
|
|
23971
24518
|
);
|
|
@@ -23973,8 +24520,8 @@ function projectConfigRoutes(db, workspacePath) {
|
|
|
23973
24520
|
}
|
|
23974
24521
|
|
|
23975
24522
|
// ../server/src/features/project-import/scanner.ts
|
|
23976
|
-
import { existsSync as
|
|
23977
|
-
import { join as
|
|
24523
|
+
import { existsSync as existsSync18, readFileSync as readFileSync13, readdirSync as readdirSync3, statSync as statSync5 } from "fs";
|
|
24524
|
+
import { join as join20 } from "path";
|
|
23978
24525
|
|
|
23979
24526
|
// ../server/src/features/project-import/changelog-md.ts
|
|
23980
24527
|
var VERSION_HEADING = /^##\s+v?(\d+\.\d+\.\d+)\b/;
|
|
@@ -24122,64 +24669,64 @@ function svcError13(code, status) {
|
|
|
24122
24669
|
return Object.assign(new Error(code), { code, status });
|
|
24123
24670
|
}
|
|
24124
24671
|
function readIfExists(path) {
|
|
24125
|
-
return
|
|
24672
|
+
return existsSync18(path) ? readFileSync13(path, "utf8") : null;
|
|
24126
24673
|
}
|
|
24127
24674
|
function emptyArtifactCounts() {
|
|
24128
24675
|
return { erd: 0, data_model: 0, mockup: 0, technical_spec: 0, test_spec: 0, source_code: 0 };
|
|
24129
24676
|
}
|
|
24130
24677
|
function countArtifacts(appDir) {
|
|
24131
24678
|
const counts = emptyArtifactCounts();
|
|
24132
|
-
const contextDir =
|
|
24133
|
-
const modelDir =
|
|
24134
|
-
if (
|
|
24679
|
+
const contextDir = join20(appDir, "context");
|
|
24680
|
+
const modelDir = join20(contextDir, "model");
|
|
24681
|
+
if (existsSync18(modelDir)) {
|
|
24135
24682
|
for (const slug of readdirSync3(modelDir)) {
|
|
24136
24683
|
try {
|
|
24137
|
-
if (!statSync5(
|
|
24684
|
+
if (!statSync5(join20(modelDir, slug)).isDirectory()) continue;
|
|
24138
24685
|
} catch {
|
|
24139
24686
|
continue;
|
|
24140
24687
|
}
|
|
24141
|
-
if (
|
|
24688
|
+
if (existsSync18(join20(modelDir, slug, "erd.mermaid")) || existsSync18(join20(modelDir, slug, "document-model.mermaid")))
|
|
24142
24689
|
counts.erd++;
|
|
24143
|
-
if (
|
|
24144
|
-
if (
|
|
24145
|
-
if (
|
|
24690
|
+
if (existsSync18(join20(modelDir, slug, "model.md"))) counts.data_model++;
|
|
24691
|
+
if (existsSync18(join20(contextDir, "specification", slug, "SPEC.md"))) counts.technical_spec++;
|
|
24692
|
+
if (existsSync18(join20(contextDir, "test", slug, "TEST_SPEC.md"))) counts.test_spec++;
|
|
24146
24693
|
}
|
|
24147
24694
|
}
|
|
24148
24695
|
for (const [sub, key, file] of [
|
|
24149
24696
|
["specification", "technical_spec", "SPEC.md"],
|
|
24150
24697
|
["test", "test_spec", "TEST_SPEC.md"]
|
|
24151
24698
|
]) {
|
|
24152
|
-
const dir =
|
|
24153
|
-
if (!
|
|
24699
|
+
const dir = join20(contextDir, sub);
|
|
24700
|
+
if (!existsSync18(dir)) continue;
|
|
24154
24701
|
let total = 0;
|
|
24155
24702
|
for (const slug of readdirSync3(dir)) {
|
|
24156
24703
|
try {
|
|
24157
|
-
if (statSync5(
|
|
24704
|
+
if (statSync5(join20(dir, slug)).isDirectory() && existsSync18(join20(dir, slug, file))) total++;
|
|
24158
24705
|
} catch {
|
|
24159
24706
|
}
|
|
24160
24707
|
}
|
|
24161
24708
|
counts[key] = Math.max(counts[key], total);
|
|
24162
24709
|
}
|
|
24163
|
-
if (
|
|
24710
|
+
if (existsSync18(join20(contextDir, "mockup", "MOCKUP.html")) || existsSync18(join20(contextDir, "mockup", "index.html")))
|
|
24164
24711
|
counts.mockup = 1;
|
|
24165
24712
|
counts.source_code = sweepSourceFiles(appDir).length;
|
|
24166
24713
|
return counts;
|
|
24167
24714
|
}
|
|
24168
24715
|
function findClaudeMd(folderDir) {
|
|
24169
|
-
if (
|
|
24170
|
-
if (
|
|
24716
|
+
if (existsSync18(join20(folderDir, "CLAUDE.md"))) return { relPath: "CLAUDE.md", prefix: "" };
|
|
24717
|
+
if (existsSync18(join20(folderDir, "source", "CLAUDE.md")))
|
|
24171
24718
|
return { relPath: "source/CLAUDE.md", prefix: "source" };
|
|
24172
24719
|
return null;
|
|
24173
24720
|
}
|
|
24174
24721
|
function listImportCandidates(db, workspacePath) {
|
|
24175
|
-
if (!
|
|
24722
|
+
if (!existsSync18(workspacePath)) return [];
|
|
24176
24723
|
const registered = new Set(
|
|
24177
24724
|
db.select({ folder: projects.workspaceFolderName }).from(projects).all().map((r) => r.folder)
|
|
24178
24725
|
);
|
|
24179
24726
|
const candidates = [];
|
|
24180
24727
|
for (const entry of readdirSync3(workspacePath)) {
|
|
24181
24728
|
if (entry.startsWith(".") || registered.has(entry)) continue;
|
|
24182
|
-
const dir =
|
|
24729
|
+
const dir = join20(workspacePath, entry);
|
|
24183
24730
|
try {
|
|
24184
24731
|
if (!statSync5(dir).isDirectory()) continue;
|
|
24185
24732
|
} catch {
|
|
@@ -24187,14 +24734,14 @@ function listImportCandidates(db, workspacePath) {
|
|
|
24187
24734
|
}
|
|
24188
24735
|
const found = findClaudeMd(dir);
|
|
24189
24736
|
if (!found) continue;
|
|
24190
|
-
const
|
|
24191
|
-
|
|
24737
|
+
const detail2 = parseProjectDetail(
|
|
24738
|
+
readFileSync13(join20(workspacePath, entry, found.relPath), "utf8")
|
|
24192
24739
|
);
|
|
24193
24740
|
candidates.push({
|
|
24194
24741
|
folderName: entry,
|
|
24195
24742
|
claudeMdPath: `${entry}/${found.relPath}`,
|
|
24196
|
-
projectName:
|
|
24197
|
-
projectCode:
|
|
24743
|
+
projectName: detail2.projectName,
|
|
24744
|
+
projectCode: detail2.projectCode
|
|
24198
24745
|
});
|
|
24199
24746
|
}
|
|
24200
24747
|
return candidates.sort((a, b) => a.folderName.localeCompare(b.folderName));
|
|
@@ -24202,22 +24749,22 @@ function listImportCandidates(db, workspacePath) {
|
|
|
24202
24749
|
function scanRepo(db, workspacePath, folderName) {
|
|
24203
24750
|
const registered = db.select({ id: projects.id }).from(projects).where(eq(projects.workspaceFolderName, folderName)).limit(1).get();
|
|
24204
24751
|
if (registered) throw svcError13("FOLDER_ALREADY_IMPORTED", 409);
|
|
24205
|
-
const folderDir =
|
|
24206
|
-
const found =
|
|
24752
|
+
const folderDir = join20(workspacePath, folderName);
|
|
24753
|
+
const found = existsSync18(folderDir) ? findClaudeMd(folderDir) : null;
|
|
24207
24754
|
if (!found) throw svcError13("CLAUDE_MD_MISSING", 404);
|
|
24208
|
-
const repoRoot = found.prefix ?
|
|
24209
|
-
const claudeMdRaw =
|
|
24210
|
-
const
|
|
24755
|
+
const repoRoot = found.prefix ? join20(folderDir, found.prefix) : folderDir;
|
|
24756
|
+
const claudeMdRaw = readFileSync13(join20(folderDir, found.relPath), "utf8");
|
|
24757
|
+
const detail2 = parseProjectDetail(claudeMdRaw);
|
|
24211
24758
|
const claudeApps = parseClaudeMdApplications(claudeMdRaw);
|
|
24212
24759
|
const claudeModules = new Set(parseModuleNames(claudeMdRaw).map((m) => m.name));
|
|
24213
24760
|
const warnings = [];
|
|
24214
|
-
if (
|
|
24215
|
-
const nameTaken = db.select({ id: projects.id }).from(projects).where(eq(projects.name,
|
|
24761
|
+
if (detail2.projectName) {
|
|
24762
|
+
const nameTaken = db.select({ id: projects.id }).from(projects).where(eq(projects.name, detail2.projectName)).limit(1).get();
|
|
24216
24763
|
if (nameTaken) {
|
|
24217
24764
|
warnings.push({
|
|
24218
24765
|
level: "warning",
|
|
24219
24766
|
code: "DUPLICATE_PROJECT_NAME",
|
|
24220
|
-
message: `A project named "${
|
|
24767
|
+
message: `A project named "${detail2.projectName}" already exists \u2014 rename before importing.`
|
|
24221
24768
|
});
|
|
24222
24769
|
}
|
|
24223
24770
|
}
|
|
@@ -24229,10 +24776,10 @@ function scanRepo(db, workspacePath, folderName) {
|
|
|
24229
24776
|
for (const claude of claudeApps) {
|
|
24230
24777
|
const name = toKebabCase(claude.heading);
|
|
24231
24778
|
const resolvedInRoot = resolveAppFolderName(repoRoot, name);
|
|
24232
|
-
const folderExists =
|
|
24779
|
+
const folderExists = existsSync18(join20(repoRoot, resolvedInRoot));
|
|
24233
24780
|
const folderRel = folderExists ? found.prefix ? `${found.prefix}/${resolvedInRoot}` : resolvedInRoot : null;
|
|
24234
|
-
const appDir = folderRel ?
|
|
24235
|
-
const prdRaw = appDir ? readIfExists(
|
|
24781
|
+
const appDir = folderRel ? join20(folderDir, folderRel) : null;
|
|
24782
|
+
const prdRaw = appDir ? readIfExists(join20(appDir, "context", "PRD.md")) : null;
|
|
24236
24783
|
const prdModules = prdRaw ? parsePrdMarkdown(prdRaw) : [];
|
|
24237
24784
|
const versions = /* @__PURE__ */ new Set();
|
|
24238
24785
|
for (const m of prdModules) {
|
|
@@ -24243,19 +24790,19 @@ function scanRepo(db, workspacePath, folderName) {
|
|
|
24243
24790
|
for (const v of versions) semverUnion.add(v);
|
|
24244
24791
|
const derived = prdRaw ? deriveRequirementIdAppCode(prdRaw, name) : { code: appCodeFromInitials(name), derivedFromIds: false };
|
|
24245
24792
|
const requirementIdAppCode = claude.requirementIdAppCode ?? derived.code;
|
|
24246
|
-
const bugMdRaw = appDir ? readIfExists(
|
|
24793
|
+
const bugMdRaw = appDir ? readIfExists(join20(appDir, "context", "BUG.md")) : null;
|
|
24247
24794
|
const bugItems = bugMdRaw ? parseBugMd(bugMdRaw) : [];
|
|
24248
|
-
const bugMasterRaw = appDir ? readIfExists(
|
|
24795
|
+
const bugMasterRaw = appDir ? readIfExists(join20(appDir, "context", "bug", "BUG_MASTER.md")) : null;
|
|
24249
24796
|
const bugMaster = bugMasterRaw ? parseBugMaster(bugMasterRaw) : /* @__PURE__ */ new Map();
|
|
24250
24797
|
for (const item of bugItems) if (item.semver) semverUnion.add(item.semver);
|
|
24251
24798
|
for (const [, m] of bugMaster) if (m.semver) semverUnion.add(m.semver);
|
|
24252
|
-
const preparePath = appDir ?
|
|
24253
|
-
const developPath = appDir ?
|
|
24254
|
-
const prepareMasterExists = !!appDir &&
|
|
24255
|
-
const developMasterExists = !!appDir &&
|
|
24799
|
+
const preparePath = appDir ? join20(appDir, "context", "prepare", "PREPARE_MASTER.md") : "";
|
|
24800
|
+
const developPath = appDir ? join20(appDir, "context", "develop", "IMPLEMENTATION_MASTER.md") : "";
|
|
24801
|
+
const prepareMasterExists = !!appDir && existsSync18(preparePath);
|
|
24802
|
+
const developMasterExists = !!appDir && existsSync18(developPath);
|
|
24256
24803
|
const prepareCompleted = prepareMasterExists && parseTrackingStatus(preparePath) === "COMPLETED";
|
|
24257
24804
|
const developCompleted = developMasterExists && parseTrackingStatus(developPath) === "COMPLETED";
|
|
24258
|
-
const changelogRaw = appDir ? readIfExists(
|
|
24805
|
+
const changelogRaw = appDir ? readIfExists(join20(appDir, "CHANGELOG.md")) : null;
|
|
24259
24806
|
const changelogEntries = changelogRaw ? parseChangelogConductorEntries(changelogRaw) : [];
|
|
24260
24807
|
if (!folderRel) {
|
|
24261
24808
|
warnings.push({
|
|
@@ -24337,21 +24884,28 @@ function scanRepo(db, workspacePath, folderName) {
|
|
|
24337
24884
|
}
|
|
24338
24885
|
const versionUnion = [...semverUnion].sort(compareSemver);
|
|
24339
24886
|
const workflowPhase = anyBugs ? "qa" : anyDevelopCompleted ? "developed" : anyPrepareMaster ? "prepared" : "brainstorm";
|
|
24340
|
-
const environmentMd = readIfExists(
|
|
24341
|
-
const devtoolMd = readIfExists(
|
|
24887
|
+
const environmentMd = readIfExists(join20(repoRoot, "ENVIRONMENT.md")) ?? readIfExists(join20(folderDir, "ENVIRONMENT.md"));
|
|
24888
|
+
const devtoolMd = readIfExists(join20(repoRoot, "DEVTOOL.md")) ?? readIfExists(join20(folderDir, "DEVTOOL.md"));
|
|
24889
|
+
const origin = readOriginUrl(folderDir);
|
|
24890
|
+
const gitRemoteUrl = origin ? stripHttpsCredentials(origin) : origin;
|
|
24891
|
+
const defaultBranch = existsSync18(join20(folderDir, ".git")) ? detectTrunkBranch(folderDir, readCurrentBranch(folderDir)) : "main";
|
|
24892
|
+
const gitProvider = /gitlab/i.test(gitRemoteUrl ?? "") ? "gitlab" : "github";
|
|
24342
24893
|
return {
|
|
24343
24894
|
folderName,
|
|
24344
24895
|
sourcePrefix: found.prefix,
|
|
24345
24896
|
claudeMdRelPath: found.relPath,
|
|
24346
24897
|
claudeMdRaw,
|
|
24347
|
-
projectName:
|
|
24348
|
-
projectCode:
|
|
24898
|
+
projectName: detail2.projectName,
|
|
24899
|
+
projectCode: detail2.projectCode,
|
|
24349
24900
|
apps,
|
|
24350
24901
|
versionUnion,
|
|
24351
24902
|
currentVersion: versionUnion.at(-1) ?? null,
|
|
24352
24903
|
workflowPhase,
|
|
24353
24904
|
environmentMd,
|
|
24354
24905
|
devtoolMd,
|
|
24906
|
+
gitRemoteUrl,
|
|
24907
|
+
defaultBranch,
|
|
24908
|
+
gitProvider,
|
|
24355
24909
|
warnings
|
|
24356
24910
|
};
|
|
24357
24911
|
}
|
|
@@ -24390,11 +24944,11 @@ function scanProject(db, workspacePath, folderName) {
|
|
|
24390
24944
|
}
|
|
24391
24945
|
|
|
24392
24946
|
// ../server/src/features/project-import/service.ts
|
|
24393
|
-
import { join as
|
|
24947
|
+
import { join as join22, relative as relative2 } from "path";
|
|
24394
24948
|
|
|
24395
24949
|
// ../server/src/features/workflow/service.ts
|
|
24396
|
-
import { existsSync as
|
|
24397
|
-
import { join as
|
|
24950
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
|
|
24951
|
+
import { join as join21, relative, resolve as resolve2, sep } from "path";
|
|
24398
24952
|
var ACTIVE_RUN_STATUSES = ["pending", "in_progress"];
|
|
24399
24953
|
function svcError14(code, status) {
|
|
24400
24954
|
return Object.assign(new Error(code), { code, status });
|
|
@@ -24536,9 +25090,9 @@ function validateAllAppsPending(db, projectId, applicationIds, conductorType, pr
|
|
|
24536
25090
|
const notPending = applicationIds.filter((id) => !pending.has(id));
|
|
24537
25091
|
if (notPending.length > 0) throw svcError14("APPS_NOT_PENDING", 422);
|
|
24538
25092
|
}
|
|
24539
|
-
function resolveRunTrackingFilePath(
|
|
25093
|
+
function resolveRunTrackingFilePath(projectDir2, appName, conductorType) {
|
|
24540
25094
|
const master = TRACKING_FILE_MAP[conductorType] ?? "";
|
|
24541
|
-
return
|
|
25095
|
+
return join21(projectDir2, resolveAppFolderName(projectDir2, appName), master);
|
|
24542
25096
|
}
|
|
24543
25097
|
function buildWorkflowPath(username) {
|
|
24544
25098
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\..+$/, "").replace("T", "-");
|
|
@@ -24611,7 +25165,7 @@ function generateWorkflowMarkdown(db, workspacePath, input, creatingUser, watche
|
|
|
24611
25165
|
const ver = db.select({ semver: prdVersions.semver }).from(prdVersions).where(eq(prdVersions.id, prdVersionId)).get();
|
|
24612
25166
|
const semver = ver?.semver ?? "latest";
|
|
24613
25167
|
const relPath = buildWorkflowPath(creatingUser.username);
|
|
24614
|
-
const
|
|
25168
|
+
const projectDir2 = join21(workspacePath, project.workspaceFolderName);
|
|
24615
25169
|
const content = buildWorkflowMarkdown(
|
|
24616
25170
|
input.conductorType,
|
|
24617
25171
|
apps.map((a) => a.name),
|
|
@@ -24619,8 +25173,8 @@ function generateWorkflowMarkdown(db, workspacePath, input, creatingUser, watche
|
|
|
24619
25173
|
project.workspaceFolderName,
|
|
24620
25174
|
relPath
|
|
24621
25175
|
);
|
|
24622
|
-
mkdirSync9(
|
|
24623
|
-
|
|
25176
|
+
mkdirSync9(join21(projectDir2, "workflow", creatingUser.username), { recursive: true });
|
|
25177
|
+
writeFileSync11(join21(projectDir2, relPath), content, "utf8");
|
|
24624
25178
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24625
25179
|
const wfm = db.insert(workflowMarkdowns).values({
|
|
24626
25180
|
projectId: input.projectId,
|
|
@@ -24636,7 +25190,7 @@ function generateWorkflowMarkdown(db, workspacePath, input, creatingUser, watche
|
|
|
24636
25190
|
const runApps = [];
|
|
24637
25191
|
for (const appId of input.applicationIds) {
|
|
24638
25192
|
const appName = apps.find((a) => a.id === appId)?.name ?? "";
|
|
24639
|
-
const trackingFilePath = resolveRunTrackingFilePath(
|
|
25193
|
+
const trackingFilePath = resolveRunTrackingFilePath(projectDir2, appName, input.conductorType);
|
|
24640
25194
|
const run = db.insert(conductorRuns).values({
|
|
24641
25195
|
projectId: input.projectId,
|
|
24642
25196
|
applicationId: appId,
|
|
@@ -24660,7 +25214,7 @@ function generateWorkflowMarkdown(db, workspacePath, input, creatingUser, watche
|
|
|
24660
25214
|
startWatcher(
|
|
24661
25215
|
db,
|
|
24662
25216
|
runId,
|
|
24663
|
-
() => resolveRunTrackingFilePath(
|
|
25217
|
+
() => resolveRunTrackingFilePath(projectDir2, appName, input.conductorType),
|
|
24664
25218
|
creatingUser.id,
|
|
24665
25219
|
watcherIntervalMs,
|
|
24666
25220
|
(id, status) => {
|
|
@@ -24699,13 +25253,13 @@ function backfillWorkflowHistoryFromChangelogs(db, workspacePath, projectId) {
|
|
|
24699
25253
|
if (!project) return 0;
|
|
24700
25254
|
const apps = db.select({ id: applications.id, name: applications.name }).from(applications).where(and(eq(applications.projectId, projectId), eq(applications.isDeleted, 0))).all();
|
|
24701
25255
|
if (apps.length === 0) return 0;
|
|
24702
|
-
const
|
|
25256
|
+
const projectDir2 = join21(workspacePath, project.workspaceFolderName);
|
|
24703
25257
|
const collected = [];
|
|
24704
25258
|
for (const app of apps) {
|
|
24705
|
-
const appRel = resolveAppFolderName(
|
|
24706
|
-
const changelogPath =
|
|
24707
|
-
if (!
|
|
24708
|
-
for (const entry of parseChangelogConductorEntries(
|
|
25259
|
+
const appRel = resolveAppFolderName(projectDir2, app.name);
|
|
25260
|
+
const changelogPath = join21(projectDir2, appRel, "CHANGELOG.md");
|
|
25261
|
+
if (!existsSync19(changelogPath)) continue;
|
|
25262
|
+
for (const entry of parseChangelogConductorEntries(readFileSync14(changelogPath, "utf8"))) {
|
|
24709
25263
|
collected.push({ appName: app.name, appRel, entry });
|
|
24710
25264
|
}
|
|
24711
25265
|
}
|
|
@@ -24751,7 +25305,7 @@ function getActiveRun(db, projectId, workspacePath) {
|
|
|
24751
25305
|
function resumeWatchers(db, workspacePath, projectId, watcherIntervalMs) {
|
|
24752
25306
|
const project = db.select().from(projects).where(eq(projects.id, projectId)).get();
|
|
24753
25307
|
if (!project) return;
|
|
24754
|
-
const
|
|
25308
|
+
const projectDir2 = join21(workspacePath, project.workspaceFolderName);
|
|
24755
25309
|
const stale = db.select({ run: conductorRuns, appName: applications.name }).from(conductorRuns).innerJoin(applications, eq(applications.id, conductorRuns.applicationId)).where(
|
|
24756
25310
|
and(
|
|
24757
25311
|
eq(conductorRuns.projectId, projectId),
|
|
@@ -24766,7 +25320,7 @@ function resumeWatchers(db, workspacePath, projectId, watcherIntervalMs) {
|
|
|
24766
25320
|
db,
|
|
24767
25321
|
run.id,
|
|
24768
25322
|
// [BUG-027] Re-resolve per poll — heals rows persisted with a stale/project-level path.
|
|
24769
|
-
() => resolveRunTrackingFilePath(
|
|
25323
|
+
() => resolveRunTrackingFilePath(projectDir2, appName, conductorType),
|
|
24770
25324
|
run.startedBy ?? 1,
|
|
24771
25325
|
watcherIntervalMs,
|
|
24772
25326
|
(id, status) => {
|
|
@@ -24801,7 +25355,7 @@ function rescanWorkflowRun(db, workspacePath, workflowMarkdownId, watcherInterva
|
|
|
24801
25355
|
if (!wfm) throw svcError14("RUN_NOT_FOUND", 404);
|
|
24802
25356
|
const project = db.select().from(projects).where(eq(projects.id, wfm.projectId)).get();
|
|
24803
25357
|
if (!project) throw svcError14("PROJECT_NOT_FOUND", 404);
|
|
24804
|
-
const
|
|
25358
|
+
const projectDir2 = join21(workspacePath, project.workspaceFolderName);
|
|
24805
25359
|
const runs = db.select({ run: conductorRuns, appName: applications.name }).from(conductorRuns).innerJoin(applications, eq(applications.id, conductorRuns.applicationId)).where(
|
|
24806
25360
|
and(
|
|
24807
25361
|
eq(conductorRuns.workflowMarkdownId, workflowMarkdownId),
|
|
@@ -24810,7 +25364,7 @@ function rescanWorkflowRun(db, workspacePath, workflowMarkdownId, watcherInterva
|
|
|
24810
25364
|
).all();
|
|
24811
25365
|
for (const { run, appName } of runs) {
|
|
24812
25366
|
const conductorType = run.conductorType;
|
|
24813
|
-
const freshPath = resolveRunTrackingFilePath(
|
|
25367
|
+
const freshPath = resolveRunTrackingFilePath(projectDir2, appName, conductorType);
|
|
24814
25368
|
if (freshPath !== run.trackingFilePath) {
|
|
24815
25369
|
db.update(conductorRuns).set({ trackingFilePath: freshPath }).where(eq(conductorRuns.id, run.id)).run();
|
|
24816
25370
|
}
|
|
@@ -24830,7 +25384,7 @@ function rescanWorkflowRun(db, workspacePath, workflowMarkdownId, watcherInterva
|
|
|
24830
25384
|
startWatcher(
|
|
24831
25385
|
db,
|
|
24832
25386
|
run.id,
|
|
24833
|
-
() => resolveRunTrackingFilePath(
|
|
25387
|
+
() => resolveRunTrackingFilePath(projectDir2, appName, conductorType),
|
|
24834
25388
|
run.startedBy ?? 1,
|
|
24835
25389
|
watcherIntervalMs,
|
|
24836
25390
|
(id, terminalStatus) => {
|
|
@@ -24862,12 +25416,12 @@ function getWorkflowRunFileContent(db, workspacePath, workflowMarkdownId) {
|
|
|
24862
25416
|
if (!wfm) throw svcError14("RUN_NOT_FOUND", 404);
|
|
24863
25417
|
const project = db.select().from(projects).where(eq(projects.id, wfm.projectId)).get();
|
|
24864
25418
|
if (!project) throw svcError14("PROJECT_NOT_FOUND", 404);
|
|
24865
|
-
const
|
|
24866
|
-
const absolute = resolve2(
|
|
24867
|
-
if (absolute !==
|
|
25419
|
+
const projectDir2 = resolve2(workspacePath, project.workspaceFolderName);
|
|
25420
|
+
const absolute = resolve2(projectDir2, wfm.filePath);
|
|
25421
|
+
if (absolute !== projectDir2 && !absolute.startsWith(projectDir2 + sep))
|
|
24868
25422
|
throw svcError14("INVALID_PATH", 400);
|
|
24869
|
-
if (!
|
|
24870
|
-
return { filePath: wfm.filePath, content:
|
|
25423
|
+
if (!existsSync19(absolute)) throw svcError14("FILE_NOT_FOUND", 404);
|
|
25424
|
+
return { filePath: wfm.filePath, content: readFileSync14(absolute, "utf-8") };
|
|
24871
25425
|
}
|
|
24872
25426
|
function getRunApplicationChangelog(db, workspacePath, workflowMarkdownId, appName) {
|
|
24873
25427
|
const wfm = db.select().from(workflowMarkdowns).where(eq(workflowMarkdowns.id, workflowMarkdownId)).get();
|
|
@@ -24882,16 +25436,16 @@ function getRunApplicationChangelog(db, workspacePath, workflowMarkdownId, appNa
|
|
|
24882
25436
|
runAppNames = appIds.length > 0 ? db.select({ name: applications.name }).from(applications).where(inArray(applications.id, appIds)).all().map((a) => a.name) : [];
|
|
24883
25437
|
}
|
|
24884
25438
|
if (!runAppNames.includes(appName)) throw svcError14("APP_NOT_IN_RUN", 404);
|
|
24885
|
-
const
|
|
24886
|
-
const appFolder = resolveAppFolderName(
|
|
24887
|
-
const absolute = resolve2(
|
|
24888
|
-
if (absolute !==
|
|
25439
|
+
const projectDir2 = resolve2(workspacePath, project.workspaceFolderName);
|
|
25440
|
+
const appFolder = resolveAppFolderName(projectDir2, appName);
|
|
25441
|
+
const absolute = resolve2(projectDir2, appFolder, "CHANGELOG.md");
|
|
25442
|
+
if (absolute !== projectDir2 && !absolute.startsWith(projectDir2 + sep))
|
|
24889
25443
|
throw svcError14("INVALID_PATH", 400);
|
|
24890
|
-
if (!
|
|
25444
|
+
if (!existsSync19(absolute)) throw svcError14("CHANGELOG_NOT_FOUND", 404);
|
|
24891
25445
|
return {
|
|
24892
25446
|
applicationName: appName,
|
|
24893
25447
|
filePath: relative(workspacePath, absolute).replace(/\\/g, "/"),
|
|
24894
|
-
content:
|
|
25448
|
+
content: readFileSync14(absolute, "utf-8")
|
|
24895
25449
|
};
|
|
24896
25450
|
}
|
|
24897
25451
|
function toDetail(db, projectFolder, wfm, workspacePath) {
|
|
@@ -24926,20 +25480,20 @@ function toDetail(db, projectFolder, wfm, workspacePath) {
|
|
|
24926
25480
|
const appNames = appRows.map((a) => a.name);
|
|
24927
25481
|
let appBreakdown;
|
|
24928
25482
|
if (workspacePath) {
|
|
24929
|
-
const
|
|
25483
|
+
const projectDir2 = join21(workspacePath, projectFolder);
|
|
24930
25484
|
appBreakdown = runs.map((r) => {
|
|
24931
25485
|
const appName = nameOf.get(r.applicationId) ?? `app #${r.applicationId}`;
|
|
24932
|
-
const trackingPath = resolveRunTrackingFilePath(
|
|
25486
|
+
const trackingPath = resolveRunTrackingFilePath(projectDir2, appName, r.conductorType);
|
|
24933
25487
|
return {
|
|
24934
25488
|
applicationName: appName,
|
|
24935
25489
|
status: r.status,
|
|
24936
|
-
trackingFileExists:
|
|
25490
|
+
trackingFileExists: existsSync19(trackingPath),
|
|
24937
25491
|
trackingFilePath: relative(workspacePath, trackingPath).replace(/\\/g, "/")
|
|
24938
25492
|
};
|
|
24939
25493
|
});
|
|
24940
25494
|
}
|
|
24941
25495
|
const completedAt = runs.every((r) => r.completedAt) ? runs.map((r) => r.completedAt).sort().at(-1) ?? null : null;
|
|
24942
|
-
const
|
|
25496
|
+
const git4 = runs.find((r) => r.gitOperationStatus && r.gitOperationStatus !== "not_started");
|
|
24943
25497
|
return {
|
|
24944
25498
|
id: wfm.id,
|
|
24945
25499
|
conductorType: wfm.conductorType,
|
|
@@ -24949,9 +25503,9 @@ function toDetail(db, projectFolder, wfm, workspacePath) {
|
|
|
24949
25503
|
filePath: wfm.filePath,
|
|
24950
25504
|
executeCommand: buildExecuteCommand(projectFolder, wfm.filePath),
|
|
24951
25505
|
versionLabel: wfm.versionLabel,
|
|
24952
|
-
gitOperationStatus:
|
|
24953
|
-
gitBranchCreated:
|
|
24954
|
-
gitCommitHash:
|
|
25506
|
+
gitOperationStatus: git4?.gitOperationStatus ?? "not_started",
|
|
25507
|
+
gitBranchCreated: git4?.gitBranchCreated ?? null,
|
|
25508
|
+
gitCommitHash: git4?.gitCommitHash ?? null,
|
|
24955
25509
|
completedAt,
|
|
24956
25510
|
createdAt: wfm.createdAt
|
|
24957
25511
|
};
|
|
@@ -24972,7 +25526,7 @@ function importProject(db, workspacePath, input, userId) {
|
|
|
24972
25526
|
const nameTaken = db.select({ id: projects.id }).from(projects).where(eq(projects.name, name)).limit(1).get();
|
|
24973
25527
|
if (nameTaken) throw svcError15("PROJECT_EXISTS", 409);
|
|
24974
25528
|
const items = [];
|
|
24975
|
-
const report = (entity, action, label,
|
|
25529
|
+
const report = (entity, action, label, detail2) => items.push({ entity, action, label, ...detail2 ? { detail: detail2 } : {} });
|
|
24976
25530
|
const projectId = db.transaction((rawTx) => {
|
|
24977
25531
|
const tx = rawTx;
|
|
24978
25532
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -24985,6 +25539,9 @@ function importProject(db, workspacePath, input, userId) {
|
|
|
24985
25539
|
// bypasses the lazy brainstorm-rescan path
|
|
24986
25540
|
workflowPhase: scan.workflowPhase,
|
|
24987
25541
|
rawClaudeMd: scan.claudeMdRaw,
|
|
25542
|
+
gitRemoteUrl: scan.gitRemoteUrl ?? null,
|
|
25543
|
+
defaultBranch: scan.defaultBranch,
|
|
25544
|
+
gitProvider: scan.gitProvider,
|
|
24988
25545
|
importedAt: now,
|
|
24989
25546
|
importedBy: userId,
|
|
24990
25547
|
createdBy: userId,
|
|
@@ -25068,7 +25625,7 @@ function importProject(db, workspacePath, input, userId) {
|
|
|
25068
25625
|
snapshotPrdContent: JSON.stringify(prdSnapshot),
|
|
25069
25626
|
snapshotBugContent: JSON.stringify(bugSnapshot)
|
|
25070
25627
|
}).where(eq(prdVersions.id, current.id)).run();
|
|
25071
|
-
const
|
|
25628
|
+
const projectDir2 = join22(workspacePath, input.folderName);
|
|
25072
25629
|
for (const app of scan.apps) {
|
|
25073
25630
|
const appId = appIdByName.get(app.name);
|
|
25074
25631
|
const hasContextArtifacts = app.artifactCounts.erd + app.artifactCounts.data_model + app.artifactCounts.mockup + app.artifactCounts.technical_spec + app.artifactCounts.test_spec > 0;
|
|
@@ -25085,8 +25642,8 @@ function importProject(db, workspacePath, input, userId) {
|
|
|
25085
25642
|
const trackingPathFor = (type) => toPosix(
|
|
25086
25643
|
relative2(
|
|
25087
25644
|
workspacePath,
|
|
25088
|
-
|
|
25089
|
-
|
|
25645
|
+
join22(
|
|
25646
|
+
projectDir2,
|
|
25090
25647
|
app.folderRel,
|
|
25091
25648
|
type === "prepare" ? "context/prepare/PREPARE_MASTER.md" : type === "develop" ? "context/develop/IMPLEMENTATION_MASTER.md" : "context/bug/BUG_MASTER.md"
|
|
25092
25649
|
)
|
|
@@ -25311,7 +25868,7 @@ ${item.body}`.match(REQUIREMENT_ID)?.[0] ?? null;
|
|
|
25311
25868
|
}
|
|
25312
25869
|
|
|
25313
25870
|
// ../server/src/features/project-import/routes.ts
|
|
25314
|
-
function
|
|
25871
|
+
function errorResponse10(c, e) {
|
|
25315
25872
|
const err = e;
|
|
25316
25873
|
const status = err.status ?? 500;
|
|
25317
25874
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -25322,14 +25879,14 @@ function projectImportRoutes(db, workspacePath) {
|
|
|
25322
25879
|
try {
|
|
25323
25880
|
return c.json(listImportCandidates(db, workspacePath));
|
|
25324
25881
|
} catch (e) {
|
|
25325
|
-
return
|
|
25882
|
+
return errorResponse10(c, e);
|
|
25326
25883
|
}
|
|
25327
25884
|
});
|
|
25328
25885
|
app.post("/scan", requireRole("admin"), zValidator("json", scanImportRequestSchema), (c) => {
|
|
25329
25886
|
try {
|
|
25330
25887
|
return c.json(scanProject(db, workspacePath, c.req.valid("json").folderName));
|
|
25331
25888
|
} catch (e) {
|
|
25332
|
-
return
|
|
25889
|
+
return errorResponse10(c, e);
|
|
25333
25890
|
}
|
|
25334
25891
|
});
|
|
25335
25892
|
app.post("/", requireRole("admin"), zValidator("json", runImportRequestSchema), (c) => {
|
|
@@ -25337,14 +25894,14 @@ function projectImportRoutes(db, workspacePath) {
|
|
|
25337
25894
|
try {
|
|
25338
25895
|
return c.json(importProject(db, workspacePath, c.req.valid("json"), user.id), 201);
|
|
25339
25896
|
} catch (e) {
|
|
25340
|
-
return
|
|
25897
|
+
return errorResponse10(c, e);
|
|
25341
25898
|
}
|
|
25342
25899
|
});
|
|
25343
25900
|
return app;
|
|
25344
25901
|
}
|
|
25345
25902
|
|
|
25346
25903
|
// ../server/src/features/project/routes.ts
|
|
25347
|
-
function
|
|
25904
|
+
function errorResponse11(c, e) {
|
|
25348
25905
|
const err = e;
|
|
25349
25906
|
const status = err.status ?? 500;
|
|
25350
25907
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -25362,7 +25919,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25362
25919
|
const id = createProject(db, workspacePath, name, code, baseIdea, user.id);
|
|
25363
25920
|
return c.json({ id }, 201);
|
|
25364
25921
|
} catch (e) {
|
|
25365
|
-
return
|
|
25922
|
+
return errorResponse11(c, e);
|
|
25366
25923
|
}
|
|
25367
25924
|
});
|
|
25368
25925
|
app.get("/:id/detail", (c) => {
|
|
@@ -25371,7 +25928,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25371
25928
|
try {
|
|
25372
25929
|
return c.json(getProjectDetailSummary(db, id));
|
|
25373
25930
|
} catch (e) {
|
|
25374
|
-
return
|
|
25931
|
+
return errorResponse11(c, e);
|
|
25375
25932
|
}
|
|
25376
25933
|
});
|
|
25377
25934
|
app.post("/:id/scaffold", requireRole("admin"), (c) => {
|
|
@@ -25381,7 +25938,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25381
25938
|
scaffoldProject(db, workspacePath, id);
|
|
25382
25939
|
return c.json({ ok: true });
|
|
25383
25940
|
} catch (e) {
|
|
25384
|
-
return
|
|
25941
|
+
return errorResponse11(c, e);
|
|
25385
25942
|
}
|
|
25386
25943
|
});
|
|
25387
25944
|
app.get("/:id/brainstorm-md", (c) => {
|
|
@@ -25390,7 +25947,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25390
25947
|
try {
|
|
25391
25948
|
return c.json(getBrainstormMd(db, workspacePath, id));
|
|
25392
25949
|
} catch (e) {
|
|
25393
|
-
return
|
|
25950
|
+
return errorResponse11(c, e);
|
|
25394
25951
|
}
|
|
25395
25952
|
});
|
|
25396
25953
|
app.patch("/:id/brainstorm-md", zValidator("json", updateBrainstormMdSchema), (c) => {
|
|
@@ -25400,7 +25957,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25400
25957
|
updateBrainstormMd(db, workspacePath, id, c.req.valid("json").content);
|
|
25401
25958
|
return c.json({ ok: true });
|
|
25402
25959
|
} catch (e) {
|
|
25403
|
-
return
|
|
25960
|
+
return errorResponse11(c, e);
|
|
25404
25961
|
}
|
|
25405
25962
|
});
|
|
25406
25963
|
app.get("/:id", (c) => {
|
|
@@ -25409,7 +25966,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25409
25966
|
try {
|
|
25410
25967
|
return c.json(getProjectDetail(db, workspacePath, id));
|
|
25411
25968
|
} catch (e) {
|
|
25412
|
-
return
|
|
25969
|
+
return errorResponse11(c, e);
|
|
25413
25970
|
}
|
|
25414
25971
|
});
|
|
25415
25972
|
app.post("/:id/regenerate", (c) => {
|
|
@@ -25419,7 +25976,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25419
25976
|
regenerateCO2Files(db, workspacePath, id);
|
|
25420
25977
|
return c.json({ ok: true });
|
|
25421
25978
|
} catch (e) {
|
|
25422
|
-
return
|
|
25979
|
+
return errorResponse11(c, e);
|
|
25423
25980
|
}
|
|
25424
25981
|
});
|
|
25425
25982
|
app.post("/:id/rescan-claude-md", (c) => {
|
|
@@ -25429,7 +25986,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25429
25986
|
const imported = rescanClaudeMd(db, workspacePath, id);
|
|
25430
25987
|
return c.json({ ok: true, imported });
|
|
25431
25988
|
} catch (e) {
|
|
25432
|
-
return
|
|
25989
|
+
return errorResponse11(c, e);
|
|
25433
25990
|
}
|
|
25434
25991
|
});
|
|
25435
25992
|
app.patch("/:id/archive", requireRole("admin"), (c) => {
|
|
@@ -25440,15 +25997,29 @@ function projectRoutes(db, workspacePath) {
|
|
|
25440
25997
|
archiveProject(db, id, user.id);
|
|
25441
25998
|
return c.json({ ok: true });
|
|
25442
25999
|
} catch (e) {
|
|
25443
|
-
return
|
|
26000
|
+
return errorResponse11(c, e);
|
|
26001
|
+
}
|
|
26002
|
+
});
|
|
26003
|
+
app.patch("/:id", zValidator("json", updateProjectGitRemoteSchema), (c) => {
|
|
26004
|
+
const id = Number(c.req.param("id"));
|
|
26005
|
+
if (Number.isNaN(id)) return c.json({ code: "INVALID_ID" }, 400);
|
|
26006
|
+
try {
|
|
26007
|
+
const body = c.req.valid("json");
|
|
26008
|
+
updateProjectGitRemote(db, id, body.gitRemoteUrl, {
|
|
26009
|
+
defaultBranch: body.defaultBranch,
|
|
26010
|
+
gitProvider: body.gitProvider
|
|
26011
|
+
});
|
|
26012
|
+
return c.json({ ok: true });
|
|
26013
|
+
} catch (e) {
|
|
26014
|
+
return errorResponse11(c, e);
|
|
25444
26015
|
}
|
|
25445
26016
|
});
|
|
25446
26017
|
return app;
|
|
25447
26018
|
}
|
|
25448
26019
|
|
|
25449
26020
|
// ../server/src/features/shared-context/service.ts
|
|
25450
|
-
import { existsSync as
|
|
25451
|
-
import { join as
|
|
26021
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync10, readFileSync as readFileSync15, readdirSync as readdirSync4, statSync as statSync6, writeFileSync as writeFileSync12 } from "fs";
|
|
26022
|
+
import { join as join23, resolve as resolve3, sep as sep2 } from "path";
|
|
25452
26023
|
function svcError16(code, status) {
|
|
25453
26024
|
return Object.assign(new Error(code), { code, status });
|
|
25454
26025
|
}
|
|
@@ -25474,7 +26045,7 @@ function mimeForName(name) {
|
|
|
25474
26045
|
function resolveSharedRoot(db, workspacePath, projectId) {
|
|
25475
26046
|
const project = db.select().from(projects).where(eq(projects.id, projectId)).limit(1).get();
|
|
25476
26047
|
if (!project) throw svcError16("NOT_FOUND", 404);
|
|
25477
|
-
const root =
|
|
26048
|
+
const root = join23(workspacePath, project.workspaceFolderName, "shared_context");
|
|
25478
26049
|
mkdirSync10(root, { recursive: true });
|
|
25479
26050
|
return root;
|
|
25480
26051
|
}
|
|
@@ -25488,9 +26059,9 @@ function safeResolve(root, relPath) {
|
|
|
25488
26059
|
return full;
|
|
25489
26060
|
}
|
|
25490
26061
|
function sanitizeSegment(name) {
|
|
25491
|
-
const
|
|
25492
|
-
if (!
|
|
25493
|
-
return
|
|
26062
|
+
const safe2 = name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
26063
|
+
if (!safe2 || safe2 === "." || safe2 === "..") throw svcError16("INVALID_NAME", 400);
|
|
26064
|
+
return safe2;
|
|
25494
26065
|
}
|
|
25495
26066
|
function walk(dir, relBase) {
|
|
25496
26067
|
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
@@ -25502,10 +26073,10 @@ function walk(dir, relBase) {
|
|
|
25502
26073
|
name: entry.name,
|
|
25503
26074
|
path: rel,
|
|
25504
26075
|
type: "folder",
|
|
25505
|
-
children: walk(
|
|
26076
|
+
children: walk(join23(dir, entry.name), rel)
|
|
25506
26077
|
});
|
|
25507
26078
|
} else if (entry.isFile()) {
|
|
25508
|
-
const stat = statSync6(
|
|
26079
|
+
const stat = statSync6(join23(dir, entry.name));
|
|
25509
26080
|
nodes.push({
|
|
25510
26081
|
name: entry.name,
|
|
25511
26082
|
path: rel,
|
|
@@ -25536,28 +26107,28 @@ function uploadFile(db, workspacePath, projectId, folder, file) {
|
|
|
25536
26107
|
if (file.bytes.length > MAX_DOCUMENT_BYTES) throw svcError16("FILE_TOO_LARGE", 413);
|
|
25537
26108
|
const root = resolveSharedRoot(db, workspacePath, projectId);
|
|
25538
26109
|
const dir = folder ? safeResolve(root, folder) : root;
|
|
25539
|
-
if (folder && !
|
|
26110
|
+
if (folder && !existsSync20(dir)) throw svcError16("FOLDER_NOT_FOUND", 404);
|
|
25540
26111
|
const safeName = sanitizeSegment(file.name);
|
|
25541
|
-
|
|
26112
|
+
writeFileSync12(join23(dir, safeName), file.bytes);
|
|
25542
26113
|
return { path: folder ? `${folder}/${safeName}` : safeName };
|
|
25543
26114
|
}
|
|
25544
26115
|
function getFileContent(db, workspacePath, projectId, relPath) {
|
|
25545
26116
|
const root = resolveSharedRoot(db, workspacePath, projectId);
|
|
25546
26117
|
const full = safeResolve(root, relPath);
|
|
25547
|
-
if (!
|
|
26118
|
+
if (!existsSync20(full) || !statSync6(full).isFile()) throw svcError16("NOT_FOUND", 404);
|
|
25548
26119
|
const name = relPath.split("/").pop() ?? relPath;
|
|
25549
|
-
return { mimeType: mimeForName(name), bytes:
|
|
26120
|
+
return { mimeType: mimeForName(name), bytes: readFileSync15(full), name };
|
|
25550
26121
|
}
|
|
25551
26122
|
function saveMarkdown(db, workspacePath, projectId, relPath, content) {
|
|
25552
26123
|
if (mimeForName(relPath) !== "text/markdown") throw svcError16("NOT_MARKDOWN", 400);
|
|
25553
26124
|
const root = resolveSharedRoot(db, workspacePath, projectId);
|
|
25554
26125
|
const full = safeResolve(root, relPath);
|
|
25555
|
-
if (!
|
|
25556
|
-
|
|
26126
|
+
if (!existsSync20(full) || !statSync6(full).isFile()) throw svcError16("NOT_FOUND", 404);
|
|
26127
|
+
writeFileSync12(full, content, "utf8");
|
|
25557
26128
|
}
|
|
25558
26129
|
|
|
25559
26130
|
// ../server/src/features/shared-context/routes.ts
|
|
25560
|
-
function
|
|
26131
|
+
function errorResponse12(c, e) {
|
|
25561
26132
|
const err = e;
|
|
25562
26133
|
const status = err.status ?? 500;
|
|
25563
26134
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -25573,7 +26144,7 @@ function sharedContextRoutes(db, workspacePath) {
|
|
|
25573
26144
|
try {
|
|
25574
26145
|
return c.json({ tree: listTree(db, workspacePath, projectId) });
|
|
25575
26146
|
} catch (e) {
|
|
25576
|
-
return
|
|
26147
|
+
return errorResponse12(c, e);
|
|
25577
26148
|
}
|
|
25578
26149
|
});
|
|
25579
26150
|
app.post("/:projectId/folders", async (c) => {
|
|
@@ -25584,7 +26155,7 @@ function sharedContextRoutes(db, workspacePath) {
|
|
|
25584
26155
|
try {
|
|
25585
26156
|
return c.json({ path: createFolder(db, workspacePath, projectId, body.path) });
|
|
25586
26157
|
} catch (e) {
|
|
25587
|
-
return
|
|
26158
|
+
return errorResponse12(c, e);
|
|
25588
26159
|
}
|
|
25589
26160
|
});
|
|
25590
26161
|
app.post("/:projectId/upload", async (c) => {
|
|
@@ -25599,7 +26170,7 @@ function sharedContextRoutes(db, workspacePath) {
|
|
|
25599
26170
|
const { path } = uploadFile(db, workspacePath, projectId, folder, { name: file.name, bytes });
|
|
25600
26171
|
return c.json({ path });
|
|
25601
26172
|
} catch (e) {
|
|
25602
|
-
return
|
|
26173
|
+
return errorResponse12(c, e);
|
|
25603
26174
|
}
|
|
25604
26175
|
});
|
|
25605
26176
|
app.get("/:projectId/content", (c) => {
|
|
@@ -25618,7 +26189,7 @@ function sharedContextRoutes(db, workspacePath) {
|
|
|
25618
26189
|
}
|
|
25619
26190
|
});
|
|
25620
26191
|
} catch (e) {
|
|
25621
|
-
return
|
|
26192
|
+
return errorResponse12(c, e);
|
|
25622
26193
|
}
|
|
25623
26194
|
});
|
|
25624
26195
|
app.put("/:projectId/content", async (c) => {
|
|
@@ -25632,7 +26203,7 @@ function sharedContextRoutes(db, workspacePath) {
|
|
|
25632
26203
|
saveMarkdown(db, workspacePath, projectId, body.path, body.content);
|
|
25633
26204
|
return c.json({ ok: true });
|
|
25634
26205
|
} catch (e) {
|
|
25635
|
-
return
|
|
26206
|
+
return errorResponse12(c, e);
|
|
25636
26207
|
}
|
|
25637
26208
|
});
|
|
25638
26209
|
return app;
|
|
@@ -25697,8 +26268,8 @@ function svcError17(code, status) {
|
|
|
25697
26268
|
return Object.assign(new Error(code), { code, status });
|
|
25698
26269
|
}
|
|
25699
26270
|
function toSafe(user) {
|
|
25700
|
-
const { passwordHash: _omit, ...
|
|
25701
|
-
return
|
|
26271
|
+
const { passwordHash: _omit, ...safe2 } = user;
|
|
26272
|
+
return safe2;
|
|
25702
26273
|
}
|
|
25703
26274
|
function loginUser(db, input, clientIp) {
|
|
25704
26275
|
const rateLimitKey = `${input.username}:${clientIp}`;
|
|
@@ -25775,8 +26346,8 @@ function getUserDetail(db, id) {
|
|
|
25775
26346
|
const admin = db.select({ displayName: users.displayName }).from(users).where(eq(users.id, user.reactivatedBy)).limit(1).get();
|
|
25776
26347
|
reactivatedByName = admin?.displayName ?? null;
|
|
25777
26348
|
}
|
|
25778
|
-
const { passwordHash: _omit, ...
|
|
25779
|
-
return { ...
|
|
26349
|
+
const { passwordHash: _omit, ...safe2 } = user;
|
|
26350
|
+
return { ...safe2, reactivatedByName };
|
|
25780
26351
|
}
|
|
25781
26352
|
function adminUpdateUser(db, targetId, input) {
|
|
25782
26353
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -25837,7 +26408,7 @@ function listAssignments(db, userId) {
|
|
|
25837
26408
|
|
|
25838
26409
|
// ../server/src/features/user/auth-routes.ts
|
|
25839
26410
|
var SESSION_MAX_AGE = 30 * 24 * 60 * 60;
|
|
25840
|
-
function
|
|
26411
|
+
function errorResponse13(c, e) {
|
|
25841
26412
|
const err = e;
|
|
25842
26413
|
const status = err.status ?? 500;
|
|
25843
26414
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -25858,7 +26429,7 @@ function authRoutes(db) {
|
|
|
25858
26429
|
});
|
|
25859
26430
|
return c.json({ mustChangePassword });
|
|
25860
26431
|
} catch (e) {
|
|
25861
|
-
return
|
|
26432
|
+
return errorResponse13(c, e);
|
|
25862
26433
|
}
|
|
25863
26434
|
});
|
|
25864
26435
|
app.post("/logout", (c) => {
|
|
@@ -25882,14 +26453,14 @@ function authRoutes(db) {
|
|
|
25882
26453
|
});
|
|
25883
26454
|
return c.json({ ok: true });
|
|
25884
26455
|
} catch (e) {
|
|
25885
|
-
return
|
|
26456
|
+
return errorResponse13(c, e);
|
|
25886
26457
|
}
|
|
25887
26458
|
});
|
|
25888
26459
|
app.get("/me", (c) => {
|
|
25889
26460
|
const user = c.get("user");
|
|
25890
26461
|
if (!user) return c.json({ code: "UNAUTHORIZED" }, 401);
|
|
25891
|
-
const { passwordHash: _omit, ...
|
|
25892
|
-
return c.json(
|
|
26462
|
+
const { passwordHash: _omit, ...safe2 } = user;
|
|
26463
|
+
return c.json(safe2);
|
|
25893
26464
|
});
|
|
25894
26465
|
app.patch("/me", zValidator("json", updateProfileSchema), (c) => {
|
|
25895
26466
|
const user = c.get("user");
|
|
@@ -25901,7 +26472,7 @@ function authRoutes(db) {
|
|
|
25901
26472
|
}
|
|
25902
26473
|
|
|
25903
26474
|
// ../server/src/features/user/routes.ts
|
|
25904
|
-
function
|
|
26475
|
+
function errorResponse14(c, e) {
|
|
25905
26476
|
const err = e;
|
|
25906
26477
|
const status = err.status ?? 500;
|
|
25907
26478
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -25915,7 +26486,7 @@ function userRoutes(db) {
|
|
|
25915
26486
|
const id = createUser(db, c.req.valid("json"));
|
|
25916
26487
|
return c.json({ id }, 201);
|
|
25917
26488
|
} catch (e) {
|
|
25918
|
-
return
|
|
26489
|
+
return errorResponse14(c, e);
|
|
25919
26490
|
}
|
|
25920
26491
|
});
|
|
25921
26492
|
app.get("/:id", requireRole("admin"), (c) => {
|
|
@@ -25939,7 +26510,7 @@ function userRoutes(db) {
|
|
|
25939
26510
|
deactivateUser(db, id, admin.id);
|
|
25940
26511
|
return c.json({ ok: true });
|
|
25941
26512
|
} catch (e) {
|
|
25942
|
-
return
|
|
26513
|
+
return errorResponse14(c, e);
|
|
25943
26514
|
}
|
|
25944
26515
|
});
|
|
25945
26516
|
app.patch(
|
|
@@ -25961,7 +26532,7 @@ function userRoutes(db) {
|
|
|
25961
26532
|
reactivateUser(db, id, admin.id);
|
|
25962
26533
|
return c.json({ ok: true });
|
|
25963
26534
|
} catch (e) {
|
|
25964
|
-
return
|
|
26535
|
+
return errorResponse14(c, e);
|
|
25965
26536
|
}
|
|
25966
26537
|
});
|
|
25967
26538
|
app.get("/:id/project-assignments", requireRole("admin"), (c) => {
|
|
@@ -25981,7 +26552,7 @@ function userRoutes(db) {
|
|
|
25981
26552
|
const assignmentId = assignProject(db, id, c.req.valid("json").projectId, admin.id);
|
|
25982
26553
|
return c.json({ id: assignmentId }, 201);
|
|
25983
26554
|
} catch (e) {
|
|
25984
|
-
return
|
|
26555
|
+
return errorResponse14(c, e);
|
|
25985
26556
|
}
|
|
25986
26557
|
}
|
|
25987
26558
|
);
|
|
@@ -25996,7 +26567,7 @@ function userRoutes(db) {
|
|
|
25996
26567
|
}
|
|
25997
26568
|
|
|
25998
26569
|
// ../server/src/features/version/routes.ts
|
|
25999
|
-
function
|
|
26570
|
+
function errorResponse15(c, e) {
|
|
26000
26571
|
const err = e;
|
|
26001
26572
|
const status = err.status ?? 500;
|
|
26002
26573
|
const message = e instanceof Error && err.code ? e.message : void 0;
|
|
@@ -26011,7 +26582,7 @@ function versionRoutes(db, workspacePath) {
|
|
|
26011
26582
|
try {
|
|
26012
26583
|
return c.json(listVersions(db, projectId, user.id));
|
|
26013
26584
|
} catch (e) {
|
|
26014
|
-
return
|
|
26585
|
+
return errorResponse15(c, e);
|
|
26015
26586
|
}
|
|
26016
26587
|
});
|
|
26017
26588
|
app.get("/:projectId/current", (c) => {
|
|
@@ -26021,7 +26592,7 @@ function versionRoutes(db, workspacePath) {
|
|
|
26021
26592
|
try {
|
|
26022
26593
|
return c.json(getCurrentVersion(db, projectId, user.id));
|
|
26023
26594
|
} catch (e) {
|
|
26024
|
-
return
|
|
26595
|
+
return errorResponse15(c, e);
|
|
26025
26596
|
}
|
|
26026
26597
|
});
|
|
26027
26598
|
app.get("/:projectId/can-add", (c) => {
|
|
@@ -26037,14 +26608,14 @@ function versionRoutes(db, workspacePath) {
|
|
|
26037
26608
|
const result = addVersion(db, projectId, c.req.valid("json").versionType, user.id, workspacePath);
|
|
26038
26609
|
return c.json(result);
|
|
26039
26610
|
} catch (e) {
|
|
26040
|
-
return
|
|
26611
|
+
return errorResponse15(c, e);
|
|
26041
26612
|
}
|
|
26042
26613
|
});
|
|
26043
26614
|
return app;
|
|
26044
26615
|
}
|
|
26045
26616
|
|
|
26046
26617
|
// ../server/src/features/workflow/routes.ts
|
|
26047
|
-
function
|
|
26618
|
+
function errorResponse16(c, e) {
|
|
26048
26619
|
const err = e;
|
|
26049
26620
|
const status = err.status ?? 500;
|
|
26050
26621
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -26056,7 +26627,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26056
26627
|
try {
|
|
26057
26628
|
return c.json(getPendingApps(db, projectId, conductorType, versionId));
|
|
26058
26629
|
} catch (e) {
|
|
26059
|
-
return
|
|
26630
|
+
return errorResponse16(c, e);
|
|
26060
26631
|
}
|
|
26061
26632
|
});
|
|
26062
26633
|
app.get("/phase-completion", zValidator("query", pendingAppsQuerySchema), (c) => {
|
|
@@ -26064,7 +26635,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26064
26635
|
try {
|
|
26065
26636
|
return c.json(getPhaseCompletion(db, projectId, conductorType, versionId));
|
|
26066
26637
|
} catch (e) {
|
|
26067
|
-
return
|
|
26638
|
+
return errorResponse16(c, e);
|
|
26068
26639
|
}
|
|
26069
26640
|
});
|
|
26070
26641
|
app.get("/runs", (c) => {
|
|
@@ -26075,7 +26646,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26075
26646
|
resumeWatchers(db, workspacePath, projectId);
|
|
26076
26647
|
return c.json(listWorkflowRuns(db, projectId));
|
|
26077
26648
|
} catch (e) {
|
|
26078
|
-
return
|
|
26649
|
+
return errorResponse16(c, e);
|
|
26079
26650
|
}
|
|
26080
26651
|
});
|
|
26081
26652
|
app.get("/active-run", (c) => {
|
|
@@ -26085,7 +26656,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26085
26656
|
resumeWatchers(db, workspacePath, projectId);
|
|
26086
26657
|
return c.json(getActiveRun(db, projectId, workspacePath));
|
|
26087
26658
|
} catch (e) {
|
|
26088
|
-
return
|
|
26659
|
+
return errorResponse16(c, e);
|
|
26089
26660
|
}
|
|
26090
26661
|
});
|
|
26091
26662
|
app.get("/runs/:id", (c) => {
|
|
@@ -26094,7 +26665,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26094
26665
|
try {
|
|
26095
26666
|
return c.json(getWorkflowRun(db, id, workspacePath));
|
|
26096
26667
|
} catch (e) {
|
|
26097
|
-
return
|
|
26668
|
+
return errorResponse16(c, e);
|
|
26098
26669
|
}
|
|
26099
26670
|
});
|
|
26100
26671
|
app.get("/runs/:id/file-content", (c) => {
|
|
@@ -26103,7 +26674,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26103
26674
|
try {
|
|
26104
26675
|
return c.json(getWorkflowRunFileContent(db, workspacePath, id));
|
|
26105
26676
|
} catch (e) {
|
|
26106
|
-
return
|
|
26677
|
+
return errorResponse16(c, e);
|
|
26107
26678
|
}
|
|
26108
26679
|
});
|
|
26109
26680
|
app.get("/runs/:id/changelog", (c) => {
|
|
@@ -26114,7 +26685,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26114
26685
|
try {
|
|
26115
26686
|
return c.json(getRunApplicationChangelog(db, workspacePath, id, appName));
|
|
26116
26687
|
} catch (e) {
|
|
26117
|
-
return
|
|
26688
|
+
return errorResponse16(c, e);
|
|
26118
26689
|
}
|
|
26119
26690
|
});
|
|
26120
26691
|
app.post("/runs/:id/force-stop", (c) => {
|
|
@@ -26124,7 +26695,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26124
26695
|
forceStopWorkflowRun(db, id);
|
|
26125
26696
|
return c.json({ ok: true });
|
|
26126
26697
|
} catch (e) {
|
|
26127
|
-
return
|
|
26698
|
+
return errorResponse16(c, e);
|
|
26128
26699
|
}
|
|
26129
26700
|
});
|
|
26130
26701
|
app.post("/runs/:id/rescan", (c) => {
|
|
@@ -26133,7 +26704,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26133
26704
|
try {
|
|
26134
26705
|
return c.json(rescanWorkflowRun(db, workspacePath, id));
|
|
26135
26706
|
} catch (e) {
|
|
26136
|
-
return
|
|
26707
|
+
return errorResponse16(c, e);
|
|
26137
26708
|
}
|
|
26138
26709
|
});
|
|
26139
26710
|
app.post("/runs", zValidator("json", createWorkflowSchema), (c) => {
|
|
@@ -26145,7 +26716,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26145
26716
|
});
|
|
26146
26717
|
return c.json(result, 201);
|
|
26147
26718
|
} catch (e) {
|
|
26148
|
-
return
|
|
26719
|
+
return errorResponse16(c, e);
|
|
26149
26720
|
}
|
|
26150
26721
|
});
|
|
26151
26722
|
return app;
|
|
@@ -26285,11 +26856,11 @@ function closeTerminalSessions(projectId, reason) {
|
|
|
26285
26856
|
}
|
|
26286
26857
|
function buildPtyLaunch(workspacePath, folderName) {
|
|
26287
26858
|
const isWindows = process.platform === "win32";
|
|
26288
|
-
const
|
|
26859
|
+
const join30 = (a, b) => `${a.replace(/[\\/]+$/, "")}/${b}`;
|
|
26289
26860
|
return {
|
|
26290
26861
|
shell: isWindows ? "powershell.exe" : process.env["SHELL"] ?? "bash",
|
|
26291
26862
|
args: [],
|
|
26292
|
-
cwd:
|
|
26863
|
+
cwd: join30(workspacePath, folderName),
|
|
26293
26864
|
autoCommand: `${CLAUDE_COMMAND}\r`
|
|
26294
26865
|
};
|
|
26295
26866
|
}
|
|
@@ -26466,16 +27037,16 @@ function terminalRoutes(db) {
|
|
|
26466
27037
|
import { execSync } from "child_process";
|
|
26467
27038
|
|
|
26468
27039
|
// ../server/src/lib/claude-plugin.ts
|
|
26469
|
-
import { existsSync as
|
|
27040
|
+
import { existsSync as existsSync21, readFileSync as readFileSync16, readdirSync as readdirSync5 } from "fs";
|
|
26470
27041
|
import { homedir } from "os";
|
|
26471
|
-
import { join as
|
|
27042
|
+
import { join as join24 } from "path";
|
|
26472
27043
|
var PLUGIN_NAME = "co2-skills";
|
|
26473
|
-
function detectCo2SkillsPlugin(claudeDir =
|
|
27044
|
+
function detectCo2SkillsPlugin(claudeDir = join24(homedir(), ".claude")) {
|
|
26474
27045
|
return readRegistry(claudeDir) ?? readCacheDir(claudeDir);
|
|
26475
27046
|
}
|
|
26476
27047
|
function readRegistry(claudeDir) {
|
|
26477
27048
|
try {
|
|
26478
|
-
const raw2 =
|
|
27049
|
+
const raw2 = readFileSync16(join24(claudeDir, "plugins", "installed_plugins.json"), "utf8");
|
|
26479
27050
|
const json = JSON.parse(raw2);
|
|
26480
27051
|
for (const [key, entries] of Object.entries(json.plugins ?? {})) {
|
|
26481
27052
|
if (!key.startsWith(`${PLUGIN_NAME}@`)) continue;
|
|
@@ -26488,8 +27059,8 @@ function readRegistry(claudeDir) {
|
|
|
26488
27059
|
}
|
|
26489
27060
|
}
|
|
26490
27061
|
function readCacheDir(claudeDir) {
|
|
26491
|
-
const base =
|
|
26492
|
-
if (!
|
|
27062
|
+
const base = join24(claudeDir, "plugins", "cache", "co2", PLUGIN_NAME);
|
|
27063
|
+
if (!existsSync21(base)) return { installed: false };
|
|
26493
27064
|
try {
|
|
26494
27065
|
const versions = readdirSync5(base).filter((d) => /^\d+\.\d+/.test(d)).sort();
|
|
26495
27066
|
return { installed: true, version: versions.at(-1) };
|
|
@@ -26543,8 +27114,8 @@ function workspaceRoutes(_db, config, port, host, workspacePath) {
|
|
|
26543
27114
|
}
|
|
26544
27115
|
|
|
26545
27116
|
// ../server/src/lib/config.ts
|
|
26546
|
-
import { existsSync as
|
|
26547
|
-
import { join as
|
|
27117
|
+
import { existsSync as existsSync22, readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "fs";
|
|
27118
|
+
import { join as join25 } from "path";
|
|
26548
27119
|
var configSchema = external_exports.object({
|
|
26549
27120
|
port: external_exports.number().int().min(1).max(65535).optional(),
|
|
26550
27121
|
host: external_exports.string().optional(),
|
|
@@ -26552,16 +27123,16 @@ var configSchema = external_exports.object({
|
|
|
26552
27123
|
provisioningState: external_exports.enum(["pending", "complete"]).optional()
|
|
26553
27124
|
});
|
|
26554
27125
|
function loadConfig(dataDir) {
|
|
26555
|
-
const file =
|
|
26556
|
-
if (!
|
|
27126
|
+
const file = join25(dataDir, "config.json");
|
|
27127
|
+
if (!existsSync22(file)) return {};
|
|
26557
27128
|
try {
|
|
26558
|
-
return configSchema.parse(JSON.parse(
|
|
27129
|
+
return configSchema.parse(JSON.parse(readFileSync17(file, "utf8")));
|
|
26559
27130
|
} catch {
|
|
26560
27131
|
return {};
|
|
26561
27132
|
}
|
|
26562
27133
|
}
|
|
26563
27134
|
function saveConfig(dataDir, config) {
|
|
26564
|
-
|
|
27135
|
+
writeFileSync13(join25(dataDir, "config.json"), JSON.stringify(config, null, 2));
|
|
26565
27136
|
}
|
|
26566
27137
|
|
|
26567
27138
|
// ../server/src/lib/logger.ts
|
|
@@ -26589,8 +27160,8 @@ function loggerMiddleware() {
|
|
|
26589
27160
|
}
|
|
26590
27161
|
|
|
26591
27162
|
// ../server/src/lib/static.ts
|
|
26592
|
-
import { existsSync as
|
|
26593
|
-
import { extname, join as
|
|
27163
|
+
import { existsSync as existsSync23, readFileSync as readFileSync18, statSync as statSync7 } from "fs";
|
|
27164
|
+
import { extname, join as join26, normalize, resolve as resolve4, sep as sep3 } from "path";
|
|
26594
27165
|
var MIME = {
|
|
26595
27166
|
".html": "text/html; charset=utf-8",
|
|
26596
27167
|
".js": "text/javascript; charset=utf-8",
|
|
@@ -26614,7 +27185,7 @@ function safeResolve2(rootDir, relPath) {
|
|
|
26614
27185
|
return target;
|
|
26615
27186
|
}
|
|
26616
27187
|
function serveSpa(staticDir) {
|
|
26617
|
-
const indexPath =
|
|
27188
|
+
const indexPath = join26(staticDir, "index.html");
|
|
26618
27189
|
return async (c, next) => {
|
|
26619
27190
|
if (c.req.method !== "GET" && c.req.method !== "HEAD") {
|
|
26620
27191
|
await next();
|
|
@@ -26627,14 +27198,14 @@ function serveSpa(staticDir) {
|
|
|
26627
27198
|
}
|
|
26628
27199
|
const rel = path === "/" ? "/index.html" : decodeURIComponent(path);
|
|
26629
27200
|
const filePath = safeResolve2(staticDir, rel);
|
|
26630
|
-
if (filePath &&
|
|
26631
|
-
const body =
|
|
27201
|
+
if (filePath && existsSync23(filePath) && statSync7(filePath).isFile()) {
|
|
27202
|
+
const body = readFileSync18(filePath);
|
|
26632
27203
|
return c.body(body, 200, {
|
|
26633
27204
|
"Content-Type": MIME[extname(filePath)] ?? "application/octet-stream"
|
|
26634
27205
|
});
|
|
26635
27206
|
}
|
|
26636
|
-
if (
|
|
26637
|
-
return c.body(
|
|
27207
|
+
if (existsSync23(indexPath)) {
|
|
27208
|
+
return c.body(readFileSync18(indexPath), 200, { "Content-Type": "text/html; charset=utf-8" });
|
|
26638
27209
|
}
|
|
26639
27210
|
await next();
|
|
26640
27211
|
};
|
|
@@ -26678,6 +27249,7 @@ function createApp(db, opts) {
|
|
|
26678
27249
|
api.route("/defects", defects);
|
|
26679
27250
|
api.route("/quality", defects);
|
|
26680
27251
|
api.route("/dashboard", dashboardRoutes(db));
|
|
27252
|
+
api.route("/git", gitRoutes(db, opts.workspacePath));
|
|
26681
27253
|
app.route("/api", api);
|
|
26682
27254
|
if (opts.staticDir) {
|
|
26683
27255
|
app.use("*", serveSpa(opts.staticDir));
|
|
@@ -26686,8 +27258,8 @@ function createApp(db, opts) {
|
|
|
26686
27258
|
}
|
|
26687
27259
|
|
|
26688
27260
|
// ../server/src/db/index.ts
|
|
26689
|
-
import { existsSync as
|
|
26690
|
-
import { dirname as dirname2, join as
|
|
27261
|
+
import { existsSync as existsSync24 } from "fs";
|
|
27262
|
+
import { dirname as dirname2, join as join27 } from "path";
|
|
26691
27263
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
26692
27264
|
import Database from "better-sqlite3";
|
|
26693
27265
|
|
|
@@ -26891,16 +27463,16 @@ function resolveMigrationsFolder() {
|
|
|
26891
27463
|
const here = dirname2(fileURLToPath2(import.meta.url));
|
|
26892
27464
|
const candidates = [
|
|
26893
27465
|
process.env["CO2_CLI_MIGRATIONS_DIR"],
|
|
26894
|
-
|
|
27466
|
+
join27(here, "../../drizzle"),
|
|
26895
27467
|
// source: src/db -> packages/server/drizzle
|
|
26896
|
-
|
|
27468
|
+
join27(here, "../drizzle"),
|
|
26897
27469
|
// bundled: cli/dist -> cli/drizzle
|
|
26898
|
-
|
|
27470
|
+
join27(here, "drizzle")
|
|
26899
27471
|
].filter((c) => Boolean(c));
|
|
26900
27472
|
for (const c of candidates) {
|
|
26901
|
-
if (
|
|
27473
|
+
if (existsSync24(join27(c, "meta", "_journal.json"))) return c;
|
|
26902
27474
|
}
|
|
26903
|
-
return
|
|
27475
|
+
return join27(here, "../../drizzle");
|
|
26904
27476
|
}
|
|
26905
27477
|
function openDb(dbPath, migrationsFolder = resolveMigrationsFolder()) {
|
|
26906
27478
|
const sqlite = new Database(dbPath);
|
|
@@ -26934,7 +27506,7 @@ function bootstrap(db) {
|
|
|
26934
27506
|
// ../server/src/lib/data-dir.ts
|
|
26935
27507
|
import { mkdirSync as mkdirSync11 } from "fs";
|
|
26936
27508
|
import { homedir as homedir2 } from "os";
|
|
26937
|
-
import { join as
|
|
27509
|
+
import { join as join28 } from "path";
|
|
26938
27510
|
function resolveDataDir(override) {
|
|
26939
27511
|
const dir = override ?? defaultDataDir();
|
|
26940
27512
|
mkdirSync11(dir, { recursive: true, mode: 448 });
|
|
@@ -26943,11 +27515,11 @@ function resolveDataDir(override) {
|
|
|
26943
27515
|
function defaultDataDir() {
|
|
26944
27516
|
switch (process.platform) {
|
|
26945
27517
|
case "darwin":
|
|
26946
|
-
return
|
|
27518
|
+
return join28(homedir2(), "Library", "Application Support", "co2-cli");
|
|
26947
27519
|
case "win32":
|
|
26948
|
-
return
|
|
27520
|
+
return join28(process.env["LOCALAPPDATA"] ?? join28(homedir2(), "AppData", "Local"), "co2-cli");
|
|
26949
27521
|
default:
|
|
26950
|
-
return
|
|
27522
|
+
return join28(process.env["XDG_DATA_HOME"] ?? join28(homedir2(), ".local", "share"), "co2-cli");
|
|
26951
27523
|
}
|
|
26952
27524
|
}
|
|
26953
27525
|
|
|
@@ -27601,8 +28173,8 @@ var startOptionsSchema = external_exports.object({
|
|
|
27601
28173
|
workspacePath: external_exports.string().optional()
|
|
27602
28174
|
});
|
|
27603
28175
|
function resolveStaticDir() {
|
|
27604
|
-
const dir =
|
|
27605
|
-
return
|
|
28176
|
+
const dir = join29(dirname3(fileURLToPath3(import.meta.url)), "../static");
|
|
28177
|
+
return existsSync25(join29(dir, "index.html")) ? dir : void 0;
|
|
27606
28178
|
}
|
|
27607
28179
|
function registerStart(program3, version2) {
|
|
27608
28180
|
program3.command("start").description("Start the co2-cli web application (foreground)").option("-p, --port <n>", "Listen port (default: 3001)").option("-H, --host <addr>", "Listen host (default: 0.0.0.0 for LAN access)").option("-d, --data-dir <path>", "Override the data directory").option("-w, --workspace-path <path>", "Override the Workspace root folder").action(async (rawOpts) => {
|
|
@@ -27611,11 +28183,11 @@ function registerStart(program3, version2) {
|
|
|
27611
28183
|
const config = loadConfig(dataDir);
|
|
27612
28184
|
const port = opts.port ?? (process.env["APP_PORT"] ? Number(process.env["APP_PORT"]) : void 0) ?? config.port ?? 3001;
|
|
27613
28185
|
const host = opts.host ?? process.env["APP_HOST"] ?? config.host ?? "0.0.0.0";
|
|
27614
|
-
const workspacePath = opts.workspacePath ?? process.env["APP_WORKSPACE_PATH"] ?? config.workspacePath ??
|
|
28186
|
+
const workspacePath = opts.workspacePath ?? process.env["APP_WORKSPACE_PATH"] ?? config.workspacePath ?? join29(process.env["HOME"] ?? process.env["USERPROFILE"] ?? ".", "workspace");
|
|
27615
28187
|
if (opts.port !== void 0 || opts.workspacePath !== void 0) {
|
|
27616
28188
|
saveConfig(dataDir, { ...config, port, workspacePath });
|
|
27617
28189
|
}
|
|
27618
|
-
const db = openDb(
|
|
28190
|
+
const db = openDb(join29(dataDir, "app.db"));
|
|
27619
28191
|
const credentials = bootstrap(db);
|
|
27620
28192
|
const app = createApp(db, {
|
|
27621
28193
|
dataDir,
|