@rashidee/co2 1.3.4 → 1.3.6
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 +916 -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 +41 -9
- package/plugin/skills/util-gencicdscript/references/cicd-app-template.md +210 -21
- package/plugin/skills/util-plancicd/SKILL.md +88 -5
- package/static/assets/{abnfDiagram-VRR7QNED-BwxjNDj8.js → abnfDiagram-VRR7QNED-CzKpxD9q.js} +1 -1
- package/static/assets/{arc-Bf00Zylw.js → arc-0jj0p8Qe.js} +1 -1
- package/static/assets/{architectureDiagram-ZJ3FMSHR-CRUg8Wt3.js → architectureDiagram-ZJ3FMSHR-DXUL6S5t.js} +1 -1
- package/static/assets/{blockDiagram-677ZJIJ3-Dy-JP_0N.js → blockDiagram-677ZJIJ3-BBjsQESd.js} +1 -1
- package/static/assets/{c4Diagram-LMCZKHZV-obM4PBxI.js → c4Diagram-LMCZKHZV-CTsLC95S.js} +1 -1
- package/static/assets/channel-1MKDXK8w.js +1 -0
- package/static/assets/{chunk-2Q5K7J3B-DoXzquQ1.js → chunk-2Q5K7J3B-BpisBKYx.js} +1 -1
- package/static/assets/{chunk-32BRIVSS-B6VgB7K8.js → chunk-32BRIVSS-DAfsr4ph.js} +1 -1
- package/static/assets/{chunk-5VM5RSS4-BmZS0Ftn.js → chunk-5VM5RSS4-Z3FtY-B6.js} +1 -1
- package/static/assets/{chunk-EX3LRPZG-DdOLrDUP.js → chunk-EX3LRPZG-FI9smQaN.js} +1 -1
- package/static/assets/{chunk-JWPE2WC7-RVdgT8k5.js → chunk-JWPE2WC7-sOygQj_B.js} +1 -1
- package/static/assets/{chunk-MOJQB5TN-BbMHSSXk.js → chunk-MOJQB5TN-DCSpai7w.js} +1 -1
- package/static/assets/{chunk-RYQCIY6F-CBOyejV-.js → chunk-RYQCIY6F-CchqtfnI.js} +1 -1
- package/static/assets/{chunk-V7JOEXUC-DVvl5ZDu.js → chunk-V7JOEXUC-DaOzsfx7.js} +1 -1
- package/static/assets/{chunk-VR4S4FIN-TKs-_Nin.js → chunk-VR4S4FIN-DmxuoVTy.js} +1 -1
- package/static/assets/{chunk-XXDRQBXY-BuwUqVAV.js → chunk-XXDRQBXY-ChERxfri.js} +1 -1
- package/static/assets/classDiagram-OUVF2IWQ-bADaUwoJ.js +1 -0
- package/static/assets/classDiagram-v2-EOCWNBFH-bADaUwoJ.js +1 -0
- package/static/assets/{cose-bilkent-JH36ORCC-bVLOVjVk.js → cose-bilkent-JH36ORCC-CO7_eF6b.js} +1 -1
- package/static/assets/{cynefin-VYW2F7L2-B7HPqh-Z.js → cynefin-VYW2F7L2-CzQkWtdY.js} +1 -1
- package/static/assets/{cynefinDiagram-TSTJHNR4-0d2yxXQG.js → cynefinDiagram-TSTJHNR4-BN2o8rFa.js} +1 -1
- package/static/assets/{dagre-VKFMJZFB-D7ErrCay.js → dagre-VKFMJZFB-GLjBE-Xs.js} +1 -1
- package/static/assets/{diagram-FQU43EPY-C36bU8jh.js → diagram-FQU43EPY-Cyv7saju.js} +1 -1
- package/static/assets/{diagram-G47NLZAW-BzaLh10R.js → diagram-G47NLZAW-B9PmZJ3U.js} +1 -1
- package/static/assets/{diagram-NH7WQ7WH-v-Zsf5gF.js → diagram-NH7WQ7WH-ByKD8yJU.js} +1 -1
- package/static/assets/{diagram-OA4YK3LP-CRbJBq_k.js → diagram-OA4YK3LP-BvbW7fIS.js} +1 -1
- package/static/assets/{diagram-WEI45ONY-8fV5Plo4.js → diagram-WEI45ONY-BgXZ9SJ8.js} +1 -1
- package/static/assets/{ebnfDiagram-CCIWWBDH-5gIe63LE.js → ebnfDiagram-CCIWWBDH-CXEEJ26B.js} +1 -1
- package/static/assets/{erDiagram-Q63AITRT-C3IcAHjG.js → erDiagram-Q63AITRT-CcHs_S16.js} +1 -1
- package/static/assets/{flowDiagram-23GEKE2U-KQjlOZK9.js → flowDiagram-23GEKE2U-Cwg3I3GK.js} +1 -1
- package/static/assets/{ganttDiagram-NO4QXBWP-Dq3c2GZf.js → ganttDiagram-NO4QXBWP-ky6w5Lvy.js} +1 -1
- package/static/assets/{gitGraphDiagram-IHSO6WYX-BYtnaU27.js → gitGraphDiagram-IHSO6WYX-DN0eRQ7r.js} +1 -1
- package/static/assets/{index-CgNepcKp.js → index-CCC4-Ibh.js} +174 -172
- package/static/assets/{index-Dn_JY-18.css → index-Cz5Fcq3G.css} +1 -1
- package/static/assets/{infoDiagram-FWYZ7A6U-CxqBig2C.js → infoDiagram-FWYZ7A6U-COojw6OC.js} +1 -1
- package/static/assets/{ishikawaDiagram-FXEZZL3T-0HfzC1wj.js → ishikawaDiagram-FXEZZL3T-BR5w5ZqM.js} +1 -1
- package/static/assets/{journeyDiagram-5HDEW3XC-umkaBeJl.js → journeyDiagram-5HDEW3XC-B5WtTEBM.js} +1 -1
- package/static/assets/{kanban-definition-HUTT4EX6-DsBos3y7.js → kanban-definition-HUTT4EX6-DnQ_zg4E.js} +1 -1
- package/static/assets/{linear-DtNdgaOB.js → linear-C5kGzenz.js} +1 -1
- package/static/assets/{mindmap-definition-LN4V7U3C-BThlwYQu.js → mindmap-definition-LN4V7U3C-BMksxBso.js} +1 -1
- package/static/assets/{pegDiagram-2B236MQR-DoxrNvw3.js → pegDiagram-2B236MQR-BKrF97iU.js} +1 -1
- package/static/assets/{pieDiagram-ENE6RG2P-BqCJUG4Q.js → pieDiagram-ENE6RG2P-DZAQkZwC.js} +1 -1
- package/static/assets/{quadrantDiagram-ABIIQ3AL-4GVp_BSP.js → quadrantDiagram-ABIIQ3AL-Dx0-5odM.js} +1 -1
- package/static/assets/{railroadDiagram-RFXS5EU6-CU341ElP.js → railroadDiagram-RFXS5EU6-BbY-CxL_.js} +1 -1
- package/static/assets/{requirementDiagram-TGXJPOKE-DoBSr-cX.js → requirementDiagram-TGXJPOKE-ItSYgvcN.js} +1 -1
- package/static/assets/{sankeyDiagram-HTMAVEWB-CJ__IqT6.js → sankeyDiagram-HTMAVEWB-CjIuRRYE.js} +1 -1
- package/static/assets/{sequenceDiagram-DBY2YBRQ-BkNwP7Io.js → sequenceDiagram-DBY2YBRQ-B277PT2z.js} +1 -1
- package/static/assets/{sizeCapture-X5ZJPWSS-B-2Xu5n9.js → sizeCapture-X5ZJPWSS-BYc46yWE.js} +1 -1
- package/static/assets/{stateDiagram-2N3HPSRC-DPbpX5_5.js → stateDiagram-2N3HPSRC-Cq41gYoq.js} +1 -1
- package/static/assets/stateDiagram-v2-6OUMAXLB-B9V3_HpU.js +1 -0
- package/static/assets/{swimlanes-5IMT3BWC-CnBZNoN8.js → swimlanes-5IMT3BWC-DMxA2jQL.js} +2 -2
- package/static/assets/swimlanesDiagram-G3AALYLV-qx8IX4cU.js +8 -0
- package/static/assets/{timeline-definition-FHXFAJF6-iVUc1pZx.js → timeline-definition-FHXFAJF6-CB8iORNI.js} +1 -1
- package/static/assets/{vennDiagram-L72KCM5P-CL66_DTK.js → vennDiagram-L72KCM5P-CHwAX4cP.js} +1 -1
- package/static/assets/{wardleyDiagram-EHGQE667-DQHcCKUc.js → wardleyDiagram-EHGQE667-DL19kD5s.js} +1 -1
- package/static/assets/{xychartDiagram-FW5EYKEG-B8thIce8.js → xychartDiagram-FW5EYKEG-b271oXxd.js} +1 -1
- package/static/index.html +2 -2
- package/static/assets/channel-BeHa2ubV.js +0 -1
- package/static/assets/classDiagram-OUVF2IWQ-B0BHpjWc.js +0 -1
- package/static/assets/classDiagram-v2-EOCWNBFH-B0BHpjWc.js +0 -1
- package/static/assets/stateDiagram-v2-6OUMAXLB-1Opbis3l.js +0 -1
- package/static/assets/swimlanesDiagram-G3AALYLV-aoTIjn7w.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.6",
|
|
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}`];
|
|
20876
|
+
}
|
|
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
|
+
};
|
|
20787
20949
|
}
|
|
20788
|
-
function deriveVersionBranch(semver) {
|
|
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
|
}
|
|
@@ -22248,6 +22454,29 @@ function cicdRoutes(db, workspacePath, studioPort) {
|
|
|
22248
22454
|
return errorResponse3(c, e);
|
|
22249
22455
|
}
|
|
22250
22456
|
});
|
|
22457
|
+
app.post("/:projectId/rescan", async (c) => {
|
|
22458
|
+
const projectId = parseProjectId(c);
|
|
22459
|
+
if (projectId === null) return c.json({ code: "INVALID_ID" }, 400);
|
|
22460
|
+
const user = c.get("user");
|
|
22461
|
+
try {
|
|
22462
|
+
const setup = getOrCreateSetup(db, projectId, user.id);
|
|
22463
|
+
if (setup.status === "planning") {
|
|
22464
|
+
pollCicdTracking(db, workspacePath, setup.id, "plan", user.id);
|
|
22465
|
+
} else if (setup.status === "generating") {
|
|
22466
|
+
pollCicdTracking(db, workspacePath, setup.id, "generation", user.id);
|
|
22467
|
+
}
|
|
22468
|
+
const fresh = getOrCreateSetup(db, projectId, user.id);
|
|
22469
|
+
if (fresh.status === "planning" && !hasCicdWatcher(fresh.id, "plan")) {
|
|
22470
|
+
startCicdWatcher(db, workspacePath, fresh.id, "plan", user.id);
|
|
22471
|
+
}
|
|
22472
|
+
if (fresh.status === "generating" && !hasCicdWatcher(fresh.id, "generation")) {
|
|
22473
|
+
startCicdWatcher(db, workspacePath, fresh.id, "generation", user.id);
|
|
22474
|
+
}
|
|
22475
|
+
return c.json(await toDto8(c, fresh));
|
|
22476
|
+
} catch (e) {
|
|
22477
|
+
return errorResponse3(c, e);
|
|
22478
|
+
}
|
|
22479
|
+
});
|
|
22251
22480
|
app.post("/:projectId/plan", (c) => {
|
|
22252
22481
|
const projectId = parseProjectId(c);
|
|
22253
22482
|
if (projectId === null) return c.json({ code: "INVALID_ID" }, 400);
|
|
@@ -22467,12 +22696,12 @@ function createProject(db, workspacePath, name, code, baseIdea, createdBy) {
|
|
|
22467
22696
|
return projectId;
|
|
22468
22697
|
}
|
|
22469
22698
|
function scaffoldProjectFolder(workspacePath, folderName, name, baseIdea) {
|
|
22470
|
-
const
|
|
22471
|
-
mkdirSync6(
|
|
22472
|
-
mkdirSync6(join16(
|
|
22473
|
-
mkdirSync6(join16(
|
|
22474
|
-
mkdirSync6(join16(
|
|
22475
|
-
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 });
|
|
22476
22705
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
22477
22706
|
const template = [
|
|
22478
22707
|
"---",
|
|
@@ -22512,9 +22741,9 @@ function scaffoldProjectFolder(workspacePath, folderName, name, baseIdea) {
|
|
|
22512
22741
|
"# Feedback",
|
|
22513
22742
|
"<!-- Human answers appear here -->"
|
|
22514
22743
|
].join("\n");
|
|
22515
|
-
writeFileSync7(join16(
|
|
22744
|
+
writeFileSync7(join16(projectDir2, `${folderName}.brainstorm.md`), template, "utf8");
|
|
22516
22745
|
writeFileSync7(
|
|
22517
|
-
join16(
|
|
22746
|
+
join16(projectDir2, "context", "references", "brainstorm-protocol.md"),
|
|
22518
22747
|
"# Brainstorm Protocol\n\n[Installed by the co2 plugin]\n",
|
|
22519
22748
|
"utf8"
|
|
22520
22749
|
);
|
|
@@ -22613,6 +22842,9 @@ function getProjectDetail(db, workspacePath, projectId) {
|
|
|
22613
22842
|
workflowPhase: project.workflowPhase,
|
|
22614
22843
|
creationStatus,
|
|
22615
22844
|
isArchived: project.isArchived,
|
|
22845
|
+
gitRemoteUrl: project.gitRemoteUrl ?? null,
|
|
22846
|
+
defaultBranch: project.defaultBranch ?? "main",
|
|
22847
|
+
gitProvider: project.gitProvider ?? "github",
|
|
22616
22848
|
applications: apps,
|
|
22617
22849
|
createdAt: project.createdAt
|
|
22618
22850
|
};
|
|
@@ -22668,11 +22900,11 @@ function getProjectDetailSummary(db, projectId) {
|
|
|
22668
22900
|
function regenerateCO2Files(db, workspacePath, projectId) {
|
|
22669
22901
|
const project = db.select().from(projects).where(eq(projects.id, projectId)).limit(1).get();
|
|
22670
22902
|
if (!project) throw svcError10("NOT_FOUND", 404);
|
|
22671
|
-
const
|
|
22672
|
-
mkdirSync6(
|
|
22903
|
+
const projectDir2 = join16(workspacePath, project.workspaceFolderName);
|
|
22904
|
+
mkdirSync6(projectDir2, { recursive: true });
|
|
22673
22905
|
const apps = db.select().from(applications).where(and(eq(applications.projectId, projectId), eq(applications.isDeleted, 0))).all();
|
|
22674
22906
|
writeFileSync7(
|
|
22675
|
-
join16(
|
|
22907
|
+
join16(projectDir2, "CLAUDE.md"),
|
|
22676
22908
|
project.rawClaudeMd ?? buildClaudeMd(project.name, apps),
|
|
22677
22909
|
"utf8"
|
|
22678
22910
|
);
|
|
@@ -22685,8 +22917,8 @@ function regenerateCO2Files(db, workspacePath, projectId) {
|
|
|
22685
22917
|
}
|
|
22686
22918
|
for (const app of apps) {
|
|
22687
22919
|
const mods = db.select().from(projectModules).where(and(eq(projectModules.applicationId, app.id), eq(projectModules.isDeleted, 0))).all();
|
|
22688
|
-
const appRel = resolveAppFolderName(
|
|
22689
|
-
const appDir = join16(
|
|
22920
|
+
const appRel = resolveAppFolderName(projectDir2, app.name);
|
|
22921
|
+
const appDir = join16(projectDir2, appRel, "context");
|
|
22690
22922
|
mkdirSync6(appDir, { recursive: true });
|
|
22691
22923
|
writeFileSync7(
|
|
22692
22924
|
join16(appDir, "PRD.md"),
|
|
@@ -22743,10 +22975,10 @@ function toKebabCase(name) {
|
|
|
22743
22975
|
function rescanClaudeMd(db, workspacePath, projectId) {
|
|
22744
22976
|
const project = db.select().from(projects).where(eq(projects.id, projectId)).limit(1).get();
|
|
22745
22977
|
if (!project) throw svcError10("NOT_FOUND", 404);
|
|
22746
|
-
const
|
|
22978
|
+
const projectDir2 = join16(workspacePath, project.workspaceFolderName);
|
|
22747
22979
|
const claudeMdPath = [
|
|
22748
|
-
join16(
|
|
22749
|
-
join16(
|
|
22980
|
+
join16(projectDir2, "CLAUDE.md"),
|
|
22981
|
+
join16(projectDir2, "source", "CLAUDE.md")
|
|
22750
22982
|
].find((p) => existsSync14(p));
|
|
22751
22983
|
if (!claudeMdPath) throw svcError10("CLAUDE_MD_MISSING", 404);
|
|
22752
22984
|
const content = readFileSync9(claudeMdPath, "utf8");
|
|
@@ -22837,6 +23069,18 @@ function deriveAppCode(name) {
|
|
|
22837
23069
|
const initials = name.split(/[\s_-]+/).filter(Boolean).map((w) => w[0].toUpperCase()).join("");
|
|
22838
23070
|
return (initials || name.slice(0, 2)).slice(0, 4);
|
|
22839
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
|
+
}
|
|
22840
23084
|
function archiveProject(db, projectId, archivedBy) {
|
|
22841
23085
|
const project = db.select({ id: projects.id }).from(projects).where(eq(projects.id, projectId)).limit(1).get();
|
|
22842
23086
|
if (!project) throw svcError10("NOT_FOUND", 404);
|
|
@@ -23497,9 +23741,9 @@ function writeBugMdFile(db, workspacePath, app, projectFolderName) {
|
|
|
23497
23741
|
db.select({ id: prdVersions.id, semver: prdVersions.semver }).from(prdVersions).where(eq(prdVersions.projectId, app.projectId)).all().map((v) => [v.id, v.semver])
|
|
23498
23742
|
);
|
|
23499
23743
|
const content = buildBugMd(modules, bugs, semverByVersionId);
|
|
23500
|
-
const
|
|
23501
|
-
const appRel = resolveAppFolderName(
|
|
23502
|
-
const appContextDir = join17(
|
|
23744
|
+
const projectDir2 = join17(workspacePath, projectFolderName);
|
|
23745
|
+
const appRel = resolveAppFolderName(projectDir2, app.name);
|
|
23746
|
+
const appContextDir = join17(projectDir2, appRel, "context");
|
|
23503
23747
|
mkdirSync7(appContextDir, { recursive: true });
|
|
23504
23748
|
writeFileSync8(join17(appContextDir, "BUG.md"), content, "utf8");
|
|
23505
23749
|
return { content, bugCount: bugs.length };
|
|
@@ -23534,8 +23778,8 @@ function syncBugMdToDisk(db, workspacePath, applicationId, bugId, mode = "replac
|
|
|
23534
23778
|
const project = db.select({ workspaceFolderName: projects.workspaceFolderName }).from(projects).where(eq(projects.id, app.projectId)).get();
|
|
23535
23779
|
if (!project) return;
|
|
23536
23780
|
if (bugId !== void 0) {
|
|
23537
|
-
const
|
|
23538
|
-
const file = join17(
|
|
23781
|
+
const projectDir2 = join17(workspacePath, project.workspaceFolderName);
|
|
23782
|
+
const file = join17(projectDir2, resolveAppFolderName(projectDir2, app.name), "context", "BUG.md");
|
|
23539
23783
|
if (existsSync15(file)) {
|
|
23540
23784
|
const bug = db.select().from(bugReports).where(and(eq(bugReports.id, bugId), eq(bugReports.isDeleted, 0))).get();
|
|
23541
23785
|
if (bug) {
|
|
@@ -23777,6 +24021,332 @@ function documentRoutes(db, dataDir) {
|
|
|
23777
24021
|
return app;
|
|
23778
24022
|
}
|
|
23779
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
|
+
|
|
23780
24350
|
// ../server/src/features/notification/routes.ts
|
|
23781
24351
|
function notificationRoutes(db) {
|
|
23782
24352
|
const app = new Hono2();
|
|
@@ -23807,7 +24377,7 @@ function notificationRoutes(db) {
|
|
|
23807
24377
|
}
|
|
23808
24378
|
|
|
23809
24379
|
// ../server/src/features/prd-wizard/routes.ts
|
|
23810
|
-
function
|
|
24380
|
+
function errorResponse8(c, e) {
|
|
23811
24381
|
const err = e;
|
|
23812
24382
|
const status = err.status ?? 500;
|
|
23813
24383
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -23836,7 +24406,7 @@ function prdWizardRoutes(db, workspacePath) {
|
|
|
23836
24406
|
startWizardApplyWatcher(db, workspacePath, requestId);
|
|
23837
24407
|
return c.json({ requestId, filePath, status: "generated" }, 201);
|
|
23838
24408
|
} catch (e) {
|
|
23839
|
-
return
|
|
24409
|
+
return errorResponse8(c, e);
|
|
23840
24410
|
}
|
|
23841
24411
|
});
|
|
23842
24412
|
app.get("/:projectId", (c) => {
|
|
@@ -23848,14 +24418,14 @@ function prdWizardRoutes(db, workspacePath) {
|
|
|
23848
24418
|
try {
|
|
23849
24419
|
return c.json(listPrdWizardRequests(db, projectId, prdVersionId));
|
|
23850
24420
|
} catch (e) {
|
|
23851
|
-
return
|
|
24421
|
+
return errorResponse8(c, e);
|
|
23852
24422
|
}
|
|
23853
24423
|
});
|
|
23854
24424
|
return app;
|
|
23855
24425
|
}
|
|
23856
24426
|
|
|
23857
24427
|
// ../server/src/features/project-config/routes.ts
|
|
23858
|
-
function
|
|
24428
|
+
function errorResponse9(c, e) {
|
|
23859
24429
|
const err = e;
|
|
23860
24430
|
const status = err.status ?? 500;
|
|
23861
24431
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -23872,7 +24442,7 @@ function projectConfigRoutes(db, workspacePath) {
|
|
|
23872
24442
|
try {
|
|
23873
24443
|
return c.json(getProjectEnvironment(db, projectId));
|
|
23874
24444
|
} catch (e) {
|
|
23875
|
-
return
|
|
24445
|
+
return errorResponse9(c, e);
|
|
23876
24446
|
}
|
|
23877
24447
|
});
|
|
23878
24448
|
app.patch("/:projectId/environment", zValidator("json", saveWorkspaceConfigSchema), (c) => {
|
|
@@ -23883,7 +24453,7 @@ function projectConfigRoutes(db, workspacePath) {
|
|
|
23883
24453
|
saveProjectEnvironment(db, workspacePath, projectId, c.req.valid("json").content)
|
|
23884
24454
|
);
|
|
23885
24455
|
} catch (e) {
|
|
23886
|
-
return
|
|
24456
|
+
return errorResponse9(c, e);
|
|
23887
24457
|
}
|
|
23888
24458
|
});
|
|
23889
24459
|
app.post(
|
|
@@ -23906,7 +24476,7 @@ function projectConfigRoutes(db, workspacePath) {
|
|
|
23906
24476
|
)
|
|
23907
24477
|
);
|
|
23908
24478
|
} catch (e) {
|
|
23909
|
-
return
|
|
24479
|
+
return errorResponse9(c, e);
|
|
23910
24480
|
}
|
|
23911
24481
|
}
|
|
23912
24482
|
);
|
|
@@ -23916,7 +24486,7 @@ function projectConfigRoutes(db, workspacePath) {
|
|
|
23916
24486
|
try {
|
|
23917
24487
|
return c.json(getProjectDevtool(db, projectId));
|
|
23918
24488
|
} catch (e) {
|
|
23919
|
-
return
|
|
24489
|
+
return errorResponse9(c, e);
|
|
23920
24490
|
}
|
|
23921
24491
|
});
|
|
23922
24492
|
app.patch("/:projectId/devtool", zValidator("json", saveWorkspaceConfigSchema), (c) => {
|
|
@@ -23925,7 +24495,7 @@ function projectConfigRoutes(db, workspacePath) {
|
|
|
23925
24495
|
try {
|
|
23926
24496
|
return c.json(saveProjectDevtool(db, workspacePath, projectId, c.req.valid("json").content));
|
|
23927
24497
|
} catch (e) {
|
|
23928
|
-
return
|
|
24498
|
+
return errorResponse9(c, e);
|
|
23929
24499
|
}
|
|
23930
24500
|
});
|
|
23931
24501
|
app.post(
|
|
@@ -23942,7 +24512,7 @@ function projectConfigRoutes(db, workspacePath) {
|
|
|
23942
24512
|
importProjectDevtool(db, workspacePath, projectId, c.req.valid("json").content, user.id)
|
|
23943
24513
|
);
|
|
23944
24514
|
} catch (e) {
|
|
23945
|
-
return
|
|
24515
|
+
return errorResponse9(c, e);
|
|
23946
24516
|
}
|
|
23947
24517
|
}
|
|
23948
24518
|
);
|
|
@@ -23950,8 +24520,8 @@ function projectConfigRoutes(db, workspacePath) {
|
|
|
23950
24520
|
}
|
|
23951
24521
|
|
|
23952
24522
|
// ../server/src/features/project-import/scanner.ts
|
|
23953
|
-
import { existsSync as
|
|
23954
|
-
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";
|
|
23955
24525
|
|
|
23956
24526
|
// ../server/src/features/project-import/changelog-md.ts
|
|
23957
24527
|
var VERSION_HEADING = /^##\s+v?(\d+\.\d+\.\d+)\b/;
|
|
@@ -24099,64 +24669,64 @@ function svcError13(code, status) {
|
|
|
24099
24669
|
return Object.assign(new Error(code), { code, status });
|
|
24100
24670
|
}
|
|
24101
24671
|
function readIfExists(path) {
|
|
24102
|
-
return
|
|
24672
|
+
return existsSync18(path) ? readFileSync13(path, "utf8") : null;
|
|
24103
24673
|
}
|
|
24104
24674
|
function emptyArtifactCounts() {
|
|
24105
24675
|
return { erd: 0, data_model: 0, mockup: 0, technical_spec: 0, test_spec: 0, source_code: 0 };
|
|
24106
24676
|
}
|
|
24107
24677
|
function countArtifacts(appDir) {
|
|
24108
24678
|
const counts = emptyArtifactCounts();
|
|
24109
|
-
const contextDir =
|
|
24110
|
-
const modelDir =
|
|
24111
|
-
if (
|
|
24679
|
+
const contextDir = join20(appDir, "context");
|
|
24680
|
+
const modelDir = join20(contextDir, "model");
|
|
24681
|
+
if (existsSync18(modelDir)) {
|
|
24112
24682
|
for (const slug of readdirSync3(modelDir)) {
|
|
24113
24683
|
try {
|
|
24114
|
-
if (!statSync5(
|
|
24684
|
+
if (!statSync5(join20(modelDir, slug)).isDirectory()) continue;
|
|
24115
24685
|
} catch {
|
|
24116
24686
|
continue;
|
|
24117
24687
|
}
|
|
24118
|
-
if (
|
|
24688
|
+
if (existsSync18(join20(modelDir, slug, "erd.mermaid")) || existsSync18(join20(modelDir, slug, "document-model.mermaid")))
|
|
24119
24689
|
counts.erd++;
|
|
24120
|
-
if (
|
|
24121
|
-
if (
|
|
24122
|
-
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++;
|
|
24123
24693
|
}
|
|
24124
24694
|
}
|
|
24125
24695
|
for (const [sub, key, file] of [
|
|
24126
24696
|
["specification", "technical_spec", "SPEC.md"],
|
|
24127
24697
|
["test", "test_spec", "TEST_SPEC.md"]
|
|
24128
24698
|
]) {
|
|
24129
|
-
const dir =
|
|
24130
|
-
if (!
|
|
24699
|
+
const dir = join20(contextDir, sub);
|
|
24700
|
+
if (!existsSync18(dir)) continue;
|
|
24131
24701
|
let total = 0;
|
|
24132
24702
|
for (const slug of readdirSync3(dir)) {
|
|
24133
24703
|
try {
|
|
24134
|
-
if (statSync5(
|
|
24704
|
+
if (statSync5(join20(dir, slug)).isDirectory() && existsSync18(join20(dir, slug, file))) total++;
|
|
24135
24705
|
} catch {
|
|
24136
24706
|
}
|
|
24137
24707
|
}
|
|
24138
24708
|
counts[key] = Math.max(counts[key], total);
|
|
24139
24709
|
}
|
|
24140
|
-
if (
|
|
24710
|
+
if (existsSync18(join20(contextDir, "mockup", "MOCKUP.html")) || existsSync18(join20(contextDir, "mockup", "index.html")))
|
|
24141
24711
|
counts.mockup = 1;
|
|
24142
24712
|
counts.source_code = sweepSourceFiles(appDir).length;
|
|
24143
24713
|
return counts;
|
|
24144
24714
|
}
|
|
24145
24715
|
function findClaudeMd(folderDir) {
|
|
24146
|
-
if (
|
|
24147
|
-
if (
|
|
24716
|
+
if (existsSync18(join20(folderDir, "CLAUDE.md"))) return { relPath: "CLAUDE.md", prefix: "" };
|
|
24717
|
+
if (existsSync18(join20(folderDir, "source", "CLAUDE.md")))
|
|
24148
24718
|
return { relPath: "source/CLAUDE.md", prefix: "source" };
|
|
24149
24719
|
return null;
|
|
24150
24720
|
}
|
|
24151
24721
|
function listImportCandidates(db, workspacePath) {
|
|
24152
|
-
if (!
|
|
24722
|
+
if (!existsSync18(workspacePath)) return [];
|
|
24153
24723
|
const registered = new Set(
|
|
24154
24724
|
db.select({ folder: projects.workspaceFolderName }).from(projects).all().map((r) => r.folder)
|
|
24155
24725
|
);
|
|
24156
24726
|
const candidates = [];
|
|
24157
24727
|
for (const entry of readdirSync3(workspacePath)) {
|
|
24158
24728
|
if (entry.startsWith(".") || registered.has(entry)) continue;
|
|
24159
|
-
const dir =
|
|
24729
|
+
const dir = join20(workspacePath, entry);
|
|
24160
24730
|
try {
|
|
24161
24731
|
if (!statSync5(dir).isDirectory()) continue;
|
|
24162
24732
|
} catch {
|
|
@@ -24164,14 +24734,14 @@ function listImportCandidates(db, workspacePath) {
|
|
|
24164
24734
|
}
|
|
24165
24735
|
const found = findClaudeMd(dir);
|
|
24166
24736
|
if (!found) continue;
|
|
24167
|
-
const
|
|
24168
|
-
|
|
24737
|
+
const detail2 = parseProjectDetail(
|
|
24738
|
+
readFileSync13(join20(workspacePath, entry, found.relPath), "utf8")
|
|
24169
24739
|
);
|
|
24170
24740
|
candidates.push({
|
|
24171
24741
|
folderName: entry,
|
|
24172
24742
|
claudeMdPath: `${entry}/${found.relPath}`,
|
|
24173
|
-
projectName:
|
|
24174
|
-
projectCode:
|
|
24743
|
+
projectName: detail2.projectName,
|
|
24744
|
+
projectCode: detail2.projectCode
|
|
24175
24745
|
});
|
|
24176
24746
|
}
|
|
24177
24747
|
return candidates.sort((a, b) => a.folderName.localeCompare(b.folderName));
|
|
@@ -24179,22 +24749,22 @@ function listImportCandidates(db, workspacePath) {
|
|
|
24179
24749
|
function scanRepo(db, workspacePath, folderName) {
|
|
24180
24750
|
const registered = db.select({ id: projects.id }).from(projects).where(eq(projects.workspaceFolderName, folderName)).limit(1).get();
|
|
24181
24751
|
if (registered) throw svcError13("FOLDER_ALREADY_IMPORTED", 409);
|
|
24182
|
-
const folderDir =
|
|
24183
|
-
const found =
|
|
24752
|
+
const folderDir = join20(workspacePath, folderName);
|
|
24753
|
+
const found = existsSync18(folderDir) ? findClaudeMd(folderDir) : null;
|
|
24184
24754
|
if (!found) throw svcError13("CLAUDE_MD_MISSING", 404);
|
|
24185
|
-
const repoRoot = found.prefix ?
|
|
24186
|
-
const claudeMdRaw =
|
|
24187
|
-
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);
|
|
24188
24758
|
const claudeApps = parseClaudeMdApplications(claudeMdRaw);
|
|
24189
24759
|
const claudeModules = new Set(parseModuleNames(claudeMdRaw).map((m) => m.name));
|
|
24190
24760
|
const warnings = [];
|
|
24191
|
-
if (
|
|
24192
|
-
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();
|
|
24193
24763
|
if (nameTaken) {
|
|
24194
24764
|
warnings.push({
|
|
24195
24765
|
level: "warning",
|
|
24196
24766
|
code: "DUPLICATE_PROJECT_NAME",
|
|
24197
|
-
message: `A project named "${
|
|
24767
|
+
message: `A project named "${detail2.projectName}" already exists \u2014 rename before importing.`
|
|
24198
24768
|
});
|
|
24199
24769
|
}
|
|
24200
24770
|
}
|
|
@@ -24206,10 +24776,10 @@ function scanRepo(db, workspacePath, folderName) {
|
|
|
24206
24776
|
for (const claude of claudeApps) {
|
|
24207
24777
|
const name = toKebabCase(claude.heading);
|
|
24208
24778
|
const resolvedInRoot = resolveAppFolderName(repoRoot, name);
|
|
24209
|
-
const folderExists =
|
|
24779
|
+
const folderExists = existsSync18(join20(repoRoot, resolvedInRoot));
|
|
24210
24780
|
const folderRel = folderExists ? found.prefix ? `${found.prefix}/${resolvedInRoot}` : resolvedInRoot : null;
|
|
24211
|
-
const appDir = folderRel ?
|
|
24212
|
-
const prdRaw = appDir ? readIfExists(
|
|
24781
|
+
const appDir = folderRel ? join20(folderDir, folderRel) : null;
|
|
24782
|
+
const prdRaw = appDir ? readIfExists(join20(appDir, "context", "PRD.md")) : null;
|
|
24213
24783
|
const prdModules = prdRaw ? parsePrdMarkdown(prdRaw) : [];
|
|
24214
24784
|
const versions = /* @__PURE__ */ new Set();
|
|
24215
24785
|
for (const m of prdModules) {
|
|
@@ -24220,19 +24790,19 @@ function scanRepo(db, workspacePath, folderName) {
|
|
|
24220
24790
|
for (const v of versions) semverUnion.add(v);
|
|
24221
24791
|
const derived = prdRaw ? deriveRequirementIdAppCode(prdRaw, name) : { code: appCodeFromInitials(name), derivedFromIds: false };
|
|
24222
24792
|
const requirementIdAppCode = claude.requirementIdAppCode ?? derived.code;
|
|
24223
|
-
const bugMdRaw = appDir ? readIfExists(
|
|
24793
|
+
const bugMdRaw = appDir ? readIfExists(join20(appDir, "context", "BUG.md")) : null;
|
|
24224
24794
|
const bugItems = bugMdRaw ? parseBugMd(bugMdRaw) : [];
|
|
24225
|
-
const bugMasterRaw = appDir ? readIfExists(
|
|
24795
|
+
const bugMasterRaw = appDir ? readIfExists(join20(appDir, "context", "bug", "BUG_MASTER.md")) : null;
|
|
24226
24796
|
const bugMaster = bugMasterRaw ? parseBugMaster(bugMasterRaw) : /* @__PURE__ */ new Map();
|
|
24227
24797
|
for (const item of bugItems) if (item.semver) semverUnion.add(item.semver);
|
|
24228
24798
|
for (const [, m] of bugMaster) if (m.semver) semverUnion.add(m.semver);
|
|
24229
|
-
const preparePath = appDir ?
|
|
24230
|
-
const developPath = appDir ?
|
|
24231
|
-
const prepareMasterExists = !!appDir &&
|
|
24232
|
-
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);
|
|
24233
24803
|
const prepareCompleted = prepareMasterExists && parseTrackingStatus(preparePath) === "COMPLETED";
|
|
24234
24804
|
const developCompleted = developMasterExists && parseTrackingStatus(developPath) === "COMPLETED";
|
|
24235
|
-
const changelogRaw = appDir ? readIfExists(
|
|
24805
|
+
const changelogRaw = appDir ? readIfExists(join20(appDir, "CHANGELOG.md")) : null;
|
|
24236
24806
|
const changelogEntries = changelogRaw ? parseChangelogConductorEntries(changelogRaw) : [];
|
|
24237
24807
|
if (!folderRel) {
|
|
24238
24808
|
warnings.push({
|
|
@@ -24314,21 +24884,28 @@ function scanRepo(db, workspacePath, folderName) {
|
|
|
24314
24884
|
}
|
|
24315
24885
|
const versionUnion = [...semverUnion].sort(compareSemver);
|
|
24316
24886
|
const workflowPhase = anyBugs ? "qa" : anyDevelopCompleted ? "developed" : anyPrepareMaster ? "prepared" : "brainstorm";
|
|
24317
|
-
const environmentMd = readIfExists(
|
|
24318
|
-
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";
|
|
24319
24893
|
return {
|
|
24320
24894
|
folderName,
|
|
24321
24895
|
sourcePrefix: found.prefix,
|
|
24322
24896
|
claudeMdRelPath: found.relPath,
|
|
24323
24897
|
claudeMdRaw,
|
|
24324
|
-
projectName:
|
|
24325
|
-
projectCode:
|
|
24898
|
+
projectName: detail2.projectName,
|
|
24899
|
+
projectCode: detail2.projectCode,
|
|
24326
24900
|
apps,
|
|
24327
24901
|
versionUnion,
|
|
24328
24902
|
currentVersion: versionUnion.at(-1) ?? null,
|
|
24329
24903
|
workflowPhase,
|
|
24330
24904
|
environmentMd,
|
|
24331
24905
|
devtoolMd,
|
|
24906
|
+
gitRemoteUrl,
|
|
24907
|
+
defaultBranch,
|
|
24908
|
+
gitProvider,
|
|
24332
24909
|
warnings
|
|
24333
24910
|
};
|
|
24334
24911
|
}
|
|
@@ -24367,11 +24944,11 @@ function scanProject(db, workspacePath, folderName) {
|
|
|
24367
24944
|
}
|
|
24368
24945
|
|
|
24369
24946
|
// ../server/src/features/project-import/service.ts
|
|
24370
|
-
import { join as
|
|
24947
|
+
import { join as join22, relative as relative2 } from "path";
|
|
24371
24948
|
|
|
24372
24949
|
// ../server/src/features/workflow/service.ts
|
|
24373
|
-
import { existsSync as
|
|
24374
|
-
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";
|
|
24375
24952
|
var ACTIVE_RUN_STATUSES = ["pending", "in_progress"];
|
|
24376
24953
|
function svcError14(code, status) {
|
|
24377
24954
|
return Object.assign(new Error(code), { code, status });
|
|
@@ -24513,9 +25090,9 @@ function validateAllAppsPending(db, projectId, applicationIds, conductorType, pr
|
|
|
24513
25090
|
const notPending = applicationIds.filter((id) => !pending.has(id));
|
|
24514
25091
|
if (notPending.length > 0) throw svcError14("APPS_NOT_PENDING", 422);
|
|
24515
25092
|
}
|
|
24516
|
-
function resolveRunTrackingFilePath(
|
|
25093
|
+
function resolveRunTrackingFilePath(projectDir2, appName, conductorType) {
|
|
24517
25094
|
const master = TRACKING_FILE_MAP[conductorType] ?? "";
|
|
24518
|
-
return
|
|
25095
|
+
return join21(projectDir2, resolveAppFolderName(projectDir2, appName), master);
|
|
24519
25096
|
}
|
|
24520
25097
|
function buildWorkflowPath(username) {
|
|
24521
25098
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\..+$/, "").replace("T", "-");
|
|
@@ -24588,7 +25165,7 @@ function generateWorkflowMarkdown(db, workspacePath, input, creatingUser, watche
|
|
|
24588
25165
|
const ver = db.select({ semver: prdVersions.semver }).from(prdVersions).where(eq(prdVersions.id, prdVersionId)).get();
|
|
24589
25166
|
const semver = ver?.semver ?? "latest";
|
|
24590
25167
|
const relPath = buildWorkflowPath(creatingUser.username);
|
|
24591
|
-
const
|
|
25168
|
+
const projectDir2 = join21(workspacePath, project.workspaceFolderName);
|
|
24592
25169
|
const content = buildWorkflowMarkdown(
|
|
24593
25170
|
input.conductorType,
|
|
24594
25171
|
apps.map((a) => a.name),
|
|
@@ -24596,8 +25173,8 @@ function generateWorkflowMarkdown(db, workspacePath, input, creatingUser, watche
|
|
|
24596
25173
|
project.workspaceFolderName,
|
|
24597
25174
|
relPath
|
|
24598
25175
|
);
|
|
24599
|
-
mkdirSync9(
|
|
24600
|
-
|
|
25176
|
+
mkdirSync9(join21(projectDir2, "workflow", creatingUser.username), { recursive: true });
|
|
25177
|
+
writeFileSync11(join21(projectDir2, relPath), content, "utf8");
|
|
24601
25178
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24602
25179
|
const wfm = db.insert(workflowMarkdowns).values({
|
|
24603
25180
|
projectId: input.projectId,
|
|
@@ -24613,7 +25190,7 @@ function generateWorkflowMarkdown(db, workspacePath, input, creatingUser, watche
|
|
|
24613
25190
|
const runApps = [];
|
|
24614
25191
|
for (const appId of input.applicationIds) {
|
|
24615
25192
|
const appName = apps.find((a) => a.id === appId)?.name ?? "";
|
|
24616
|
-
const trackingFilePath = resolveRunTrackingFilePath(
|
|
25193
|
+
const trackingFilePath = resolveRunTrackingFilePath(projectDir2, appName, input.conductorType);
|
|
24617
25194
|
const run = db.insert(conductorRuns).values({
|
|
24618
25195
|
projectId: input.projectId,
|
|
24619
25196
|
applicationId: appId,
|
|
@@ -24637,7 +25214,7 @@ function generateWorkflowMarkdown(db, workspacePath, input, creatingUser, watche
|
|
|
24637
25214
|
startWatcher(
|
|
24638
25215
|
db,
|
|
24639
25216
|
runId,
|
|
24640
|
-
() => resolveRunTrackingFilePath(
|
|
25217
|
+
() => resolveRunTrackingFilePath(projectDir2, appName, input.conductorType),
|
|
24641
25218
|
creatingUser.id,
|
|
24642
25219
|
watcherIntervalMs,
|
|
24643
25220
|
(id, status) => {
|
|
@@ -24676,13 +25253,13 @@ function backfillWorkflowHistoryFromChangelogs(db, workspacePath, projectId) {
|
|
|
24676
25253
|
if (!project) return 0;
|
|
24677
25254
|
const apps = db.select({ id: applications.id, name: applications.name }).from(applications).where(and(eq(applications.projectId, projectId), eq(applications.isDeleted, 0))).all();
|
|
24678
25255
|
if (apps.length === 0) return 0;
|
|
24679
|
-
const
|
|
25256
|
+
const projectDir2 = join21(workspacePath, project.workspaceFolderName);
|
|
24680
25257
|
const collected = [];
|
|
24681
25258
|
for (const app of apps) {
|
|
24682
|
-
const appRel = resolveAppFolderName(
|
|
24683
|
-
const changelogPath =
|
|
24684
|
-
if (!
|
|
24685
|
-
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"))) {
|
|
24686
25263
|
collected.push({ appName: app.name, appRel, entry });
|
|
24687
25264
|
}
|
|
24688
25265
|
}
|
|
@@ -24728,7 +25305,7 @@ function getActiveRun(db, projectId, workspacePath) {
|
|
|
24728
25305
|
function resumeWatchers(db, workspacePath, projectId, watcherIntervalMs) {
|
|
24729
25306
|
const project = db.select().from(projects).where(eq(projects.id, projectId)).get();
|
|
24730
25307
|
if (!project) return;
|
|
24731
|
-
const
|
|
25308
|
+
const projectDir2 = join21(workspacePath, project.workspaceFolderName);
|
|
24732
25309
|
const stale = db.select({ run: conductorRuns, appName: applications.name }).from(conductorRuns).innerJoin(applications, eq(applications.id, conductorRuns.applicationId)).where(
|
|
24733
25310
|
and(
|
|
24734
25311
|
eq(conductorRuns.projectId, projectId),
|
|
@@ -24743,7 +25320,7 @@ function resumeWatchers(db, workspacePath, projectId, watcherIntervalMs) {
|
|
|
24743
25320
|
db,
|
|
24744
25321
|
run.id,
|
|
24745
25322
|
// [BUG-027] Re-resolve per poll — heals rows persisted with a stale/project-level path.
|
|
24746
|
-
() => resolveRunTrackingFilePath(
|
|
25323
|
+
() => resolveRunTrackingFilePath(projectDir2, appName, conductorType),
|
|
24747
25324
|
run.startedBy ?? 1,
|
|
24748
25325
|
watcherIntervalMs,
|
|
24749
25326
|
(id, status) => {
|
|
@@ -24778,7 +25355,7 @@ function rescanWorkflowRun(db, workspacePath, workflowMarkdownId, watcherInterva
|
|
|
24778
25355
|
if (!wfm) throw svcError14("RUN_NOT_FOUND", 404);
|
|
24779
25356
|
const project = db.select().from(projects).where(eq(projects.id, wfm.projectId)).get();
|
|
24780
25357
|
if (!project) throw svcError14("PROJECT_NOT_FOUND", 404);
|
|
24781
|
-
const
|
|
25358
|
+
const projectDir2 = join21(workspacePath, project.workspaceFolderName);
|
|
24782
25359
|
const runs = db.select({ run: conductorRuns, appName: applications.name }).from(conductorRuns).innerJoin(applications, eq(applications.id, conductorRuns.applicationId)).where(
|
|
24783
25360
|
and(
|
|
24784
25361
|
eq(conductorRuns.workflowMarkdownId, workflowMarkdownId),
|
|
@@ -24787,7 +25364,7 @@ function rescanWorkflowRun(db, workspacePath, workflowMarkdownId, watcherInterva
|
|
|
24787
25364
|
).all();
|
|
24788
25365
|
for (const { run, appName } of runs) {
|
|
24789
25366
|
const conductorType = run.conductorType;
|
|
24790
|
-
const freshPath = resolveRunTrackingFilePath(
|
|
25367
|
+
const freshPath = resolveRunTrackingFilePath(projectDir2, appName, conductorType);
|
|
24791
25368
|
if (freshPath !== run.trackingFilePath) {
|
|
24792
25369
|
db.update(conductorRuns).set({ trackingFilePath: freshPath }).where(eq(conductorRuns.id, run.id)).run();
|
|
24793
25370
|
}
|
|
@@ -24807,7 +25384,7 @@ function rescanWorkflowRun(db, workspacePath, workflowMarkdownId, watcherInterva
|
|
|
24807
25384
|
startWatcher(
|
|
24808
25385
|
db,
|
|
24809
25386
|
run.id,
|
|
24810
|
-
() => resolveRunTrackingFilePath(
|
|
25387
|
+
() => resolveRunTrackingFilePath(projectDir2, appName, conductorType),
|
|
24811
25388
|
run.startedBy ?? 1,
|
|
24812
25389
|
watcherIntervalMs,
|
|
24813
25390
|
(id, terminalStatus) => {
|
|
@@ -24839,12 +25416,12 @@ function getWorkflowRunFileContent(db, workspacePath, workflowMarkdownId) {
|
|
|
24839
25416
|
if (!wfm) throw svcError14("RUN_NOT_FOUND", 404);
|
|
24840
25417
|
const project = db.select().from(projects).where(eq(projects.id, wfm.projectId)).get();
|
|
24841
25418
|
if (!project) throw svcError14("PROJECT_NOT_FOUND", 404);
|
|
24842
|
-
const
|
|
24843
|
-
const absolute = resolve2(
|
|
24844
|
-
if (absolute !==
|
|
25419
|
+
const projectDir2 = resolve2(workspacePath, project.workspaceFolderName);
|
|
25420
|
+
const absolute = resolve2(projectDir2, wfm.filePath);
|
|
25421
|
+
if (absolute !== projectDir2 && !absolute.startsWith(projectDir2 + sep))
|
|
24845
25422
|
throw svcError14("INVALID_PATH", 400);
|
|
24846
|
-
if (!
|
|
24847
|
-
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") };
|
|
24848
25425
|
}
|
|
24849
25426
|
function getRunApplicationChangelog(db, workspacePath, workflowMarkdownId, appName) {
|
|
24850
25427
|
const wfm = db.select().from(workflowMarkdowns).where(eq(workflowMarkdowns.id, workflowMarkdownId)).get();
|
|
@@ -24859,16 +25436,16 @@ function getRunApplicationChangelog(db, workspacePath, workflowMarkdownId, appNa
|
|
|
24859
25436
|
runAppNames = appIds.length > 0 ? db.select({ name: applications.name }).from(applications).where(inArray(applications.id, appIds)).all().map((a) => a.name) : [];
|
|
24860
25437
|
}
|
|
24861
25438
|
if (!runAppNames.includes(appName)) throw svcError14("APP_NOT_IN_RUN", 404);
|
|
24862
|
-
const
|
|
24863
|
-
const appFolder = resolveAppFolderName(
|
|
24864
|
-
const absolute = resolve2(
|
|
24865
|
-
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))
|
|
24866
25443
|
throw svcError14("INVALID_PATH", 400);
|
|
24867
|
-
if (!
|
|
25444
|
+
if (!existsSync19(absolute)) throw svcError14("CHANGELOG_NOT_FOUND", 404);
|
|
24868
25445
|
return {
|
|
24869
25446
|
applicationName: appName,
|
|
24870
25447
|
filePath: relative(workspacePath, absolute).replace(/\\/g, "/"),
|
|
24871
|
-
content:
|
|
25448
|
+
content: readFileSync14(absolute, "utf-8")
|
|
24872
25449
|
};
|
|
24873
25450
|
}
|
|
24874
25451
|
function toDetail(db, projectFolder, wfm, workspacePath) {
|
|
@@ -24903,20 +25480,20 @@ function toDetail(db, projectFolder, wfm, workspacePath) {
|
|
|
24903
25480
|
const appNames = appRows.map((a) => a.name);
|
|
24904
25481
|
let appBreakdown;
|
|
24905
25482
|
if (workspacePath) {
|
|
24906
|
-
const
|
|
25483
|
+
const projectDir2 = join21(workspacePath, projectFolder);
|
|
24907
25484
|
appBreakdown = runs.map((r) => {
|
|
24908
25485
|
const appName = nameOf.get(r.applicationId) ?? `app #${r.applicationId}`;
|
|
24909
|
-
const trackingPath = resolveRunTrackingFilePath(
|
|
25486
|
+
const trackingPath = resolveRunTrackingFilePath(projectDir2, appName, r.conductorType);
|
|
24910
25487
|
return {
|
|
24911
25488
|
applicationName: appName,
|
|
24912
25489
|
status: r.status,
|
|
24913
|
-
trackingFileExists:
|
|
25490
|
+
trackingFileExists: existsSync19(trackingPath),
|
|
24914
25491
|
trackingFilePath: relative(workspacePath, trackingPath).replace(/\\/g, "/")
|
|
24915
25492
|
};
|
|
24916
25493
|
});
|
|
24917
25494
|
}
|
|
24918
25495
|
const completedAt = runs.every((r) => r.completedAt) ? runs.map((r) => r.completedAt).sort().at(-1) ?? null : null;
|
|
24919
|
-
const
|
|
25496
|
+
const git4 = runs.find((r) => r.gitOperationStatus && r.gitOperationStatus !== "not_started");
|
|
24920
25497
|
return {
|
|
24921
25498
|
id: wfm.id,
|
|
24922
25499
|
conductorType: wfm.conductorType,
|
|
@@ -24926,9 +25503,9 @@ function toDetail(db, projectFolder, wfm, workspacePath) {
|
|
|
24926
25503
|
filePath: wfm.filePath,
|
|
24927
25504
|
executeCommand: buildExecuteCommand(projectFolder, wfm.filePath),
|
|
24928
25505
|
versionLabel: wfm.versionLabel,
|
|
24929
|
-
gitOperationStatus:
|
|
24930
|
-
gitBranchCreated:
|
|
24931
|
-
gitCommitHash:
|
|
25506
|
+
gitOperationStatus: git4?.gitOperationStatus ?? "not_started",
|
|
25507
|
+
gitBranchCreated: git4?.gitBranchCreated ?? null,
|
|
25508
|
+
gitCommitHash: git4?.gitCommitHash ?? null,
|
|
24932
25509
|
completedAt,
|
|
24933
25510
|
createdAt: wfm.createdAt
|
|
24934
25511
|
};
|
|
@@ -24949,7 +25526,7 @@ function importProject(db, workspacePath, input, userId) {
|
|
|
24949
25526
|
const nameTaken = db.select({ id: projects.id }).from(projects).where(eq(projects.name, name)).limit(1).get();
|
|
24950
25527
|
if (nameTaken) throw svcError15("PROJECT_EXISTS", 409);
|
|
24951
25528
|
const items = [];
|
|
24952
|
-
const report = (entity, action, label,
|
|
25529
|
+
const report = (entity, action, label, detail2) => items.push({ entity, action, label, ...detail2 ? { detail: detail2 } : {} });
|
|
24953
25530
|
const projectId = db.transaction((rawTx) => {
|
|
24954
25531
|
const tx = rawTx;
|
|
24955
25532
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -24962,6 +25539,9 @@ function importProject(db, workspacePath, input, userId) {
|
|
|
24962
25539
|
// bypasses the lazy brainstorm-rescan path
|
|
24963
25540
|
workflowPhase: scan.workflowPhase,
|
|
24964
25541
|
rawClaudeMd: scan.claudeMdRaw,
|
|
25542
|
+
gitRemoteUrl: scan.gitRemoteUrl ?? null,
|
|
25543
|
+
defaultBranch: scan.defaultBranch,
|
|
25544
|
+
gitProvider: scan.gitProvider,
|
|
24965
25545
|
importedAt: now,
|
|
24966
25546
|
importedBy: userId,
|
|
24967
25547
|
createdBy: userId,
|
|
@@ -25045,7 +25625,7 @@ function importProject(db, workspacePath, input, userId) {
|
|
|
25045
25625
|
snapshotPrdContent: JSON.stringify(prdSnapshot),
|
|
25046
25626
|
snapshotBugContent: JSON.stringify(bugSnapshot)
|
|
25047
25627
|
}).where(eq(prdVersions.id, current.id)).run();
|
|
25048
|
-
const
|
|
25628
|
+
const projectDir2 = join22(workspacePath, input.folderName);
|
|
25049
25629
|
for (const app of scan.apps) {
|
|
25050
25630
|
const appId = appIdByName.get(app.name);
|
|
25051
25631
|
const hasContextArtifacts = app.artifactCounts.erd + app.artifactCounts.data_model + app.artifactCounts.mockup + app.artifactCounts.technical_spec + app.artifactCounts.test_spec > 0;
|
|
@@ -25062,8 +25642,8 @@ function importProject(db, workspacePath, input, userId) {
|
|
|
25062
25642
|
const trackingPathFor = (type) => toPosix(
|
|
25063
25643
|
relative2(
|
|
25064
25644
|
workspacePath,
|
|
25065
|
-
|
|
25066
|
-
|
|
25645
|
+
join22(
|
|
25646
|
+
projectDir2,
|
|
25067
25647
|
app.folderRel,
|
|
25068
25648
|
type === "prepare" ? "context/prepare/PREPARE_MASTER.md" : type === "develop" ? "context/develop/IMPLEMENTATION_MASTER.md" : "context/bug/BUG_MASTER.md"
|
|
25069
25649
|
)
|
|
@@ -25288,7 +25868,7 @@ ${item.body}`.match(REQUIREMENT_ID)?.[0] ?? null;
|
|
|
25288
25868
|
}
|
|
25289
25869
|
|
|
25290
25870
|
// ../server/src/features/project-import/routes.ts
|
|
25291
|
-
function
|
|
25871
|
+
function errorResponse10(c, e) {
|
|
25292
25872
|
const err = e;
|
|
25293
25873
|
const status = err.status ?? 500;
|
|
25294
25874
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -25299,14 +25879,14 @@ function projectImportRoutes(db, workspacePath) {
|
|
|
25299
25879
|
try {
|
|
25300
25880
|
return c.json(listImportCandidates(db, workspacePath));
|
|
25301
25881
|
} catch (e) {
|
|
25302
|
-
return
|
|
25882
|
+
return errorResponse10(c, e);
|
|
25303
25883
|
}
|
|
25304
25884
|
});
|
|
25305
25885
|
app.post("/scan", requireRole("admin"), zValidator("json", scanImportRequestSchema), (c) => {
|
|
25306
25886
|
try {
|
|
25307
25887
|
return c.json(scanProject(db, workspacePath, c.req.valid("json").folderName));
|
|
25308
25888
|
} catch (e) {
|
|
25309
|
-
return
|
|
25889
|
+
return errorResponse10(c, e);
|
|
25310
25890
|
}
|
|
25311
25891
|
});
|
|
25312
25892
|
app.post("/", requireRole("admin"), zValidator("json", runImportRequestSchema), (c) => {
|
|
@@ -25314,14 +25894,14 @@ function projectImportRoutes(db, workspacePath) {
|
|
|
25314
25894
|
try {
|
|
25315
25895
|
return c.json(importProject(db, workspacePath, c.req.valid("json"), user.id), 201);
|
|
25316
25896
|
} catch (e) {
|
|
25317
|
-
return
|
|
25897
|
+
return errorResponse10(c, e);
|
|
25318
25898
|
}
|
|
25319
25899
|
});
|
|
25320
25900
|
return app;
|
|
25321
25901
|
}
|
|
25322
25902
|
|
|
25323
25903
|
// ../server/src/features/project/routes.ts
|
|
25324
|
-
function
|
|
25904
|
+
function errorResponse11(c, e) {
|
|
25325
25905
|
const err = e;
|
|
25326
25906
|
const status = err.status ?? 500;
|
|
25327
25907
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -25339,7 +25919,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25339
25919
|
const id = createProject(db, workspacePath, name, code, baseIdea, user.id);
|
|
25340
25920
|
return c.json({ id }, 201);
|
|
25341
25921
|
} catch (e) {
|
|
25342
|
-
return
|
|
25922
|
+
return errorResponse11(c, e);
|
|
25343
25923
|
}
|
|
25344
25924
|
});
|
|
25345
25925
|
app.get("/:id/detail", (c) => {
|
|
@@ -25348,7 +25928,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25348
25928
|
try {
|
|
25349
25929
|
return c.json(getProjectDetailSummary(db, id));
|
|
25350
25930
|
} catch (e) {
|
|
25351
|
-
return
|
|
25931
|
+
return errorResponse11(c, e);
|
|
25352
25932
|
}
|
|
25353
25933
|
});
|
|
25354
25934
|
app.post("/:id/scaffold", requireRole("admin"), (c) => {
|
|
@@ -25358,7 +25938,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25358
25938
|
scaffoldProject(db, workspacePath, id);
|
|
25359
25939
|
return c.json({ ok: true });
|
|
25360
25940
|
} catch (e) {
|
|
25361
|
-
return
|
|
25941
|
+
return errorResponse11(c, e);
|
|
25362
25942
|
}
|
|
25363
25943
|
});
|
|
25364
25944
|
app.get("/:id/brainstorm-md", (c) => {
|
|
@@ -25367,7 +25947,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25367
25947
|
try {
|
|
25368
25948
|
return c.json(getBrainstormMd(db, workspacePath, id));
|
|
25369
25949
|
} catch (e) {
|
|
25370
|
-
return
|
|
25950
|
+
return errorResponse11(c, e);
|
|
25371
25951
|
}
|
|
25372
25952
|
});
|
|
25373
25953
|
app.patch("/:id/brainstorm-md", zValidator("json", updateBrainstormMdSchema), (c) => {
|
|
@@ -25377,7 +25957,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25377
25957
|
updateBrainstormMd(db, workspacePath, id, c.req.valid("json").content);
|
|
25378
25958
|
return c.json({ ok: true });
|
|
25379
25959
|
} catch (e) {
|
|
25380
|
-
return
|
|
25960
|
+
return errorResponse11(c, e);
|
|
25381
25961
|
}
|
|
25382
25962
|
});
|
|
25383
25963
|
app.get("/:id", (c) => {
|
|
@@ -25386,7 +25966,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25386
25966
|
try {
|
|
25387
25967
|
return c.json(getProjectDetail(db, workspacePath, id));
|
|
25388
25968
|
} catch (e) {
|
|
25389
|
-
return
|
|
25969
|
+
return errorResponse11(c, e);
|
|
25390
25970
|
}
|
|
25391
25971
|
});
|
|
25392
25972
|
app.post("/:id/regenerate", (c) => {
|
|
@@ -25396,7 +25976,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25396
25976
|
regenerateCO2Files(db, workspacePath, id);
|
|
25397
25977
|
return c.json({ ok: true });
|
|
25398
25978
|
} catch (e) {
|
|
25399
|
-
return
|
|
25979
|
+
return errorResponse11(c, e);
|
|
25400
25980
|
}
|
|
25401
25981
|
});
|
|
25402
25982
|
app.post("/:id/rescan-claude-md", (c) => {
|
|
@@ -25406,7 +25986,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
25406
25986
|
const imported = rescanClaudeMd(db, workspacePath, id);
|
|
25407
25987
|
return c.json({ ok: true, imported });
|
|
25408
25988
|
} catch (e) {
|
|
25409
|
-
return
|
|
25989
|
+
return errorResponse11(c, e);
|
|
25410
25990
|
}
|
|
25411
25991
|
});
|
|
25412
25992
|
app.patch("/:id/archive", requireRole("admin"), (c) => {
|
|
@@ -25417,15 +25997,29 @@ function projectRoutes(db, workspacePath) {
|
|
|
25417
25997
|
archiveProject(db, id, user.id);
|
|
25418
25998
|
return c.json({ ok: true });
|
|
25419
25999
|
} catch (e) {
|
|
25420
|
-
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);
|
|
25421
26015
|
}
|
|
25422
26016
|
});
|
|
25423
26017
|
return app;
|
|
25424
26018
|
}
|
|
25425
26019
|
|
|
25426
26020
|
// ../server/src/features/shared-context/service.ts
|
|
25427
|
-
import { existsSync as
|
|
25428
|
-
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";
|
|
25429
26023
|
function svcError16(code, status) {
|
|
25430
26024
|
return Object.assign(new Error(code), { code, status });
|
|
25431
26025
|
}
|
|
@@ -25451,7 +26045,7 @@ function mimeForName(name) {
|
|
|
25451
26045
|
function resolveSharedRoot(db, workspacePath, projectId) {
|
|
25452
26046
|
const project = db.select().from(projects).where(eq(projects.id, projectId)).limit(1).get();
|
|
25453
26047
|
if (!project) throw svcError16("NOT_FOUND", 404);
|
|
25454
|
-
const root =
|
|
26048
|
+
const root = join23(workspacePath, project.workspaceFolderName, "shared_context");
|
|
25455
26049
|
mkdirSync10(root, { recursive: true });
|
|
25456
26050
|
return root;
|
|
25457
26051
|
}
|
|
@@ -25465,9 +26059,9 @@ function safeResolve(root, relPath) {
|
|
|
25465
26059
|
return full;
|
|
25466
26060
|
}
|
|
25467
26061
|
function sanitizeSegment(name) {
|
|
25468
|
-
const
|
|
25469
|
-
if (!
|
|
25470
|
-
return
|
|
26062
|
+
const safe2 = name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
26063
|
+
if (!safe2 || safe2 === "." || safe2 === "..") throw svcError16("INVALID_NAME", 400);
|
|
26064
|
+
return safe2;
|
|
25471
26065
|
}
|
|
25472
26066
|
function walk(dir, relBase) {
|
|
25473
26067
|
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
@@ -25479,10 +26073,10 @@ function walk(dir, relBase) {
|
|
|
25479
26073
|
name: entry.name,
|
|
25480
26074
|
path: rel,
|
|
25481
26075
|
type: "folder",
|
|
25482
|
-
children: walk(
|
|
26076
|
+
children: walk(join23(dir, entry.name), rel)
|
|
25483
26077
|
});
|
|
25484
26078
|
} else if (entry.isFile()) {
|
|
25485
|
-
const stat = statSync6(
|
|
26079
|
+
const stat = statSync6(join23(dir, entry.name));
|
|
25486
26080
|
nodes.push({
|
|
25487
26081
|
name: entry.name,
|
|
25488
26082
|
path: rel,
|
|
@@ -25513,28 +26107,28 @@ function uploadFile(db, workspacePath, projectId, folder, file) {
|
|
|
25513
26107
|
if (file.bytes.length > MAX_DOCUMENT_BYTES) throw svcError16("FILE_TOO_LARGE", 413);
|
|
25514
26108
|
const root = resolveSharedRoot(db, workspacePath, projectId);
|
|
25515
26109
|
const dir = folder ? safeResolve(root, folder) : root;
|
|
25516
|
-
if (folder && !
|
|
26110
|
+
if (folder && !existsSync20(dir)) throw svcError16("FOLDER_NOT_FOUND", 404);
|
|
25517
26111
|
const safeName = sanitizeSegment(file.name);
|
|
25518
|
-
|
|
26112
|
+
writeFileSync12(join23(dir, safeName), file.bytes);
|
|
25519
26113
|
return { path: folder ? `${folder}/${safeName}` : safeName };
|
|
25520
26114
|
}
|
|
25521
26115
|
function getFileContent(db, workspacePath, projectId, relPath) {
|
|
25522
26116
|
const root = resolveSharedRoot(db, workspacePath, projectId);
|
|
25523
26117
|
const full = safeResolve(root, relPath);
|
|
25524
|
-
if (!
|
|
26118
|
+
if (!existsSync20(full) || !statSync6(full).isFile()) throw svcError16("NOT_FOUND", 404);
|
|
25525
26119
|
const name = relPath.split("/").pop() ?? relPath;
|
|
25526
|
-
return { mimeType: mimeForName(name), bytes:
|
|
26120
|
+
return { mimeType: mimeForName(name), bytes: readFileSync15(full), name };
|
|
25527
26121
|
}
|
|
25528
26122
|
function saveMarkdown(db, workspacePath, projectId, relPath, content) {
|
|
25529
26123
|
if (mimeForName(relPath) !== "text/markdown") throw svcError16("NOT_MARKDOWN", 400);
|
|
25530
26124
|
const root = resolveSharedRoot(db, workspacePath, projectId);
|
|
25531
26125
|
const full = safeResolve(root, relPath);
|
|
25532
|
-
if (!
|
|
25533
|
-
|
|
26126
|
+
if (!existsSync20(full) || !statSync6(full).isFile()) throw svcError16("NOT_FOUND", 404);
|
|
26127
|
+
writeFileSync12(full, content, "utf8");
|
|
25534
26128
|
}
|
|
25535
26129
|
|
|
25536
26130
|
// ../server/src/features/shared-context/routes.ts
|
|
25537
|
-
function
|
|
26131
|
+
function errorResponse12(c, e) {
|
|
25538
26132
|
const err = e;
|
|
25539
26133
|
const status = err.status ?? 500;
|
|
25540
26134
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -25550,7 +26144,7 @@ function sharedContextRoutes(db, workspacePath) {
|
|
|
25550
26144
|
try {
|
|
25551
26145
|
return c.json({ tree: listTree(db, workspacePath, projectId) });
|
|
25552
26146
|
} catch (e) {
|
|
25553
|
-
return
|
|
26147
|
+
return errorResponse12(c, e);
|
|
25554
26148
|
}
|
|
25555
26149
|
});
|
|
25556
26150
|
app.post("/:projectId/folders", async (c) => {
|
|
@@ -25561,7 +26155,7 @@ function sharedContextRoutes(db, workspacePath) {
|
|
|
25561
26155
|
try {
|
|
25562
26156
|
return c.json({ path: createFolder(db, workspacePath, projectId, body.path) });
|
|
25563
26157
|
} catch (e) {
|
|
25564
|
-
return
|
|
26158
|
+
return errorResponse12(c, e);
|
|
25565
26159
|
}
|
|
25566
26160
|
});
|
|
25567
26161
|
app.post("/:projectId/upload", async (c) => {
|
|
@@ -25576,7 +26170,7 @@ function sharedContextRoutes(db, workspacePath) {
|
|
|
25576
26170
|
const { path } = uploadFile(db, workspacePath, projectId, folder, { name: file.name, bytes });
|
|
25577
26171
|
return c.json({ path });
|
|
25578
26172
|
} catch (e) {
|
|
25579
|
-
return
|
|
26173
|
+
return errorResponse12(c, e);
|
|
25580
26174
|
}
|
|
25581
26175
|
});
|
|
25582
26176
|
app.get("/:projectId/content", (c) => {
|
|
@@ -25595,7 +26189,7 @@ function sharedContextRoutes(db, workspacePath) {
|
|
|
25595
26189
|
}
|
|
25596
26190
|
});
|
|
25597
26191
|
} catch (e) {
|
|
25598
|
-
return
|
|
26192
|
+
return errorResponse12(c, e);
|
|
25599
26193
|
}
|
|
25600
26194
|
});
|
|
25601
26195
|
app.put("/:projectId/content", async (c) => {
|
|
@@ -25609,7 +26203,7 @@ function sharedContextRoutes(db, workspacePath) {
|
|
|
25609
26203
|
saveMarkdown(db, workspacePath, projectId, body.path, body.content);
|
|
25610
26204
|
return c.json({ ok: true });
|
|
25611
26205
|
} catch (e) {
|
|
25612
|
-
return
|
|
26206
|
+
return errorResponse12(c, e);
|
|
25613
26207
|
}
|
|
25614
26208
|
});
|
|
25615
26209
|
return app;
|
|
@@ -25674,8 +26268,8 @@ function svcError17(code, status) {
|
|
|
25674
26268
|
return Object.assign(new Error(code), { code, status });
|
|
25675
26269
|
}
|
|
25676
26270
|
function toSafe(user) {
|
|
25677
|
-
const { passwordHash: _omit, ...
|
|
25678
|
-
return
|
|
26271
|
+
const { passwordHash: _omit, ...safe2 } = user;
|
|
26272
|
+
return safe2;
|
|
25679
26273
|
}
|
|
25680
26274
|
function loginUser(db, input, clientIp) {
|
|
25681
26275
|
const rateLimitKey = `${input.username}:${clientIp}`;
|
|
@@ -25752,8 +26346,8 @@ function getUserDetail(db, id) {
|
|
|
25752
26346
|
const admin = db.select({ displayName: users.displayName }).from(users).where(eq(users.id, user.reactivatedBy)).limit(1).get();
|
|
25753
26347
|
reactivatedByName = admin?.displayName ?? null;
|
|
25754
26348
|
}
|
|
25755
|
-
const { passwordHash: _omit, ...
|
|
25756
|
-
return { ...
|
|
26349
|
+
const { passwordHash: _omit, ...safe2 } = user;
|
|
26350
|
+
return { ...safe2, reactivatedByName };
|
|
25757
26351
|
}
|
|
25758
26352
|
function adminUpdateUser(db, targetId, input) {
|
|
25759
26353
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -25814,7 +26408,7 @@ function listAssignments(db, userId) {
|
|
|
25814
26408
|
|
|
25815
26409
|
// ../server/src/features/user/auth-routes.ts
|
|
25816
26410
|
var SESSION_MAX_AGE = 30 * 24 * 60 * 60;
|
|
25817
|
-
function
|
|
26411
|
+
function errorResponse13(c, e) {
|
|
25818
26412
|
const err = e;
|
|
25819
26413
|
const status = err.status ?? 500;
|
|
25820
26414
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -25835,7 +26429,7 @@ function authRoutes(db) {
|
|
|
25835
26429
|
});
|
|
25836
26430
|
return c.json({ mustChangePassword });
|
|
25837
26431
|
} catch (e) {
|
|
25838
|
-
return
|
|
26432
|
+
return errorResponse13(c, e);
|
|
25839
26433
|
}
|
|
25840
26434
|
});
|
|
25841
26435
|
app.post("/logout", (c) => {
|
|
@@ -25859,14 +26453,14 @@ function authRoutes(db) {
|
|
|
25859
26453
|
});
|
|
25860
26454
|
return c.json({ ok: true });
|
|
25861
26455
|
} catch (e) {
|
|
25862
|
-
return
|
|
26456
|
+
return errorResponse13(c, e);
|
|
25863
26457
|
}
|
|
25864
26458
|
});
|
|
25865
26459
|
app.get("/me", (c) => {
|
|
25866
26460
|
const user = c.get("user");
|
|
25867
26461
|
if (!user) return c.json({ code: "UNAUTHORIZED" }, 401);
|
|
25868
|
-
const { passwordHash: _omit, ...
|
|
25869
|
-
return c.json(
|
|
26462
|
+
const { passwordHash: _omit, ...safe2 } = user;
|
|
26463
|
+
return c.json(safe2);
|
|
25870
26464
|
});
|
|
25871
26465
|
app.patch("/me", zValidator("json", updateProfileSchema), (c) => {
|
|
25872
26466
|
const user = c.get("user");
|
|
@@ -25878,7 +26472,7 @@ function authRoutes(db) {
|
|
|
25878
26472
|
}
|
|
25879
26473
|
|
|
25880
26474
|
// ../server/src/features/user/routes.ts
|
|
25881
|
-
function
|
|
26475
|
+
function errorResponse14(c, e) {
|
|
25882
26476
|
const err = e;
|
|
25883
26477
|
const status = err.status ?? 500;
|
|
25884
26478
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -25892,7 +26486,7 @@ function userRoutes(db) {
|
|
|
25892
26486
|
const id = createUser(db, c.req.valid("json"));
|
|
25893
26487
|
return c.json({ id }, 201);
|
|
25894
26488
|
} catch (e) {
|
|
25895
|
-
return
|
|
26489
|
+
return errorResponse14(c, e);
|
|
25896
26490
|
}
|
|
25897
26491
|
});
|
|
25898
26492
|
app.get("/:id", requireRole("admin"), (c) => {
|
|
@@ -25916,7 +26510,7 @@ function userRoutes(db) {
|
|
|
25916
26510
|
deactivateUser(db, id, admin.id);
|
|
25917
26511
|
return c.json({ ok: true });
|
|
25918
26512
|
} catch (e) {
|
|
25919
|
-
return
|
|
26513
|
+
return errorResponse14(c, e);
|
|
25920
26514
|
}
|
|
25921
26515
|
});
|
|
25922
26516
|
app.patch(
|
|
@@ -25938,7 +26532,7 @@ function userRoutes(db) {
|
|
|
25938
26532
|
reactivateUser(db, id, admin.id);
|
|
25939
26533
|
return c.json({ ok: true });
|
|
25940
26534
|
} catch (e) {
|
|
25941
|
-
return
|
|
26535
|
+
return errorResponse14(c, e);
|
|
25942
26536
|
}
|
|
25943
26537
|
});
|
|
25944
26538
|
app.get("/:id/project-assignments", requireRole("admin"), (c) => {
|
|
@@ -25958,7 +26552,7 @@ function userRoutes(db) {
|
|
|
25958
26552
|
const assignmentId = assignProject(db, id, c.req.valid("json").projectId, admin.id);
|
|
25959
26553
|
return c.json({ id: assignmentId }, 201);
|
|
25960
26554
|
} catch (e) {
|
|
25961
|
-
return
|
|
26555
|
+
return errorResponse14(c, e);
|
|
25962
26556
|
}
|
|
25963
26557
|
}
|
|
25964
26558
|
);
|
|
@@ -25973,7 +26567,7 @@ function userRoutes(db) {
|
|
|
25973
26567
|
}
|
|
25974
26568
|
|
|
25975
26569
|
// ../server/src/features/version/routes.ts
|
|
25976
|
-
function
|
|
26570
|
+
function errorResponse15(c, e) {
|
|
25977
26571
|
const err = e;
|
|
25978
26572
|
const status = err.status ?? 500;
|
|
25979
26573
|
const message = e instanceof Error && err.code ? e.message : void 0;
|
|
@@ -25988,7 +26582,7 @@ function versionRoutes(db, workspacePath) {
|
|
|
25988
26582
|
try {
|
|
25989
26583
|
return c.json(listVersions(db, projectId, user.id));
|
|
25990
26584
|
} catch (e) {
|
|
25991
|
-
return
|
|
26585
|
+
return errorResponse15(c, e);
|
|
25992
26586
|
}
|
|
25993
26587
|
});
|
|
25994
26588
|
app.get("/:projectId/current", (c) => {
|
|
@@ -25998,7 +26592,7 @@ function versionRoutes(db, workspacePath) {
|
|
|
25998
26592
|
try {
|
|
25999
26593
|
return c.json(getCurrentVersion(db, projectId, user.id));
|
|
26000
26594
|
} catch (e) {
|
|
26001
|
-
return
|
|
26595
|
+
return errorResponse15(c, e);
|
|
26002
26596
|
}
|
|
26003
26597
|
});
|
|
26004
26598
|
app.get("/:projectId/can-add", (c) => {
|
|
@@ -26014,14 +26608,14 @@ function versionRoutes(db, workspacePath) {
|
|
|
26014
26608
|
const result = addVersion(db, projectId, c.req.valid("json").versionType, user.id, workspacePath);
|
|
26015
26609
|
return c.json(result);
|
|
26016
26610
|
} catch (e) {
|
|
26017
|
-
return
|
|
26611
|
+
return errorResponse15(c, e);
|
|
26018
26612
|
}
|
|
26019
26613
|
});
|
|
26020
26614
|
return app;
|
|
26021
26615
|
}
|
|
26022
26616
|
|
|
26023
26617
|
// ../server/src/features/workflow/routes.ts
|
|
26024
|
-
function
|
|
26618
|
+
function errorResponse16(c, e) {
|
|
26025
26619
|
const err = e;
|
|
26026
26620
|
const status = err.status ?? 500;
|
|
26027
26621
|
return c.json({ code: err.code ?? "INTERNAL_ERROR" }, status);
|
|
@@ -26033,7 +26627,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26033
26627
|
try {
|
|
26034
26628
|
return c.json(getPendingApps(db, projectId, conductorType, versionId));
|
|
26035
26629
|
} catch (e) {
|
|
26036
|
-
return
|
|
26630
|
+
return errorResponse16(c, e);
|
|
26037
26631
|
}
|
|
26038
26632
|
});
|
|
26039
26633
|
app.get("/phase-completion", zValidator("query", pendingAppsQuerySchema), (c) => {
|
|
@@ -26041,7 +26635,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26041
26635
|
try {
|
|
26042
26636
|
return c.json(getPhaseCompletion(db, projectId, conductorType, versionId));
|
|
26043
26637
|
} catch (e) {
|
|
26044
|
-
return
|
|
26638
|
+
return errorResponse16(c, e);
|
|
26045
26639
|
}
|
|
26046
26640
|
});
|
|
26047
26641
|
app.get("/runs", (c) => {
|
|
@@ -26052,7 +26646,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26052
26646
|
resumeWatchers(db, workspacePath, projectId);
|
|
26053
26647
|
return c.json(listWorkflowRuns(db, projectId));
|
|
26054
26648
|
} catch (e) {
|
|
26055
|
-
return
|
|
26649
|
+
return errorResponse16(c, e);
|
|
26056
26650
|
}
|
|
26057
26651
|
});
|
|
26058
26652
|
app.get("/active-run", (c) => {
|
|
@@ -26062,7 +26656,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26062
26656
|
resumeWatchers(db, workspacePath, projectId);
|
|
26063
26657
|
return c.json(getActiveRun(db, projectId, workspacePath));
|
|
26064
26658
|
} catch (e) {
|
|
26065
|
-
return
|
|
26659
|
+
return errorResponse16(c, e);
|
|
26066
26660
|
}
|
|
26067
26661
|
});
|
|
26068
26662
|
app.get("/runs/:id", (c) => {
|
|
@@ -26071,7 +26665,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26071
26665
|
try {
|
|
26072
26666
|
return c.json(getWorkflowRun(db, id, workspacePath));
|
|
26073
26667
|
} catch (e) {
|
|
26074
|
-
return
|
|
26668
|
+
return errorResponse16(c, e);
|
|
26075
26669
|
}
|
|
26076
26670
|
});
|
|
26077
26671
|
app.get("/runs/:id/file-content", (c) => {
|
|
@@ -26080,7 +26674,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26080
26674
|
try {
|
|
26081
26675
|
return c.json(getWorkflowRunFileContent(db, workspacePath, id));
|
|
26082
26676
|
} catch (e) {
|
|
26083
|
-
return
|
|
26677
|
+
return errorResponse16(c, e);
|
|
26084
26678
|
}
|
|
26085
26679
|
});
|
|
26086
26680
|
app.get("/runs/:id/changelog", (c) => {
|
|
@@ -26091,7 +26685,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26091
26685
|
try {
|
|
26092
26686
|
return c.json(getRunApplicationChangelog(db, workspacePath, id, appName));
|
|
26093
26687
|
} catch (e) {
|
|
26094
|
-
return
|
|
26688
|
+
return errorResponse16(c, e);
|
|
26095
26689
|
}
|
|
26096
26690
|
});
|
|
26097
26691
|
app.post("/runs/:id/force-stop", (c) => {
|
|
@@ -26101,7 +26695,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26101
26695
|
forceStopWorkflowRun(db, id);
|
|
26102
26696
|
return c.json({ ok: true });
|
|
26103
26697
|
} catch (e) {
|
|
26104
|
-
return
|
|
26698
|
+
return errorResponse16(c, e);
|
|
26105
26699
|
}
|
|
26106
26700
|
});
|
|
26107
26701
|
app.post("/runs/:id/rescan", (c) => {
|
|
@@ -26110,7 +26704,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26110
26704
|
try {
|
|
26111
26705
|
return c.json(rescanWorkflowRun(db, workspacePath, id));
|
|
26112
26706
|
} catch (e) {
|
|
26113
|
-
return
|
|
26707
|
+
return errorResponse16(c, e);
|
|
26114
26708
|
}
|
|
26115
26709
|
});
|
|
26116
26710
|
app.post("/runs", zValidator("json", createWorkflowSchema), (c) => {
|
|
@@ -26122,7 +26716,7 @@ function workflowRoutes(db, workspacePath) {
|
|
|
26122
26716
|
});
|
|
26123
26717
|
return c.json(result, 201);
|
|
26124
26718
|
} catch (e) {
|
|
26125
|
-
return
|
|
26719
|
+
return errorResponse16(c, e);
|
|
26126
26720
|
}
|
|
26127
26721
|
});
|
|
26128
26722
|
return app;
|
|
@@ -26262,11 +26856,11 @@ function closeTerminalSessions(projectId, reason) {
|
|
|
26262
26856
|
}
|
|
26263
26857
|
function buildPtyLaunch(workspacePath, folderName) {
|
|
26264
26858
|
const isWindows = process.platform === "win32";
|
|
26265
|
-
const
|
|
26859
|
+
const join30 = (a, b) => `${a.replace(/[\\/]+$/, "")}/${b}`;
|
|
26266
26860
|
return {
|
|
26267
26861
|
shell: isWindows ? "powershell.exe" : process.env["SHELL"] ?? "bash",
|
|
26268
26862
|
args: [],
|
|
26269
|
-
cwd:
|
|
26863
|
+
cwd: join30(workspacePath, folderName),
|
|
26270
26864
|
autoCommand: `${CLAUDE_COMMAND}\r`
|
|
26271
26865
|
};
|
|
26272
26866
|
}
|
|
@@ -26443,16 +27037,16 @@ function terminalRoutes(db) {
|
|
|
26443
27037
|
import { execSync } from "child_process";
|
|
26444
27038
|
|
|
26445
27039
|
// ../server/src/lib/claude-plugin.ts
|
|
26446
|
-
import { existsSync as
|
|
27040
|
+
import { existsSync as existsSync21, readFileSync as readFileSync16, readdirSync as readdirSync5 } from "fs";
|
|
26447
27041
|
import { homedir } from "os";
|
|
26448
|
-
import { join as
|
|
27042
|
+
import { join as join24 } from "path";
|
|
26449
27043
|
var PLUGIN_NAME = "co2-skills";
|
|
26450
|
-
function detectCo2SkillsPlugin(claudeDir =
|
|
27044
|
+
function detectCo2SkillsPlugin(claudeDir = join24(homedir(), ".claude")) {
|
|
26451
27045
|
return readRegistry(claudeDir) ?? readCacheDir(claudeDir);
|
|
26452
27046
|
}
|
|
26453
27047
|
function readRegistry(claudeDir) {
|
|
26454
27048
|
try {
|
|
26455
|
-
const raw2 =
|
|
27049
|
+
const raw2 = readFileSync16(join24(claudeDir, "plugins", "installed_plugins.json"), "utf8");
|
|
26456
27050
|
const json = JSON.parse(raw2);
|
|
26457
27051
|
for (const [key, entries] of Object.entries(json.plugins ?? {})) {
|
|
26458
27052
|
if (!key.startsWith(`${PLUGIN_NAME}@`)) continue;
|
|
@@ -26465,8 +27059,8 @@ function readRegistry(claudeDir) {
|
|
|
26465
27059
|
}
|
|
26466
27060
|
}
|
|
26467
27061
|
function readCacheDir(claudeDir) {
|
|
26468
|
-
const base =
|
|
26469
|
-
if (!
|
|
27062
|
+
const base = join24(claudeDir, "plugins", "cache", "co2", PLUGIN_NAME);
|
|
27063
|
+
if (!existsSync21(base)) return { installed: false };
|
|
26470
27064
|
try {
|
|
26471
27065
|
const versions = readdirSync5(base).filter((d) => /^\d+\.\d+/.test(d)).sort();
|
|
26472
27066
|
return { installed: true, version: versions.at(-1) };
|
|
@@ -26520,8 +27114,8 @@ function workspaceRoutes(_db, config, port, host, workspacePath) {
|
|
|
26520
27114
|
}
|
|
26521
27115
|
|
|
26522
27116
|
// ../server/src/lib/config.ts
|
|
26523
|
-
import { existsSync as
|
|
26524
|
-
import { join as
|
|
27117
|
+
import { existsSync as existsSync22, readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "fs";
|
|
27118
|
+
import { join as join25 } from "path";
|
|
26525
27119
|
var configSchema = external_exports.object({
|
|
26526
27120
|
port: external_exports.number().int().min(1).max(65535).optional(),
|
|
26527
27121
|
host: external_exports.string().optional(),
|
|
@@ -26529,16 +27123,16 @@ var configSchema = external_exports.object({
|
|
|
26529
27123
|
provisioningState: external_exports.enum(["pending", "complete"]).optional()
|
|
26530
27124
|
});
|
|
26531
27125
|
function loadConfig(dataDir) {
|
|
26532
|
-
const file =
|
|
26533
|
-
if (!
|
|
27126
|
+
const file = join25(dataDir, "config.json");
|
|
27127
|
+
if (!existsSync22(file)) return {};
|
|
26534
27128
|
try {
|
|
26535
|
-
return configSchema.parse(JSON.parse(
|
|
27129
|
+
return configSchema.parse(JSON.parse(readFileSync17(file, "utf8")));
|
|
26536
27130
|
} catch {
|
|
26537
27131
|
return {};
|
|
26538
27132
|
}
|
|
26539
27133
|
}
|
|
26540
27134
|
function saveConfig(dataDir, config) {
|
|
26541
|
-
|
|
27135
|
+
writeFileSync13(join25(dataDir, "config.json"), JSON.stringify(config, null, 2));
|
|
26542
27136
|
}
|
|
26543
27137
|
|
|
26544
27138
|
// ../server/src/lib/logger.ts
|
|
@@ -26566,8 +27160,8 @@ function loggerMiddleware() {
|
|
|
26566
27160
|
}
|
|
26567
27161
|
|
|
26568
27162
|
// ../server/src/lib/static.ts
|
|
26569
|
-
import { existsSync as
|
|
26570
|
-
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";
|
|
26571
27165
|
var MIME = {
|
|
26572
27166
|
".html": "text/html; charset=utf-8",
|
|
26573
27167
|
".js": "text/javascript; charset=utf-8",
|
|
@@ -26591,7 +27185,7 @@ function safeResolve2(rootDir, relPath) {
|
|
|
26591
27185
|
return target;
|
|
26592
27186
|
}
|
|
26593
27187
|
function serveSpa(staticDir) {
|
|
26594
|
-
const indexPath =
|
|
27188
|
+
const indexPath = join26(staticDir, "index.html");
|
|
26595
27189
|
return async (c, next) => {
|
|
26596
27190
|
if (c.req.method !== "GET" && c.req.method !== "HEAD") {
|
|
26597
27191
|
await next();
|
|
@@ -26604,14 +27198,14 @@ function serveSpa(staticDir) {
|
|
|
26604
27198
|
}
|
|
26605
27199
|
const rel = path === "/" ? "/index.html" : decodeURIComponent(path);
|
|
26606
27200
|
const filePath = safeResolve2(staticDir, rel);
|
|
26607
|
-
if (filePath &&
|
|
26608
|
-
const body =
|
|
27201
|
+
if (filePath && existsSync23(filePath) && statSync7(filePath).isFile()) {
|
|
27202
|
+
const body = readFileSync18(filePath);
|
|
26609
27203
|
return c.body(body, 200, {
|
|
26610
27204
|
"Content-Type": MIME[extname(filePath)] ?? "application/octet-stream"
|
|
26611
27205
|
});
|
|
26612
27206
|
}
|
|
26613
|
-
if (
|
|
26614
|
-
return c.body(
|
|
27207
|
+
if (existsSync23(indexPath)) {
|
|
27208
|
+
return c.body(readFileSync18(indexPath), 200, { "Content-Type": "text/html; charset=utf-8" });
|
|
26615
27209
|
}
|
|
26616
27210
|
await next();
|
|
26617
27211
|
};
|
|
@@ -26655,6 +27249,7 @@ function createApp(db, opts) {
|
|
|
26655
27249
|
api.route("/defects", defects);
|
|
26656
27250
|
api.route("/quality", defects);
|
|
26657
27251
|
api.route("/dashboard", dashboardRoutes(db));
|
|
27252
|
+
api.route("/git", gitRoutes(db, opts.workspacePath));
|
|
26658
27253
|
app.route("/api", api);
|
|
26659
27254
|
if (opts.staticDir) {
|
|
26660
27255
|
app.use("*", serveSpa(opts.staticDir));
|
|
@@ -26663,8 +27258,8 @@ function createApp(db, opts) {
|
|
|
26663
27258
|
}
|
|
26664
27259
|
|
|
26665
27260
|
// ../server/src/db/index.ts
|
|
26666
|
-
import { existsSync as
|
|
26667
|
-
import { dirname as dirname2, join as
|
|
27261
|
+
import { existsSync as existsSync24 } from "fs";
|
|
27262
|
+
import { dirname as dirname2, join as join27 } from "path";
|
|
26668
27263
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
26669
27264
|
import Database from "better-sqlite3";
|
|
26670
27265
|
|
|
@@ -26868,16 +27463,16 @@ function resolveMigrationsFolder() {
|
|
|
26868
27463
|
const here = dirname2(fileURLToPath2(import.meta.url));
|
|
26869
27464
|
const candidates = [
|
|
26870
27465
|
process.env["CO2_CLI_MIGRATIONS_DIR"],
|
|
26871
|
-
|
|
27466
|
+
join27(here, "../../drizzle"),
|
|
26872
27467
|
// source: src/db -> packages/server/drizzle
|
|
26873
|
-
|
|
27468
|
+
join27(here, "../drizzle"),
|
|
26874
27469
|
// bundled: cli/dist -> cli/drizzle
|
|
26875
|
-
|
|
27470
|
+
join27(here, "drizzle")
|
|
26876
27471
|
].filter((c) => Boolean(c));
|
|
26877
27472
|
for (const c of candidates) {
|
|
26878
|
-
if (
|
|
27473
|
+
if (existsSync24(join27(c, "meta", "_journal.json"))) return c;
|
|
26879
27474
|
}
|
|
26880
|
-
return
|
|
27475
|
+
return join27(here, "../../drizzle");
|
|
26881
27476
|
}
|
|
26882
27477
|
function openDb(dbPath, migrationsFolder = resolveMigrationsFolder()) {
|
|
26883
27478
|
const sqlite = new Database(dbPath);
|
|
@@ -26911,7 +27506,7 @@ function bootstrap(db) {
|
|
|
26911
27506
|
// ../server/src/lib/data-dir.ts
|
|
26912
27507
|
import { mkdirSync as mkdirSync11 } from "fs";
|
|
26913
27508
|
import { homedir as homedir2 } from "os";
|
|
26914
|
-
import { join as
|
|
27509
|
+
import { join as join28 } from "path";
|
|
26915
27510
|
function resolveDataDir(override) {
|
|
26916
27511
|
const dir = override ?? defaultDataDir();
|
|
26917
27512
|
mkdirSync11(dir, { recursive: true, mode: 448 });
|
|
@@ -26920,11 +27515,11 @@ function resolveDataDir(override) {
|
|
|
26920
27515
|
function defaultDataDir() {
|
|
26921
27516
|
switch (process.platform) {
|
|
26922
27517
|
case "darwin":
|
|
26923
|
-
return
|
|
27518
|
+
return join28(homedir2(), "Library", "Application Support", "co2-cli");
|
|
26924
27519
|
case "win32":
|
|
26925
|
-
return
|
|
27520
|
+
return join28(process.env["LOCALAPPDATA"] ?? join28(homedir2(), "AppData", "Local"), "co2-cli");
|
|
26926
27521
|
default:
|
|
26927
|
-
return
|
|
27522
|
+
return join28(process.env["XDG_DATA_HOME"] ?? join28(homedir2(), ".local", "share"), "co2-cli");
|
|
26928
27523
|
}
|
|
26929
27524
|
}
|
|
26930
27525
|
|
|
@@ -27578,8 +28173,8 @@ var startOptionsSchema = external_exports.object({
|
|
|
27578
28173
|
workspacePath: external_exports.string().optional()
|
|
27579
28174
|
});
|
|
27580
28175
|
function resolveStaticDir() {
|
|
27581
|
-
const dir =
|
|
27582
|
-
return
|
|
28176
|
+
const dir = join29(dirname3(fileURLToPath3(import.meta.url)), "../static");
|
|
28177
|
+
return existsSync25(join29(dir, "index.html")) ? dir : void 0;
|
|
27583
28178
|
}
|
|
27584
28179
|
function registerStart(program3, version2) {
|
|
27585
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) => {
|
|
@@ -27588,11 +28183,11 @@ function registerStart(program3, version2) {
|
|
|
27588
28183
|
const config = loadConfig(dataDir);
|
|
27589
28184
|
const port = opts.port ?? (process.env["APP_PORT"] ? Number(process.env["APP_PORT"]) : void 0) ?? config.port ?? 3001;
|
|
27590
28185
|
const host = opts.host ?? process.env["APP_HOST"] ?? config.host ?? "0.0.0.0";
|
|
27591
|
-
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");
|
|
27592
28187
|
if (opts.port !== void 0 || opts.workspacePath !== void 0) {
|
|
27593
28188
|
saveConfig(dataDir, { ...config, port, workspacePath });
|
|
27594
28189
|
}
|
|
27595
|
-
const db = openDb(
|
|
28190
|
+
const db = openDb(join29(dataDir, "app.db"));
|
|
27596
28191
|
const credentials = bootstrap(db);
|
|
27597
28192
|
const app = createApp(db, {
|
|
27598
28193
|
dataDir,
|