ai-project-manage-cli 6.0.25 → 6.0.27
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 +204 -15
- package/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -525,13 +525,10 @@ function formatSessionMessagesXml(sessionId, messages) {
|
|
|
525
525
|
import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
526
526
|
import { join as join4 } from "path";
|
|
527
527
|
var MANIFEST_FILE = ".sync-manifest.json";
|
|
528
|
-
async function downloadAttachment(cfg,
|
|
528
|
+
async function downloadAttachment(cfg, attachmentId) {
|
|
529
529
|
const base = cfg.baseUrl.trim().replace(/\/+$/, "");
|
|
530
|
-
const
|
|
531
|
-
const
|
|
532
|
-
const res = await fetch(url, {
|
|
533
|
-
headers: { Authorization: `Bearer ${cfg.token}` }
|
|
534
|
-
});
|
|
530
|
+
const url = `${base}/api/v1/tasks/attachments/file?${new URLSearchParams({ attachmentId })}`;
|
|
531
|
+
const res = await fetch(url);
|
|
535
532
|
if (!res.ok) {
|
|
536
533
|
throw new Error(
|
|
537
534
|
`[apm] \u4E0B\u8F7D\u9644\u4EF6\u5931\u8D25 (${res.status}): attachmentId=${attachmentId}`
|
|
@@ -540,13 +537,13 @@ async function downloadAttachment(cfg, sessionId, attachmentId) {
|
|
|
540
537
|
return Buffer.from(await res.arrayBuffer());
|
|
541
538
|
}
|
|
542
539
|
function loadManifest(dir) {
|
|
543
|
-
const
|
|
544
|
-
if (!existsSync2(
|
|
540
|
+
const path10 = join4(dir, MANIFEST_FILE);
|
|
541
|
+
if (!existsSync2(path10)) {
|
|
545
542
|
return { version: 1, attachments: {} };
|
|
546
543
|
}
|
|
547
544
|
try {
|
|
548
545
|
const parsed = JSON.parse(
|
|
549
|
-
readFileSync3(
|
|
546
|
+
readFileSync3(path10, "utf8")
|
|
550
547
|
);
|
|
551
548
|
if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
|
|
552
549
|
return parsed;
|
|
@@ -589,7 +586,7 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
|
589
586
|
);
|
|
590
587
|
continue;
|
|
591
588
|
}
|
|
592
|
-
const buffer = await downloadAttachment(cfg,
|
|
589
|
+
const buffer = await downloadAttachment(cfg, item.id);
|
|
593
590
|
writeFileSync3(dest, buffer);
|
|
594
591
|
nextManifest.attachments[item.id] = {
|
|
595
592
|
name: item.name,
|
|
@@ -772,8 +769,8 @@ function sanitizeSkillDirName(name) {
|
|
|
772
769
|
function listBaseSkillDirNames() {
|
|
773
770
|
if (!existsSync3(BASE_SKILLS_TEMPLATE_DIR)) return [];
|
|
774
771
|
return readdirSync2(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
|
|
775
|
-
const
|
|
776
|
-
return statSync2(
|
|
772
|
+
const path10 = join7(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
773
|
+
return statSync2(path10).isDirectory();
|
|
777
774
|
});
|
|
778
775
|
}
|
|
779
776
|
function syncAgentsGuide(apmDir) {
|
|
@@ -785,8 +782,8 @@ function syncAgentsGuide(apmDir) {
|
|
|
785
782
|
function listBaseRuleFileNames() {
|
|
786
783
|
if (!existsSync3(BASE_RULES_TEMPLATE_DIR)) return [];
|
|
787
784
|
return readdirSync2(BASE_RULES_TEMPLATE_DIR).filter((name) => {
|
|
788
|
-
const
|
|
789
|
-
return statSync2(
|
|
785
|
+
const path10 = join7(BASE_RULES_TEMPLATE_DIR, name);
|
|
786
|
+
return statSync2(path10).isFile();
|
|
790
787
|
});
|
|
791
788
|
}
|
|
792
789
|
function syncBaseRules(rulesDir) {
|
|
@@ -1593,6 +1590,31 @@ function resolveFrontendDeployFromApmConfig(cfg) {
|
|
|
1593
1590
|
bucket: reqFe(f.bucket, "bucket").trim()
|
|
1594
1591
|
};
|
|
1595
1592
|
}
|
|
1593
|
+
function reqWd(v, field) {
|
|
1594
|
+
if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
|
|
1595
|
+
console.error(`apm.config.json \u4E2D wisdomDeploy.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
|
|
1596
|
+
process.exit(1);
|
|
1597
|
+
}
|
|
1598
|
+
return v;
|
|
1599
|
+
}
|
|
1600
|
+
function reqWisdomPositiveInt(v, field) {
|
|
1601
|
+
const n = Number(v);
|
|
1602
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
|
|
1603
|
+
console.error(`apm.config.json \u4E2D wisdomDeploy.${field} \u987B\u4E3A\u6B63\u6574\u6570`);
|
|
1604
|
+
process.exit(1);
|
|
1605
|
+
}
|
|
1606
|
+
return n;
|
|
1607
|
+
}
|
|
1608
|
+
function resolveWisdomDeployFromApmConfig(cfg) {
|
|
1609
|
+
const w = cfg.wisdomDeploy ?? {};
|
|
1610
|
+
return {
|
|
1611
|
+
host: reqWd(w.host, "host").trim(),
|
|
1612
|
+
port: reqWisdomPositiveInt(w.port, "port"),
|
|
1613
|
+
username: reqWd(w.username, "username").trim(),
|
|
1614
|
+
password: reqWd(w.password, "password").trim(),
|
|
1615
|
+
remotePath: reqWd(w.remotePath, "remotePath").trim()
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1596
1618
|
|
|
1597
1619
|
// src/commands/deploy/internal/backend-deploy/backend-deploy-workflow.ts
|
|
1598
1620
|
import path4 from "node:path";
|
|
@@ -2444,17 +2466,184 @@ function registerDeployFrontendCommands(program) {
|
|
|
2444
2466
|
);
|
|
2445
2467
|
}
|
|
2446
2468
|
|
|
2469
|
+
// src/commands/deploy/sftp.ts
|
|
2470
|
+
import path9 from "node:path";
|
|
2471
|
+
|
|
2472
|
+
// src/commands/deploy/internal/wisdom-sftp.ts
|
|
2473
|
+
import { readdir as readdir3, readFile as readFile2, unlink, writeFile } from "node:fs/promises";
|
|
2474
|
+
import path8 from "node:path";
|
|
2475
|
+
import JSZip from "jszip";
|
|
2476
|
+
import SftpClient from "ssh2-sftp-client";
|
|
2477
|
+
async function addDirToZip(dir, zipFolder) {
|
|
2478
|
+
const entries = await readdir3(dir, { withFileTypes: true });
|
|
2479
|
+
for (const entry of entries) {
|
|
2480
|
+
const fullPath = path8.join(dir, entry.name);
|
|
2481
|
+
if (entry.isDirectory()) {
|
|
2482
|
+
const folder = zipFolder.folder(entry.name);
|
|
2483
|
+
if (folder) {
|
|
2484
|
+
await addDirToZip(fullPath, folder);
|
|
2485
|
+
}
|
|
2486
|
+
} else {
|
|
2487
|
+
const content = await readFile2(fullPath);
|
|
2488
|
+
zipFolder.file(entry.name, content);
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
async function zipDirectory(distDir, zipPath) {
|
|
2493
|
+
console.error(`\u538B\u7F29\u76EE\u5F55: ${distDir}`);
|
|
2494
|
+
const zip = new JSZip();
|
|
2495
|
+
await addDirToZip(distDir, zip);
|
|
2496
|
+
const content = await zip.generateAsync({
|
|
2497
|
+
type: "nodebuffer",
|
|
2498
|
+
compression: "DEFLATE",
|
|
2499
|
+
compressionOptions: { level: 6 }
|
|
2500
|
+
});
|
|
2501
|
+
await writeFile(zipPath, content);
|
|
2502
|
+
const sizeMb = (content.length / 1024 / 1024).toFixed(2);
|
|
2503
|
+
console.error(`\u5DF2\u751F\u6210: ${zipPath} (${sizeMb} MB)`);
|
|
2504
|
+
return content.length;
|
|
2505
|
+
}
|
|
2506
|
+
async function ensureRemoteDir(sftp, dir) {
|
|
2507
|
+
const parts = dir.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
2508
|
+
let current = dir.startsWith("/") ? "" : ".";
|
|
2509
|
+
for (const part of parts) {
|
|
2510
|
+
current = current ? `${current}/${part}` : `/${part}`;
|
|
2511
|
+
try {
|
|
2512
|
+
await sftp.mkdir(current, true);
|
|
2513
|
+
} catch {
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
function execCommand(client, command) {
|
|
2518
|
+
return new Promise((resolve5, reject) => {
|
|
2519
|
+
client.exec(command, (err, stream) => {
|
|
2520
|
+
if (err) return reject(err);
|
|
2521
|
+
let stdout = "";
|
|
2522
|
+
let stderr = "";
|
|
2523
|
+
stream.on("close", (code) => {
|
|
2524
|
+
if (code !== 0) {
|
|
2525
|
+
reject(new Error(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (${code}): ${stderr || stdout}`));
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
resolve5(stdout);
|
|
2529
|
+
}).on("data", (data) => {
|
|
2530
|
+
stdout += data.toString();
|
|
2531
|
+
});
|
|
2532
|
+
stream.stderr.on("data", (data) => {
|
|
2533
|
+
stderr += data.toString();
|
|
2534
|
+
});
|
|
2535
|
+
});
|
|
2536
|
+
});
|
|
2537
|
+
}
|
|
2538
|
+
async function uploadAndMaybeExtract(settings, localZip, extract) {
|
|
2539
|
+
const remoteZipPath = `${settings.remotePath.replace(/\/$/, "")}/dist.zip`;
|
|
2540
|
+
console.error(
|
|
2541
|
+
`\u8FDE\u63A5 ${settings.username}@${settings.host}:${settings.port} ...`
|
|
2542
|
+
);
|
|
2543
|
+
const sftp = new SftpClient();
|
|
2544
|
+
try {
|
|
2545
|
+
await sftp.connect({
|
|
2546
|
+
host: settings.host,
|
|
2547
|
+
port: settings.port,
|
|
2548
|
+
username: settings.username,
|
|
2549
|
+
password: settings.password,
|
|
2550
|
+
readyTimeout: 2e4,
|
|
2551
|
+
tryKeyboard: true
|
|
2552
|
+
});
|
|
2553
|
+
await ensureRemoteDir(sftp, settings.remotePath);
|
|
2554
|
+
await sftp.put(localZip, remoteZipPath);
|
|
2555
|
+
console.error(` \u2713 ${localZip} -> ${remoteZipPath}`);
|
|
2556
|
+
if (extract) {
|
|
2557
|
+
const target = settings.remotePath.replace(/\/$/, "");
|
|
2558
|
+
const unzipCmd = `unzip -o "${remoteZipPath}" -d "${target}"`;
|
|
2559
|
+
console.error(`\u8FDC\u7A0B\u89E3\u538B: ${unzipCmd}`);
|
|
2560
|
+
await execCommand(
|
|
2561
|
+
sftp.client,
|
|
2562
|
+
unzipCmd
|
|
2563
|
+
);
|
|
2564
|
+
console.error("\u8FDC\u7A0B\u89E3\u538B\u5B8C\u6210!");
|
|
2565
|
+
}
|
|
2566
|
+
console.error(extract ? "\u90E8\u7F72\u5B8C\u6210!" : "\u4E0A\u4F20\u5B8C\u6210!");
|
|
2567
|
+
} finally {
|
|
2568
|
+
await sftp.end();
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
async function runWisdomSftpDeploy(params) {
|
|
2572
|
+
const zipPath = path8.join(params.localDir, "..", ".deploy-sftp-dist.zip");
|
|
2573
|
+
const resolvedZipPath = path8.resolve(zipPath);
|
|
2574
|
+
let zipSizeBytes = 0;
|
|
2575
|
+
try {
|
|
2576
|
+
zipSizeBytes = await zipDirectory(params.localDir, resolvedZipPath);
|
|
2577
|
+
await uploadAndMaybeExtract(
|
|
2578
|
+
params.settings,
|
|
2579
|
+
resolvedZipPath,
|
|
2580
|
+
params.extract
|
|
2581
|
+
);
|
|
2582
|
+
} finally {
|
|
2583
|
+
try {
|
|
2584
|
+
await unlink(resolvedZipPath);
|
|
2585
|
+
console.error(`\u5DF2\u6E05\u7406\u672C\u5730\u4E34\u65F6\u6587\u4EF6: ${resolvedZipPath}`);
|
|
2586
|
+
} catch {
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
return {
|
|
2590
|
+
ok: true,
|
|
2591
|
+
localDir: params.localDir,
|
|
2592
|
+
host: params.settings.host,
|
|
2593
|
+
remotePath: params.settings.remotePath,
|
|
2594
|
+
zipSizeBytes,
|
|
2595
|
+
extracted: params.extract
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
|
|
2599
|
+
// src/commands/deploy/sftp.ts
|
|
2600
|
+
function registerDeploySftpCommands(program) {
|
|
2601
|
+
program.command("deploy-sftp").description(
|
|
2602
|
+
"\u5C06\u6307\u5B9A\u76EE\u5F55\u538B\u7F29\u4E3A zip \u540E\u901A\u8FC7 SFTP \u4E0A\u4F20\u5230\u8FDC\u7A0B\u670D\u52A1\u5668\uFF08\u8FDE\u63A5\u4FE1\u606F\u89C1 apm.config.json wisdomDeploy\uFF09"
|
|
2603
|
+
).option(
|
|
2604
|
+
"--dir <path>",
|
|
2605
|
+
"\u672C\u5730\u4EA7\u7269\u76EE\u5F55\uFF1B\u5355\u4ED3\u9ED8\u8BA4 apps/web/dist\uFF08\u9700\u5148 rush build / vite build\uFF09",
|
|
2606
|
+
"apps/web/dist"
|
|
2607
|
+
).option(
|
|
2608
|
+
"--config <path>",
|
|
2609
|
+
"apm.config.json \u8DEF\u5F84\uFF08\u9ED8\u8BA4 .apm/apm.config.json\uFF09"
|
|
2610
|
+
).option("--extract", "\u4E0A\u4F20\u540E\u5728\u8FDC\u7A0B\u670D\u52A1\u5668\u89E3\u538B zip \u5230 remotePath").action(
|
|
2611
|
+
async (opts) => {
|
|
2612
|
+
const cfg = loadApmConfig({ configPath: opts.config });
|
|
2613
|
+
const settings = resolveWisdomDeployFromApmConfig(cfg);
|
|
2614
|
+
const root = path9.resolve(process.cwd(), opts.dir || "apps/web/dist");
|
|
2615
|
+
if (!await isDirectoryPath(root)) {
|
|
2616
|
+
console.error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${root}`);
|
|
2617
|
+
process.exit(1);
|
|
2618
|
+
}
|
|
2619
|
+
try {
|
|
2620
|
+
const result = await runWisdomSftpDeploy({
|
|
2621
|
+
localDir: root,
|
|
2622
|
+
settings,
|
|
2623
|
+
extract: Boolean(opts.extract)
|
|
2624
|
+
});
|
|
2625
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2626
|
+
} catch (err) {
|
|
2627
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2628
|
+
console.error(`${opts.extract ? "\u90E8\u7F72" : "\u4E0A\u4F20"}\u5931\u8D25:`, message);
|
|
2629
|
+
process.exit(1);
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
);
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2447
2635
|
// src/commands/deploy/index.ts
|
|
2448
2636
|
function registerDeployCommands(program) {
|
|
2449
2637
|
registerDeployBackendCommands(program);
|
|
2450
2638
|
registerDeployFrontendCommands(program);
|
|
2639
|
+
registerDeploySftpCommands(program);
|
|
2451
2640
|
}
|
|
2452
2641
|
|
|
2453
2642
|
// src/index.ts
|
|
2454
2643
|
function buildProgram() {
|
|
2455
2644
|
const program = new Command();
|
|
2456
2645
|
program.name("apm").description(
|
|
2457
|
-
|
|
2646
|
+
`\u6BD4\u90BB\u661F\u56FE\u547D\u4EE4\u884C\uFF08\u4F1A\u8BDD\u5DE5\u4F5C\u533A\u4E0E\u7814\u53D1\u81EA\u52A8\u5316\uFF09\u3002
|
|
2458
2647
|
\u672A\u4F20 --server \u65F6\u4F18\u5148\u4F7F\u7528\u73AF\u5883\u53D8\u91CF AI_PM_SERVER\uFF0C\u5426\u5219\u9ED8\u8BA4 ${DEFAULT_BASE_URL}\u3002`
|
|
2459
2648
|
).version(readCliVersion(), "-V, --version", "\u663E\u793A\u7248\u672C\u53F7").helpOption("-h, --help", "\u663E\u793A\u5E2E\u52A9").showHelpAfterError(true);
|
|
2460
2649
|
program.command("login").description(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-project-manage-cli",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.27",
|
|
4
4
|
"description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"typescript": "~5.6.0",
|
|
24
24
|
"esbuild": "~0.28.0",
|
|
25
25
|
"vitest": "~4.1.5",
|
|
26
|
-
"@types/dockerode": "~4.0.1"
|
|
26
|
+
"@types/dockerode": "~4.0.1",
|
|
27
|
+
"@types/ssh2-sftp-client": "~9.0.6"
|
|
27
28
|
},
|
|
28
29
|
"engines": {
|
|
29
30
|
"node": ">=18"
|
|
@@ -35,6 +36,8 @@
|
|
|
35
36
|
"commander": "~14.0.3",
|
|
36
37
|
"yaml": "~2.8.4",
|
|
37
38
|
"minio": "~8.0.7",
|
|
38
|
-
"dockerode": "~5.0.0"
|
|
39
|
+
"dockerode": "~5.0.0",
|
|
40
|
+
"ssh2-sftp-client": "~12.0.1",
|
|
41
|
+
"jszip": "~3.10.1"
|
|
39
42
|
}
|
|
40
43
|
}
|