ai-project-manage-cli 3.0.17 → 3.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +262 -257
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { readFileSync as readFileSync10 } from "fs";
|
|
5
|
-
import { dirname as dirname2, join as
|
|
5
|
+
import { dirname as dirname2, join as join9 } from "path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
|
|
@@ -158,8 +158,8 @@ import { fileURLToPath } from "url";
|
|
|
158
158
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
159
159
|
var CLI_TEMPLATE_DIR = resolve(__dirname, "../template");
|
|
160
160
|
var WORKSPACE_APM_DIR = resolve(process.cwd(), ".apm");
|
|
161
|
-
function requirementWorkitemsDir(requirementId) {
|
|
162
|
-
return join2(
|
|
161
|
+
function requirementWorkitemsDir(requirementId, workspaceDir = WORKSPACE_APM_DIR) {
|
|
162
|
+
return join2(workspaceDir, "workitems", requirementId);
|
|
163
163
|
}
|
|
164
164
|
async function ensureLoggedConfig() {
|
|
165
165
|
const cfg = await ensureApmConfig();
|
|
@@ -227,10 +227,10 @@ async function runComment(requirementId, file, model) {
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
// src/commands/connect.ts
|
|
230
|
-
import { execSync } from "child_process";
|
|
231
230
|
import { randomUUID } from "crypto";
|
|
232
231
|
import WebSocket from "ws";
|
|
233
232
|
import { Agent } from "@cursor/sdk";
|
|
233
|
+
import { join as join5 } from "path";
|
|
234
234
|
|
|
235
235
|
// src/session-utils.ts
|
|
236
236
|
import { appendFileSync } from "fs";
|
|
@@ -278,17 +278,20 @@ var EventSession = class {
|
|
|
278
278
|
}
|
|
279
279
|
return;
|
|
280
280
|
}
|
|
281
|
-
this.events.push(
|
|
281
|
+
this.events.push(formatedEvent);
|
|
282
282
|
}
|
|
283
283
|
formatEvent(event) {
|
|
284
284
|
switch (event.type) {
|
|
285
285
|
case "assistant":
|
|
286
286
|
return {
|
|
287
287
|
type: "assistant",
|
|
288
|
-
content: event.message.content[0].text || ""
|
|
288
|
+
content: event.message.content[0].text || event.content || ""
|
|
289
289
|
};
|
|
290
290
|
case "thinking":
|
|
291
|
-
return {
|
|
291
|
+
return {
|
|
292
|
+
type: "thinking",
|
|
293
|
+
content: event.text || event.content || ""
|
|
294
|
+
};
|
|
292
295
|
case "tool_call":
|
|
293
296
|
return {
|
|
294
297
|
type: "tool_call",
|
|
@@ -329,6 +332,228 @@ var EventSession = class {
|
|
|
329
332
|
}
|
|
330
333
|
};
|
|
331
334
|
|
|
335
|
+
// src/commands/upload-artifact.ts
|
|
336
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
337
|
+
import { join as join3, relative, sep } from "path";
|
|
338
|
+
var EXCLUDED_RELATIVE_PATHS = /* @__PURE__ */ new Set([
|
|
339
|
+
"defect.xml",
|
|
340
|
+
"prd.md",
|
|
341
|
+
"requirement-status.yaml",
|
|
342
|
+
"reviews.xml",
|
|
343
|
+
"testcase.xml"
|
|
344
|
+
]);
|
|
345
|
+
function toPosixRelative(root, absoluteFile) {
|
|
346
|
+
return relative(root, absoluteFile).split(sep).join("/");
|
|
347
|
+
}
|
|
348
|
+
function artifactTagFromRelPath(relPosix) {
|
|
349
|
+
const parts = relPosix.split("/");
|
|
350
|
+
const fileName = parts[parts.length - 1] ?? relPosix;
|
|
351
|
+
if (parts.length > 1) {
|
|
352
|
+
return parts[0] ?? fileName;
|
|
353
|
+
}
|
|
354
|
+
const dot = fileName.lastIndexOf(".");
|
|
355
|
+
return dot > 0 ? fileName.slice(0, dot) : fileName;
|
|
356
|
+
}
|
|
357
|
+
function* walkMarkdownFiles(dir) {
|
|
358
|
+
const names = readdirSync2(dir);
|
|
359
|
+
for (const name of names) {
|
|
360
|
+
if (name.startsWith(".")) continue;
|
|
361
|
+
const full = join3(dir, name);
|
|
362
|
+
const st = statSync2(full);
|
|
363
|
+
if (st.isDirectory()) {
|
|
364
|
+
yield* walkMarkdownFiles(full);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
if (!st.isFile()) continue;
|
|
368
|
+
if (!name.toLowerCase().endsWith(".md")) continue;
|
|
369
|
+
yield full;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
async function deleteAllArtifactsForRequirement(api, requirementId) {
|
|
373
|
+
const pageSize = 100;
|
|
374
|
+
let page = 1;
|
|
375
|
+
const rows = [];
|
|
376
|
+
while (true) {
|
|
377
|
+
const batch = await api.requirementArtifact.list({
|
|
378
|
+
requirementId,
|
|
379
|
+
page,
|
|
380
|
+
pageSize
|
|
381
|
+
});
|
|
382
|
+
rows.push(...batch.items);
|
|
383
|
+
if (batch.total === 0 || rows.length >= batch.total) break;
|
|
384
|
+
page += 1;
|
|
385
|
+
}
|
|
386
|
+
for (const row of rows) {
|
|
387
|
+
await api.requirementArtifact.delete({ artifactId: row.id });
|
|
388
|
+
}
|
|
389
|
+
return rows.length;
|
|
390
|
+
}
|
|
391
|
+
async function runUploadArtifact(requirementId, workspaceDir) {
|
|
392
|
+
const cfg = await ensureLoggedConfig();
|
|
393
|
+
const api = createApmApiClient(cfg);
|
|
394
|
+
const root = requirementWorkitemsDir(requirementId, workspaceDir);
|
|
395
|
+
if (!existsSync2(root)) {
|
|
396
|
+
console.error(
|
|
397
|
+
`[apm] \u76EE\u5F55\u4E0D\u5B58\u5728: ${root}
|
|
398
|
+
\u8BF7\u5148\u6267\u884C: apm pull ${requirementId}`
|
|
399
|
+
);
|
|
400
|
+
process.exit(1);
|
|
401
|
+
}
|
|
402
|
+
const deleted = await deleteAllArtifactsForRequirement(api, requirementId);
|
|
403
|
+
console.log(`[apm] \u5DF2\u6E05\u7A7A\u9700\u6C42\u4EA7\u7269\u6587\u6863 ${deleted} \u6761`);
|
|
404
|
+
const paths = [...walkMarkdownFiles(root)];
|
|
405
|
+
let created = 0;
|
|
406
|
+
let skipped = 0;
|
|
407
|
+
for (const abs of paths) {
|
|
408
|
+
const relPosix = toPosixRelative(root, abs);
|
|
409
|
+
if (EXCLUDED_RELATIVE_PATHS.has(relPosix)) {
|
|
410
|
+
skipped += 1;
|
|
411
|
+
console.log(`[apm] \u8DF3\u8FC7\uFF08\u6392\u9664\u5217\u8868\uFF09: ${relPosix}`);
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
const content = readFileSync3(abs, "utf8");
|
|
415
|
+
const tag = artifactTagFromRelPath(relPosix);
|
|
416
|
+
await api.requirementArtifact.create({
|
|
417
|
+
requirementId,
|
|
418
|
+
tag,
|
|
419
|
+
fileName: relPosix,
|
|
420
|
+
content
|
|
421
|
+
});
|
|
422
|
+
created += 1;
|
|
423
|
+
console.log(`[apm] \u5DF2\u4E0A\u4F20\u4EA7\u7269: ${relPosix} (tag=${tag})`);
|
|
424
|
+
}
|
|
425
|
+
console.log(
|
|
426
|
+
`[apm] \u5B8C\u6210\uFF1A\u5220\u9664 ${deleted}\uFF0C\u65B0\u5EFA ${created}\uFF0C\u8DF3\u8FC7\uFF08\u6392\u9664\uFF09 ${skipped}\uFF0C\u5171\u626B\u63CF ${paths.length} \u4E2A Markdown \u6587\u4EF6`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/commands/pull.ts
|
|
431
|
+
import { writeFileSync as writeFileSync2 } from "fs";
|
|
432
|
+
import { join as join4 } from "path";
|
|
433
|
+
import { stringify as yamlStringify } from "yaml";
|
|
434
|
+
function valueToXmlContent(value) {
|
|
435
|
+
if (value === null || value === void 0) return "";
|
|
436
|
+
if (typeof value === "string") return xmlEscape(value);
|
|
437
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
438
|
+
return xmlEscape(String(value));
|
|
439
|
+
}
|
|
440
|
+
return `<![CDATA[${JSON.stringify(value)}]]>`;
|
|
441
|
+
}
|
|
442
|
+
function recordToXmlLines(record, indent) {
|
|
443
|
+
const lines = [];
|
|
444
|
+
for (const [rawKey, val] of Object.entries(record)) {
|
|
445
|
+
const key = /^[a-zA-Z_][\w.-]*$/.test(rawKey) ? rawKey : "field";
|
|
446
|
+
lines.push(`${indent}<${key}>${valueToXmlContent(val)}</${key}>`);
|
|
447
|
+
}
|
|
448
|
+
return lines;
|
|
449
|
+
}
|
|
450
|
+
function unknownArrayToXml(rootName, itemName, items) {
|
|
451
|
+
const lines = [`<${rootName}>`];
|
|
452
|
+
items.forEach((item, index) => {
|
|
453
|
+
if (item !== null && typeof item === "object" && !Array.isArray(item)) {
|
|
454
|
+
lines.push(` <${itemName} index="${index}">`);
|
|
455
|
+
lines.push(...recordToXmlLines(item, " "));
|
|
456
|
+
lines.push(` </${itemName}>`);
|
|
457
|
+
} else {
|
|
458
|
+
lines.push(
|
|
459
|
+
` <${itemName} index="${index}">${valueToXmlContent(
|
|
460
|
+
item
|
|
461
|
+
)}</${itemName}>`
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
lines.push(`</${rootName}>`, "");
|
|
466
|
+
return lines.join("\n");
|
|
467
|
+
}
|
|
468
|
+
function tasksForStatusYaml(tasks) {
|
|
469
|
+
return tasks.map((t) => ({
|
|
470
|
+
id: t.id,
|
|
471
|
+
title: t.title,
|
|
472
|
+
status: t.status,
|
|
473
|
+
executor: t.executorType
|
|
474
|
+
}));
|
|
475
|
+
}
|
|
476
|
+
function escapeForCdata(text) {
|
|
477
|
+
return text.replace(/\]\]>/g, "]]]]><![CDATA[>");
|
|
478
|
+
}
|
|
479
|
+
function defectsToXml(defects) {
|
|
480
|
+
const sorted = [...defects].sort(
|
|
481
|
+
(a, b) => new Date(a.createdAt ?? 0).getTime() - new Date(b.createdAt ?? 0).getTime()
|
|
482
|
+
);
|
|
483
|
+
const lines = ["<defects>"];
|
|
484
|
+
for (const d of sorted) {
|
|
485
|
+
lines.push(` <defect id="${xmlEscape(d.id)}">`);
|
|
486
|
+
lines.push(` <status>${xmlEscape(d.status)}</status>`);
|
|
487
|
+
lines.push(
|
|
488
|
+
` <current><![CDATA[${escapeForCdata(
|
|
489
|
+
d.currentState ?? ""
|
|
490
|
+
)}]]></current>`
|
|
491
|
+
);
|
|
492
|
+
lines.push(
|
|
493
|
+
` <expected><![CDATA[${escapeForCdata(
|
|
494
|
+
d.expectedEffect ?? ""
|
|
495
|
+
)}]]></expected>`
|
|
496
|
+
);
|
|
497
|
+
lines.push(` </defect>`);
|
|
498
|
+
}
|
|
499
|
+
lines.push("</defects>", "");
|
|
500
|
+
return lines.join("\n");
|
|
501
|
+
}
|
|
502
|
+
async function runPull(requirementId, workspaceDir) {
|
|
503
|
+
const cfg = await ensureLoggedConfig();
|
|
504
|
+
const api = createApmApiClient(cfg);
|
|
505
|
+
const data = await api.cliRequirements.pull({ requirementId });
|
|
506
|
+
const WORKITEMS_DIR = requirementWorkitemsDir(requirementId, workspaceDir);
|
|
507
|
+
await ensureDirExists(WORKITEMS_DIR);
|
|
508
|
+
const req2 = data.requirement;
|
|
509
|
+
const statusYaml = yamlStringify(
|
|
510
|
+
{
|
|
511
|
+
id: req2.id,
|
|
512
|
+
status: req2.status,
|
|
513
|
+
title: req2.title,
|
|
514
|
+
env: req2.envName || "",
|
|
515
|
+
tasks: tasksForStatusYaml(data.tasks ?? [])
|
|
516
|
+
},
|
|
517
|
+
{ lineWidth: 0 }
|
|
518
|
+
);
|
|
519
|
+
writeFileSync2(
|
|
520
|
+
join4(WORKITEMS_DIR, "requirement-status.yaml"),
|
|
521
|
+
statusYaml.endsWith("\n") ? statusYaml : `${statusYaml}
|
|
522
|
+
`,
|
|
523
|
+
"utf8"
|
|
524
|
+
);
|
|
525
|
+
writeFileSync2(join4(WORKITEMS_DIR, "prd.md"), req2.content || "", "utf8");
|
|
526
|
+
const reviews = data.reviews ?? [];
|
|
527
|
+
const reviewsXml = [
|
|
528
|
+
"<reviews>",
|
|
529
|
+
...reviews.map((r) => {
|
|
530
|
+
return [
|
|
531
|
+
` <review id="${xmlEscape(r.id)}">`,
|
|
532
|
+
` <model>${xmlEscape(r.model ?? "")}</model>`,
|
|
533
|
+
` <content>`,
|
|
534
|
+
`${xmlEscape(r.content ?? "")}`,
|
|
535
|
+
` </content>`,
|
|
536
|
+
` <reply>`,
|
|
537
|
+
`${xmlEscape(r.reply ?? "")}`,
|
|
538
|
+
` </reply>`,
|
|
539
|
+
" </review>"
|
|
540
|
+
].join("\n");
|
|
541
|
+
}),
|
|
542
|
+
"</reviews>",
|
|
543
|
+
""
|
|
544
|
+
].join("\n");
|
|
545
|
+
writeFileSync2(join4(WORKITEMS_DIR, "reviews.xml"), reviewsXml, "utf8");
|
|
546
|
+
const defectsXml = defectsToXml(data.defects ?? []);
|
|
547
|
+
writeFileSync2(join4(WORKITEMS_DIR, "defect.xml"), defectsXml, "utf8");
|
|
548
|
+
const testCasesXml = unknownArrayToXml(
|
|
549
|
+
"testcases",
|
|
550
|
+
"case",
|
|
551
|
+
data.testCases ?? []
|
|
552
|
+
);
|
|
553
|
+
writeFileSync2(join4(WORKITEMS_DIR, "testcase.xml"), testCasesXml, "utf8");
|
|
554
|
+
return WORKITEMS_DIR;
|
|
555
|
+
}
|
|
556
|
+
|
|
332
557
|
// src/commands/connect.ts
|
|
333
558
|
function runConnect(opts) {
|
|
334
559
|
void (async () => {
|
|
@@ -372,11 +597,10 @@ function runConnect(opts) {
|
|
|
372
597
|
return;
|
|
373
598
|
}
|
|
374
599
|
const payload = msg.payload;
|
|
600
|
+
const apmRoot = join5(payload.cwd, ".apm");
|
|
601
|
+
console.log("[apm] ROOT:", apmRoot);
|
|
375
602
|
try {
|
|
376
|
-
|
|
377
|
-
cwd: payload.cwd,
|
|
378
|
-
encoding: "utf8"
|
|
379
|
-
});
|
|
603
|
+
await runPull(payload.requirementId, apmRoot);
|
|
380
604
|
} catch (pullErr) {
|
|
381
605
|
console.error("[apm] apm pull \u5931\u8D25:", pullErr);
|
|
382
606
|
throw pullErr;
|
|
@@ -455,10 +679,7 @@ function runConnect(opts) {
|
|
|
455
679
|
console.log("[Done]");
|
|
456
680
|
session.writeToFile(payload.cwd, payload.requirementId, run.agentId);
|
|
457
681
|
try {
|
|
458
|
-
|
|
459
|
-
cwd: payload.cwd,
|
|
460
|
-
encoding: "utf8"
|
|
461
|
-
});
|
|
682
|
+
await runUploadArtifact(payload.requirementId, apmRoot);
|
|
462
683
|
} catch (pullErr) {
|
|
463
684
|
console.error("[apm] apm upload-artifact \u5931\u8D25:", pullErr);
|
|
464
685
|
throw pullErr;
|
|
@@ -478,17 +699,17 @@ function runConnect(opts) {
|
|
|
478
699
|
}
|
|
479
700
|
|
|
480
701
|
// src/commands/init.ts
|
|
481
|
-
import { join as
|
|
482
|
-
import { readFileSync as
|
|
702
|
+
import { join as join6 } from "path";
|
|
703
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
483
704
|
async function runInit(name) {
|
|
484
705
|
await ensureWorkspaceApmDirForInit();
|
|
485
706
|
await copyTemplateFiles(WORKSPACE_APM_DIR);
|
|
486
707
|
if (name) {
|
|
487
|
-
const apmConfigPath =
|
|
488
|
-
const config =
|
|
708
|
+
const apmConfigPath = join6(WORKSPACE_APM_DIR, "apm.config.json");
|
|
709
|
+
const config = readFileSync4(apmConfigPath, "utf8");
|
|
489
710
|
const configJson = JSON.parse(config);
|
|
490
711
|
configJson.name = name;
|
|
491
|
-
|
|
712
|
+
writeFileSync3(apmConfigPath, JSON.stringify(configJson, null, 2), "utf8");
|
|
492
713
|
}
|
|
493
714
|
}
|
|
494
715
|
|
|
@@ -539,13 +760,13 @@ async function runLogin(opts) {
|
|
|
539
760
|
|
|
540
761
|
// src/commands/branch.ts
|
|
541
762
|
import { execFile } from "child_process";
|
|
542
|
-
import { resolve as
|
|
763
|
+
import { resolve as resolve4 } from "path";
|
|
543
764
|
import { promisify } from "util";
|
|
544
765
|
var execFileAsync = promisify(execFile);
|
|
545
766
|
async function fetchBaselineBranchFromApi(requirementId, cwd) {
|
|
546
767
|
const cfg = await ensureLoggedConfig();
|
|
547
768
|
const api = createApmApiClient(cfg);
|
|
548
|
-
const workdirPath =
|
|
769
|
+
const workdirPath = resolve4(cwd);
|
|
549
770
|
const { baselineBranch } = await api.cliRequirements.branchBaseline({
|
|
550
771
|
requirementId,
|
|
551
772
|
workdirPath
|
|
@@ -675,134 +896,13 @@ async function runBranch(requirementId, options = {}) {
|
|
|
675
896
|
return branch;
|
|
676
897
|
}
|
|
677
898
|
|
|
678
|
-
// src/commands/pull.ts
|
|
679
|
-
import { writeFileSync as writeFileSync3 } from "fs";
|
|
680
|
-
import { join as join4 } from "path";
|
|
681
|
-
import { stringify as yamlStringify } from "yaml";
|
|
682
|
-
function valueToXmlContent(value) {
|
|
683
|
-
if (value === null || value === void 0) return "";
|
|
684
|
-
if (typeof value === "string") return xmlEscape(value);
|
|
685
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
686
|
-
return xmlEscape(String(value));
|
|
687
|
-
}
|
|
688
|
-
return `<![CDATA[${JSON.stringify(value)}]]>`;
|
|
689
|
-
}
|
|
690
|
-
function recordToXmlLines(record, indent) {
|
|
691
|
-
const lines = [];
|
|
692
|
-
for (const [rawKey, val] of Object.entries(record)) {
|
|
693
|
-
const key = /^[a-zA-Z_][\w.-]*$/.test(rawKey) ? rawKey : "field";
|
|
694
|
-
lines.push(`${indent}<${key}>${valueToXmlContent(val)}</${key}>`);
|
|
695
|
-
}
|
|
696
|
-
return lines;
|
|
697
|
-
}
|
|
698
|
-
function unknownArrayToXml(rootName, itemName, items) {
|
|
699
|
-
const lines = [`<${rootName}>`];
|
|
700
|
-
items.forEach((item, index) => {
|
|
701
|
-
if (item !== null && typeof item === "object" && !Array.isArray(item)) {
|
|
702
|
-
lines.push(` <${itemName} index="${index}">`);
|
|
703
|
-
lines.push(...recordToXmlLines(item, " "));
|
|
704
|
-
lines.push(` </${itemName}>`);
|
|
705
|
-
} else {
|
|
706
|
-
lines.push(
|
|
707
|
-
` <${itemName} index="${index}">${valueToXmlContent(
|
|
708
|
-
item
|
|
709
|
-
)}</${itemName}>`
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
});
|
|
713
|
-
lines.push(`</${rootName}>`, "");
|
|
714
|
-
return lines.join("\n");
|
|
715
|
-
}
|
|
716
|
-
function tasksForStatusYaml(tasks) {
|
|
717
|
-
return tasks.map((t) => ({
|
|
718
|
-
id: t.id,
|
|
719
|
-
title: t.title,
|
|
720
|
-
status: t.status,
|
|
721
|
-
executor: t.executorType
|
|
722
|
-
}));
|
|
723
|
-
}
|
|
724
|
-
function escapeForCdata(text) {
|
|
725
|
-
return text.replace(/\]\]>/g, "]]]]><![CDATA[>");
|
|
726
|
-
}
|
|
727
|
-
function defectsToXml(defects) {
|
|
728
|
-
const sorted = [...defects].sort(
|
|
729
|
-
(a, b) => new Date(a.createdAt ?? 0).getTime() - new Date(b.createdAt ?? 0).getTime()
|
|
730
|
-
);
|
|
731
|
-
const lines = ["<defects>"];
|
|
732
|
-
for (const d of sorted) {
|
|
733
|
-
lines.push(` <defect id="${xmlEscape(d.id)}">`);
|
|
734
|
-
lines.push(` <status>${xmlEscape(d.status)}</status>`);
|
|
735
|
-
lines.push(` <current><![CDATA[${escapeForCdata(d.currentState ?? "")}]]></current>`);
|
|
736
|
-
lines.push(
|
|
737
|
-
` <expected><![CDATA[${escapeForCdata(d.expectedEffect ?? "")}]]></expected>`
|
|
738
|
-
);
|
|
739
|
-
lines.push(` </defect>`);
|
|
740
|
-
}
|
|
741
|
-
lines.push("</defects>", "");
|
|
742
|
-
return lines.join("\n");
|
|
743
|
-
}
|
|
744
|
-
async function runPull(requirementId) {
|
|
745
|
-
const cfg = await ensureLoggedConfig();
|
|
746
|
-
const api = createApmApiClient(cfg);
|
|
747
|
-
const data = await api.cliRequirements.pull({ requirementId });
|
|
748
|
-
const WORKITEMS_DIR = requirementWorkitemsDir(requirementId);
|
|
749
|
-
await ensureDirExists(WORKITEMS_DIR);
|
|
750
|
-
const req2 = data.requirement;
|
|
751
|
-
const statusYaml = yamlStringify(
|
|
752
|
-
{
|
|
753
|
-
id: req2.id,
|
|
754
|
-
status: req2.status,
|
|
755
|
-
title: req2.title,
|
|
756
|
-
env: req2.envName || "",
|
|
757
|
-
tasks: tasksForStatusYaml(data.tasks ?? [])
|
|
758
|
-
},
|
|
759
|
-
{ lineWidth: 0 }
|
|
760
|
-
);
|
|
761
|
-
writeFileSync3(
|
|
762
|
-
join4(WORKITEMS_DIR, "requirement-status.yaml"),
|
|
763
|
-
statusYaml.endsWith("\n") ? statusYaml : `${statusYaml}
|
|
764
|
-
`,
|
|
765
|
-
"utf8"
|
|
766
|
-
);
|
|
767
|
-
writeFileSync3(join4(WORKITEMS_DIR, "prd.md"), req2.content || "", "utf8");
|
|
768
|
-
const reviews = data.reviews ?? [];
|
|
769
|
-
const reviewsXml = [
|
|
770
|
-
"<reviews>",
|
|
771
|
-
...reviews.map((r) => {
|
|
772
|
-
return [
|
|
773
|
-
` <review id="${xmlEscape(r.id)}">`,
|
|
774
|
-
` <model>${xmlEscape(r.model ?? "")}</model>`,
|
|
775
|
-
` <content>`,
|
|
776
|
-
`${xmlEscape(r.content ?? "")}`,
|
|
777
|
-
` </content>`,
|
|
778
|
-
` <reply>`,
|
|
779
|
-
`${xmlEscape(r.reply ?? "")}`,
|
|
780
|
-
` </reply>`,
|
|
781
|
-
" </review>"
|
|
782
|
-
].join("\n");
|
|
783
|
-
}),
|
|
784
|
-
"</reviews>",
|
|
785
|
-
""
|
|
786
|
-
].join("\n");
|
|
787
|
-
writeFileSync3(join4(WORKITEMS_DIR, "reviews.xml"), reviewsXml, "utf8");
|
|
788
|
-
const defectsXml = defectsToXml(data.defects ?? []);
|
|
789
|
-
writeFileSync3(join4(WORKITEMS_DIR, "defect.xml"), defectsXml, "utf8");
|
|
790
|
-
const testCasesXml = unknownArrayToXml(
|
|
791
|
-
"testcases",
|
|
792
|
-
"case",
|
|
793
|
-
data.testCases ?? []
|
|
794
|
-
);
|
|
795
|
-
writeFileSync3(join4(WORKITEMS_DIR, "testcase.xml"), testCasesXml, "utf8");
|
|
796
|
-
return WORKITEMS_DIR;
|
|
797
|
-
}
|
|
798
|
-
|
|
799
899
|
// src/commands/refine.ts
|
|
800
|
-
import { readFileSync as
|
|
801
|
-
import { join as
|
|
900
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
901
|
+
import { join as join7 } from "path";
|
|
802
902
|
async function runRefine(requirementId) {
|
|
803
903
|
const cfg = await ensureLoggedConfig();
|
|
804
|
-
const filePath =
|
|
805
|
-
const content =
|
|
904
|
+
const filePath = join7(WORKSPACE_APM_DIR, "workitems", requirementId, "prd.md");
|
|
905
|
+
const content = readFileSync5(filePath, "utf8");
|
|
806
906
|
const api = createApmApiClient(cfg);
|
|
807
907
|
const data = await api.cliRequirements.refine({ requirementId, content });
|
|
808
908
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -830,111 +930,16 @@ async function runUpdateStatus(requirementId, status) {
|
|
|
830
930
|
console.log(JSON.stringify(data, null, 2));
|
|
831
931
|
}
|
|
832
932
|
|
|
833
|
-
// src/commands/upload-artifact.ts
|
|
834
|
-
import { existsSync as existsSync2, readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
835
|
-
import { join as join6, relative, sep } from "path";
|
|
836
|
-
var EXCLUDED_RELATIVE_PATHS = /* @__PURE__ */ new Set([
|
|
837
|
-
"defect.xml",
|
|
838
|
-
"prd.md",
|
|
839
|
-
"requirement-status.yaml",
|
|
840
|
-
"reviews.xml",
|
|
841
|
-
"testcase.xml"
|
|
842
|
-
]);
|
|
843
|
-
function toPosixRelative(root, absoluteFile) {
|
|
844
|
-
return relative(root, absoluteFile).split(sep).join("/");
|
|
845
|
-
}
|
|
846
|
-
function artifactTagFromRelPath(relPosix) {
|
|
847
|
-
const parts = relPosix.split("/");
|
|
848
|
-
const fileName = parts[parts.length - 1] ?? relPosix;
|
|
849
|
-
if (parts.length > 1) {
|
|
850
|
-
return parts[0] ?? fileName;
|
|
851
|
-
}
|
|
852
|
-
const dot = fileName.lastIndexOf(".");
|
|
853
|
-
return dot > 0 ? fileName.slice(0, dot) : fileName;
|
|
854
|
-
}
|
|
855
|
-
function* walkMarkdownFiles(dir) {
|
|
856
|
-
const names = readdirSync2(dir);
|
|
857
|
-
for (const name of names) {
|
|
858
|
-
if (name.startsWith(".")) continue;
|
|
859
|
-
const full = join6(dir, name);
|
|
860
|
-
const st = statSync2(full);
|
|
861
|
-
if (st.isDirectory()) {
|
|
862
|
-
yield* walkMarkdownFiles(full);
|
|
863
|
-
continue;
|
|
864
|
-
}
|
|
865
|
-
if (!st.isFile()) continue;
|
|
866
|
-
if (!name.toLowerCase().endsWith(".md")) continue;
|
|
867
|
-
yield full;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
async function deleteAllArtifactsForRequirement(api, requirementId) {
|
|
871
|
-
const pageSize = 100;
|
|
872
|
-
let page = 1;
|
|
873
|
-
const rows = [];
|
|
874
|
-
while (true) {
|
|
875
|
-
const batch = await api.requirementArtifact.list({
|
|
876
|
-
requirementId,
|
|
877
|
-
page,
|
|
878
|
-
pageSize
|
|
879
|
-
});
|
|
880
|
-
rows.push(...batch.items);
|
|
881
|
-
if (batch.total === 0 || rows.length >= batch.total) break;
|
|
882
|
-
page += 1;
|
|
883
|
-
}
|
|
884
|
-
for (const row of rows) {
|
|
885
|
-
await api.requirementArtifact.delete({ artifactId: row.id });
|
|
886
|
-
}
|
|
887
|
-
return rows.length;
|
|
888
|
-
}
|
|
889
|
-
async function runUploadArtifact(requirementId) {
|
|
890
|
-
const cfg = await ensureLoggedConfig();
|
|
891
|
-
const api = createApmApiClient(cfg);
|
|
892
|
-
const root = requirementWorkitemsDir(requirementId);
|
|
893
|
-
if (!existsSync2(root)) {
|
|
894
|
-
console.error(
|
|
895
|
-
`[apm] \u76EE\u5F55\u4E0D\u5B58\u5728: ${root}
|
|
896
|
-
\u8BF7\u5148\u6267\u884C: apm pull ${requirementId}`
|
|
897
|
-
);
|
|
898
|
-
process.exit(1);
|
|
899
|
-
}
|
|
900
|
-
const deleted = await deleteAllArtifactsForRequirement(api, requirementId);
|
|
901
|
-
console.log(`[apm] \u5DF2\u6E05\u7A7A\u9700\u6C42\u4EA7\u7269\u6587\u6863 ${deleted} \u6761`);
|
|
902
|
-
const paths = [...walkMarkdownFiles(root)];
|
|
903
|
-
let created = 0;
|
|
904
|
-
let skipped = 0;
|
|
905
|
-
for (const abs of paths) {
|
|
906
|
-
const relPosix = toPosixRelative(root, abs);
|
|
907
|
-
if (EXCLUDED_RELATIVE_PATHS.has(relPosix)) {
|
|
908
|
-
skipped += 1;
|
|
909
|
-
console.log(`[apm] \u8DF3\u8FC7\uFF08\u6392\u9664\u5217\u8868\uFF09: ${relPosix}`);
|
|
910
|
-
continue;
|
|
911
|
-
}
|
|
912
|
-
const content = readFileSync5(abs, "utf8");
|
|
913
|
-
const tag = artifactTagFromRelPath(relPosix);
|
|
914
|
-
await api.requirementArtifact.create({
|
|
915
|
-
requirementId,
|
|
916
|
-
tag,
|
|
917
|
-
fileName: relPosix,
|
|
918
|
-
content
|
|
919
|
-
});
|
|
920
|
-
created += 1;
|
|
921
|
-
console.log(`[apm] \u5DF2\u4E0A\u4F20\u4EA7\u7269: ${relPosix} (tag=${tag})`);
|
|
922
|
-
}
|
|
923
|
-
console.log(
|
|
924
|
-
`[apm] \u5B8C\u6210\uFF1A\u5220\u9664 ${deleted}\uFF0C\u65B0\u5EFA ${created}\uFF0C\u8DF3\u8FC7\uFF08\u6392\u9664\uFF09 ${skipped}\uFF0C\u5171\u626B\u63CF ${paths.length} \u4E2A Markdown \u6587\u4EF6`
|
|
925
|
-
);
|
|
926
|
-
}
|
|
927
|
-
|
|
928
933
|
// src/commands/deploy/backend.ts
|
|
929
934
|
import path5 from "node:path";
|
|
930
935
|
|
|
931
936
|
// src/commands/deploy/lib/apm-config.ts
|
|
932
937
|
import { existsSync as existsSync3, readFileSync as readFileSync6 } from "node:fs";
|
|
933
|
-
import { resolve as
|
|
938
|
+
import { resolve as resolve5 } from "node:path";
|
|
934
939
|
function loadApmConfig(options) {
|
|
935
|
-
const p =
|
|
940
|
+
const p = resolve5(
|
|
936
941
|
process.cwd(),
|
|
937
|
-
options?.configPath ??
|
|
942
|
+
options?.configPath ?? resolve5(WORKSPACE_APM_DIR, "apm.config.json")
|
|
938
943
|
);
|
|
939
944
|
if (!existsSync3(p)) {
|
|
940
945
|
console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
|
|
@@ -1178,17 +1183,17 @@ var DockerodeClient = class {
|
|
|
1178
1183
|
await this.client.getImage(image).remove({ force: true });
|
|
1179
1184
|
}
|
|
1180
1185
|
async pullImage(image, auth) {
|
|
1181
|
-
const stream = await new Promise((
|
|
1186
|
+
const stream = await new Promise((resolve6, reject) => {
|
|
1182
1187
|
const pullOptions = auth ? { authconfig: auth } : void 0;
|
|
1183
1188
|
this.client.pull(image, pullOptions, (err, output) => {
|
|
1184
1189
|
if (err || !output) {
|
|
1185
1190
|
reject(err ?? new Error("docker pull \u8FD4\u56DE\u7A7A\u8F93\u51FA"));
|
|
1186
1191
|
return;
|
|
1187
1192
|
}
|
|
1188
|
-
|
|
1193
|
+
resolve6(output);
|
|
1189
1194
|
});
|
|
1190
1195
|
});
|
|
1191
|
-
await new Promise((
|
|
1196
|
+
await new Promise((resolve6, reject) => {
|
|
1192
1197
|
this.client.modem.followProgress(
|
|
1193
1198
|
stream,
|
|
1194
1199
|
(err) => {
|
|
@@ -1196,7 +1201,7 @@ var DockerodeClient = class {
|
|
|
1196
1201
|
reject(err);
|
|
1197
1202
|
return;
|
|
1198
1203
|
}
|
|
1199
|
-
|
|
1204
|
+
resolve6();
|
|
1200
1205
|
},
|
|
1201
1206
|
() => void 0
|
|
1202
1207
|
);
|
|
@@ -1341,7 +1346,7 @@ function assertDeployImageTag(tag) {
|
|
|
1341
1346
|
import { platform } from "node:os";
|
|
1342
1347
|
|
|
1343
1348
|
// src/commands/deploy/lib/backend-deploy/command-runner.ts
|
|
1344
|
-
import { execSync
|
|
1349
|
+
import { execSync } from "child_process";
|
|
1345
1350
|
|
|
1346
1351
|
// src/commands/deploy/lib/backend-deploy/logger.ts
|
|
1347
1352
|
var Logger = class {
|
|
@@ -1367,7 +1372,7 @@ var CommandRunner = class {
|
|
|
1367
1372
|
static exec(command, cwd) {
|
|
1368
1373
|
try {
|
|
1369
1374
|
Logger.info(`\u6267\u884C\u547D\u4EE4: ${command}`);
|
|
1370
|
-
const result =
|
|
1375
|
+
const result = execSync(command, {
|
|
1371
1376
|
cwd,
|
|
1372
1377
|
encoding: "utf8",
|
|
1373
1378
|
stdio: "pipe"
|
|
@@ -1385,7 +1390,7 @@ var CommandRunner = class {
|
|
|
1385
1390
|
static execWithOutput(command, cwd) {
|
|
1386
1391
|
try {
|
|
1387
1392
|
Logger.info(`\u6267\u884C\u547D\u4EE4: ${command}`);
|
|
1388
|
-
|
|
1393
|
+
execSync(command, {
|
|
1389
1394
|
cwd,
|
|
1390
1395
|
stdio: "inherit"
|
|
1391
1396
|
});
|
|
@@ -1579,9 +1584,9 @@ import path7 from "node:path";
|
|
|
1579
1584
|
|
|
1580
1585
|
// src/commands/deploy/lib/load-apm-dotenv.ts
|
|
1581
1586
|
import { existsSync as existsSync7, readFileSync as readFileSync9 } from "node:fs";
|
|
1582
|
-
import { join as
|
|
1587
|
+
import { join as join8 } from "node:path";
|
|
1583
1588
|
function loadApmDotEnvIfPresent() {
|
|
1584
|
-
const p =
|
|
1589
|
+
const p = join8(WORKSPACE_APM_DIR, ".env");
|
|
1585
1590
|
if (!existsSync7(p)) {
|
|
1586
1591
|
return;
|
|
1587
1592
|
}
|
|
@@ -1712,14 +1717,14 @@ var MinioClient = class {
|
|
|
1712
1717
|
async deleteObjectsByPrefix(bucket, prefix) {
|
|
1713
1718
|
const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
|
|
1714
1719
|
const keys = [];
|
|
1715
|
-
await new Promise((
|
|
1720
|
+
await new Promise((resolve6, reject) => {
|
|
1716
1721
|
objectsStream.on("data", (obj) => {
|
|
1717
1722
|
if (obj.name) {
|
|
1718
1723
|
keys.push(obj.name);
|
|
1719
1724
|
}
|
|
1720
1725
|
});
|
|
1721
1726
|
objectsStream.on("error", reject);
|
|
1722
|
-
objectsStream.on("end",
|
|
1727
|
+
objectsStream.on("end", resolve6);
|
|
1723
1728
|
});
|
|
1724
1729
|
const chunkSize = 500;
|
|
1725
1730
|
for (let i = 0; i < keys.length; i += chunkSize) {
|
|
@@ -1946,7 +1951,7 @@ function registerDeployCommands(program) {
|
|
|
1946
1951
|
function readCliVersion() {
|
|
1947
1952
|
try {
|
|
1948
1953
|
const dir = dirname2(fileURLToPath2(import.meta.url));
|
|
1949
|
-
const pkgPath =
|
|
1954
|
+
const pkgPath = join9(dir, "..", "package.json");
|
|
1950
1955
|
const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
|
|
1951
1956
|
return pkg.version ?? "0.0.0";
|
|
1952
1957
|
} catch {
|