ai-project-manage-cli 6.0.79 → 6.0.81

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.
Files changed (3) hide show
  1. package/README.md +23 -0
  2. package/dist/index.js +476 -83
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -31,8 +31,31 @@ apm sync-project-documents
31
31
  apm sync-project-documents --push
32
32
  apm update-skills
33
33
  apm branch <sessionId>
34
+ apm branch prune [--dry-run] [-A|--all]
34
35
  ```
35
36
 
37
+ ## 常驻连接(后台守护)
38
+
39
+ ```bash
40
+ apm connect --daemon # 长期在线(后台)
41
+ apm connect --daemon -f # 长期在线,并在当前终端实时看日志
42
+ apm connect --daemon --server https://your-server.com
43
+
44
+ # 运维
45
+ apm daemon status
46
+ apm daemon logs
47
+ apm daemon logs -f # 随时重新跟踪日志
48
+ apm daemon restart
49
+ apm daemon stop
50
+ apm daemon delete
51
+
52
+ # 备选启动
53
+ apm daemon start
54
+ apm daemon start -f
55
+ ```
56
+
57
+ PM2 已内置;同一时刻仅允许一个 connect。启动时会自动检测 CLI 更新并刷新守护配置。
58
+
36
59
  `apm pull` 会自动将平台登记的**仓库项目文档**同步到 `.apm/project/`(含 `manifest.json`)。Agent 修改 `.apm/project/` 下文件后,`apm connect` 处理完消息会自动推回平台。
37
60
 
38
61
  详见仓库根目录 [docs/CLI.md](../../docs/CLI.md)。
package/dist/index.js CHANGED
@@ -1180,22 +1180,47 @@ async function ensureGitRepo2(cwd) {
1180
1180
  async function getCurrentBranch2(cwd) {
1181
1181
  return (await execGit3(cwd, ["rev-parse", "--abbrev-ref", "HEAD"], true)).trim();
1182
1182
  }
1183
- async function resolveDefaultBranch(cwd) {
1183
+ async function resolveBaselineBranch(cwd, api) {
1184
+ const workdirPath = resolveWorkdirPath(cwd);
1185
+ const baseline = await api.cli.workspaceBaseline({ workdirPath });
1186
+ if (!baseline.repositoryId) {
1187
+ const detail = baseline.diagnostic?.message ?? `\u672A\u5728\u5E73\u53F0\u627E\u5230\u4E0E\u5F53\u524D\u76EE\u5F55\u5339\u914D\u7684\u5DE5\u4F5C\u76EE\u5F55\u767B\u8BB0\uFF1A${workdirPath}\uFF08\u89C4\u8303\u5316\uFF1A${baseline.workdirPath}\uFF09
1188
+ \u8BF7\u5148\u5728\u5E73\u53F0\u767B\u8BB0\u8BE5\u8DEF\u5F84\u5BF9\u5E94\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0E\u4ED3\u5E93\u3002`;
1189
+ throw new Error(`[apm] ${detail}`);
1190
+ }
1191
+ const baselineBranch = (baseline.defaultBranch ?? "").trim();
1192
+ if (!baselineBranch) {
1193
+ throw new Error("[apm] \u5E73\u53F0\u8FD4\u56DE\u7684\u57FA\u7EBF\u5206\u652F\u540D\u4E3A\u7A7A");
1194
+ }
1195
+ await execGit3(cwd, ["fetch", "origin", baselineBranch], true);
1196
+ if (!await remoteBranchExists(cwd, baselineBranch)) {
1197
+ throw new Error(
1198
+ `[apm] \u8FDC\u7A0B\u4E0D\u5B58\u5728\u57FA\u7EBF\u5206\u652F origin/${baselineBranch}\uFF0C\u8BF7\u786E\u8BA4\u4ED3\u5E93\u9ED8\u8BA4\u5206\u652F\u5DF2\u63A8\u9001\u5230 origin`
1199
+ );
1200
+ }
1201
+ return baselineBranch;
1202
+ }
1203
+ async function isBranchBasedOnBaseline(cwd, branch, baselineBranch) {
1204
+ const ref = await localBranchExists2(cwd, branch) ? branch : `origin/${branch}`;
1184
1205
  try {
1185
- const ref = (await execGit3(cwd, ["symbolic-ref", "refs/remotes/origin/HEAD"], true)).trim();
1186
- const match = ref.match(/^refs\/remotes\/origin\/(.+)$/);
1187
- if (match?.[1]) {
1188
- return match[1];
1189
- }
1206
+ await execGit3(
1207
+ cwd,
1208
+ ["merge-base", "--is-ancestor", `origin/${baselineBranch}`, ref],
1209
+ true
1210
+ );
1211
+ return true;
1190
1212
  } catch {
1213
+ return false;
1191
1214
  }
1192
- const out = await execGit3(cwd, ["remote", "show", "origin"], true);
1193
- const headLine = out.split(/\r?\n/).find((line) => line.includes("HEAD branch"));
1194
- const branch = headLine?.split(":").pop()?.trim();
1195
- if (branch) {
1196
- return branch;
1215
+ }
1216
+ async function listCandidateSessionBranches(cwd, includeRemote) {
1217
+ const names = new Set(await listLocalSessionBranches(cwd));
1218
+ if (includeRemote) {
1219
+ for (const branch of await listRemoteSessionBranches(cwd)) {
1220
+ names.add(branch);
1221
+ }
1197
1222
  }
1198
- throw new Error("[apm] \u65E0\u6CD5\u89E3\u6790 origin \u9ED8\u8BA4\u5206\u652F\uFF0C\u8BF7\u5148\u6267\u884C git fetch origin");
1223
+ return [...names].sort((a, b) => a.localeCompare(b));
1199
1224
  }
1200
1225
  async function listLocalSessionBranches(cwd) {
1201
1226
  const out = await execGit3(
@@ -1268,20 +1293,27 @@ async function isBranchMergedIntoDefault(cwd, branch, defaultBranch) {
1268
1293
  async function runCleanBranches(options = {}) {
1269
1294
  const cwd = options.cwd ?? process.cwd();
1270
1295
  const dryRun = options.dryRun ?? false;
1296
+ const includeRemote = options.includeRemote ?? false;
1271
1297
  await ensureGitRepo2(cwd);
1272
1298
  await execGit3(cwd, ["fetch", "--prune", "origin"], true);
1273
1299
  const cfg = await ensureLoggedConfig();
1274
1300
  const api = createApmApiClient(cfg);
1301
+ const baselineBranch = await resolveBaselineBranch(cwd, api);
1275
1302
  const { sessions } = await api.cli.listSessionsForBranchCleanup({});
1276
1303
  const sessionStatusById = new Map(
1277
1304
  sessions.map((item) => [item.sessionId, item.taskStatus])
1278
1305
  );
1279
- const branchNames = /* @__PURE__ */ new Set([
1280
- ...await listLocalSessionBranches(cwd),
1281
- ...await listRemoteSessionBranches(cwd)
1282
- ]);
1283
- if (branchNames.size === 0) {
1284
- console.log("[apm] \u672A\u53D1\u73B0 feat/session-* \u5206\u652F");
1306
+ const candidates = await listCandidateSessionBranches(cwd, includeRemote);
1307
+ const branchNames = [];
1308
+ for (const branch of candidates) {
1309
+ if (await isBranchBasedOnBaseline(cwd, branch, baselineBranch)) {
1310
+ branchNames.push(branch);
1311
+ }
1312
+ }
1313
+ if (branchNames.length === 0) {
1314
+ console.log(
1315
+ `[apm] \u672A\u53D1\u73B0\u4ECE ${baselineBranch} \u884D\u751F\u7684\u672C\u5730 feat/session-* \u5206\u652F${includeRemote ? "\uFF08\u542B\u8FDC\u7A0B\uFF09" : ""}`
1316
+ );
1285
1317
  return;
1286
1318
  }
1287
1319
  const toDelete = [...branchNames].map((branch) => {
@@ -1300,10 +1332,6 @@ async function runCleanBranches(options = {}) {
1300
1332
  return;
1301
1333
  }
1302
1334
  let currentBranch = await getCurrentBranch2(cwd);
1303
- let defaultBranch = null;
1304
- if (dryRun) {
1305
- defaultBranch = await resolveDefaultBranch(cwd);
1306
- }
1307
1335
  for (const item of toDelete) {
1308
1336
  const { branch, sessionId, reason } = item;
1309
1337
  const label = `${branch} (${sessionId}: ${reason})`;
@@ -1311,16 +1339,15 @@ async function runCleanBranches(options = {}) {
1311
1339
  const merged = await isBranchMergedIntoDefault(
1312
1340
  cwd,
1313
1341
  branch,
1314
- defaultBranch
1342
+ baselineBranch
1315
1343
  );
1316
1344
  const mergeTag = merged ? "\u5DF2\u5408\u5E76" : "\u672A\u5408\u5E76";
1317
1345
  console.log(`[apm] [dry-run] \u5C06\u5220\u9664 ${label} [${mergeTag}]`);
1318
1346
  continue;
1319
1347
  }
1320
1348
  if (currentBranch === branch) {
1321
- defaultBranch ??= await resolveDefaultBranch(cwd);
1322
- await execGit3(cwd, ["checkout", defaultBranch], true);
1323
- currentBranch = defaultBranch;
1349
+ await execGit3(cwd, ["checkout", baselineBranch], true);
1350
+ currentBranch = baselineBranch;
1324
1351
  }
1325
1352
  if (await localBranchExists2(cwd, branch)) {
1326
1353
  await execGit3(cwd, ["branch", "-D", branch], true);
@@ -2126,7 +2153,6 @@ async function runUpdateMessageStatus(options) {
2126
2153
  }
2127
2154
 
2128
2155
  // src/commands/connect.ts
2129
- import { spawnSync as spawnSync2 } from "child_process";
2130
2156
  import WebSocket from "ws";
2131
2157
 
2132
2158
  // src/ws/protocol.ts
@@ -3232,6 +3258,337 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT) {
3232
3258
  return { acquire, release };
3233
3259
  }
3234
3260
 
3261
+ // src/commands/connect-lock.ts
3262
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync11, unlinkSync, writeFileSync as writeFileSync12 } from "fs";
3263
+ import { join as join14 } from "path";
3264
+ var CONNECT_LOCK_PATH = join14(APM_CONFIG_DIR, "connect.lock");
3265
+ function isProcessAlive(pid) {
3266
+ if (!Number.isInteger(pid) || pid <= 0) return false;
3267
+ try {
3268
+ process.kill(pid, 0);
3269
+ return true;
3270
+ } catch {
3271
+ return false;
3272
+ }
3273
+ }
3274
+ function readConnectLock() {
3275
+ if (!existsSync13(CONNECT_LOCK_PATH)) return null;
3276
+ try {
3277
+ const raw = readFileSync11(CONNECT_LOCK_PATH, "utf8");
3278
+ const parsed = JSON.parse(raw);
3279
+ if (typeof parsed.pid !== "number" || parsed.mode !== "foreground" && parsed.mode !== "pm2" || typeof parsed.startedAt !== "string") {
3280
+ return null;
3281
+ }
3282
+ return parsed;
3283
+ } catch {
3284
+ return null;
3285
+ }
3286
+ }
3287
+ function pruneStaleConnectLock() {
3288
+ const lock = readConnectLock();
3289
+ if (!lock) return;
3290
+ if (isProcessAlive(lock.pid)) return;
3291
+ try {
3292
+ unlinkSync(CONNECT_LOCK_PATH);
3293
+ } catch {
3294
+ }
3295
+ }
3296
+ function acquireConnectLock(mode) {
3297
+ pruneStaleConnectLock();
3298
+ const existing = readConnectLock();
3299
+ if (existing && isProcessAlive(existing.pid)) {
3300
+ console.error(
3301
+ `[apm] \u5DF2\u6709 apm connect \u5728\u8FD0\u884C (pid=${existing.pid}, mode=${existing.mode})`
3302
+ );
3303
+ process.exit(1);
3304
+ }
3305
+ mkdirSync6(APM_CONFIG_DIR, { recursive: true });
3306
+ const lock = {
3307
+ pid: process.pid,
3308
+ mode,
3309
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
3310
+ };
3311
+ writeFileSync12(CONNECT_LOCK_PATH, JSON.stringify(lock, null, 2) + "\n", "utf8");
3312
+ }
3313
+ function releaseConnectLock() {
3314
+ const lock = readConnectLock();
3315
+ if (lock?.pid !== process.pid) return;
3316
+ try {
3317
+ unlinkSync(CONNECT_LOCK_PATH);
3318
+ } catch {
3319
+ }
3320
+ }
3321
+ function forceReleaseConnectLock() {
3322
+ try {
3323
+ if (existsSync13(CONNECT_LOCK_PATH)) {
3324
+ unlinkSync(CONNECT_LOCK_PATH);
3325
+ }
3326
+ } catch {
3327
+ }
3328
+ }
3329
+
3330
+ // src/commands/daemon.ts
3331
+ import { spawnSync as spawnSync2 } from "child_process";
3332
+ import { createRequire } from "node:module";
3333
+ import { existsSync as existsSync14, mkdirSync as mkdirSync7, writeFileSync as writeFileSync13 } from "fs";
3334
+ import { join as join15 } from "path";
3335
+ var PM2_APP_NAME = "apm-connect";
3336
+ var PM2_ECOSYSTEM_PATH = join15(APM_CONFIG_DIR, "connect.ecosystem.cjs");
3337
+ function resolveConnectArgs(server) {
3338
+ const args = ["connect"];
3339
+ const trimmed = server?.trim();
3340
+ if (trimmed) {
3341
+ args.push("--server", trimmed.replace(/\/+$/, ""));
3342
+ }
3343
+ return args;
3344
+ }
3345
+ function buildConnectPm2Ecosystem(options) {
3346
+ const env = {};
3347
+ const baseUrl = options.baseUrl?.trim().replace(/\/+$/, "");
3348
+ if (baseUrl) {
3349
+ env.AI_PM_SERVER = baseUrl;
3350
+ }
3351
+ return {
3352
+ apps: [
3353
+ {
3354
+ name: PM2_APP_NAME,
3355
+ script: options.apmScript,
3356
+ interpreter: options.nodePath,
3357
+ args: options.connectArgs,
3358
+ autorestart: true,
3359
+ min_uptime: "10s",
3360
+ max_restarts: 100,
3361
+ restart_delay: 3e3,
3362
+ exp_backoff_restart_delay: 1e3,
3363
+ max_memory_restart: "1G",
3364
+ env
3365
+ }
3366
+ ]
3367
+ };
3368
+ }
3369
+ function formatConnectPm2EcosystemFile(ecosystem) {
3370
+ return `module.exports = ${JSON.stringify(ecosystem, null, 2)};
3371
+ `;
3372
+ }
3373
+ function resolvePm2Bin() {
3374
+ const require2 = createRequire(import.meta.url);
3375
+ return require2.resolve("pm2/bin/pm2");
3376
+ }
3377
+ var useNpmShell2 = process.platform === "win32";
3378
+ function resolveApmEntryPath(entryArg = process.argv[1]) {
3379
+ const fromArgv = entryArg?.trim();
3380
+ if (fromArgv && existsSync14(fromArgv)) {
3381
+ return fromArgv;
3382
+ }
3383
+ const npmResult = spawnSync2(useNpmShell2 ? "npm.cmd" : "npm", ["root", "-g"], {
3384
+ encoding: "utf8",
3385
+ shell: useNpmShell2,
3386
+ stdio: ["ignore", "pipe", "pipe"]
3387
+ });
3388
+ if (npmResult.status === 0) {
3389
+ const globalRoot = npmResult.stdout?.toString().trim();
3390
+ if (globalRoot) {
3391
+ const candidate = join15(globalRoot, CLI_PACKAGE_NAME, "dist", "index.js");
3392
+ if (existsSync14(candidate)) {
3393
+ return candidate;
3394
+ }
3395
+ }
3396
+ }
3397
+ if (fromArgv) {
3398
+ return fromArgv;
3399
+ }
3400
+ console.error("[apm] \u65E0\u6CD5\u89E3\u6790 apm \u5165\u53E3\u8DEF\u5F84");
3401
+ process.exit(1);
3402
+ }
3403
+ function isRunningUnderPm2() {
3404
+ return process.env.name === PM2_APP_NAME && process.env.pm_id !== void 0;
3405
+ }
3406
+ function runPm2(args, options) {
3407
+ let pm2Bin;
3408
+ try {
3409
+ pm2Bin = resolvePm2Bin();
3410
+ } catch {
3411
+ console.error(
3412
+ "[apm] \u672A\u627E\u5230\u5185\u7F6E pm2\uFF0C\u8BF7\u91CD\u65B0\u5B89\u88C5 apm CLI\uFF1Anpm install -g ai-project-manage-cli@latest"
3413
+ );
3414
+ process.exit(1);
3415
+ }
3416
+ const spawnOptions = {
3417
+ encoding: "utf8",
3418
+ stdio: options?.inherit ? "inherit" : ["ignore", "pipe", "pipe"]
3419
+ };
3420
+ const result = spawnSync2(process.execPath, [pm2Bin, ...args], spawnOptions);
3421
+ if (result.error) {
3422
+ console.error("[apm] \u6267\u884C pm2 \u5931\u8D25:", result.error.message);
3423
+ process.exit(1);
3424
+ }
3425
+ if (result.status !== 0) {
3426
+ const stderr = result.stderr?.toString().trim();
3427
+ const stdout = result.stdout?.toString().trim();
3428
+ const detail = stderr || stdout || `exit code ${result.status}`;
3429
+ console.error(`[apm] pm2 ${args.join(" ")} \u5931\u8D25: ${detail}`);
3430
+ process.exit(result.status ?? 1);
3431
+ }
3432
+ }
3433
+ function runPm2Json(args) {
3434
+ let pm2Bin;
3435
+ try {
3436
+ pm2Bin = resolvePm2Bin();
3437
+ } catch {
3438
+ return "[]";
3439
+ }
3440
+ const result = spawnSync2(process.execPath, [pm2Bin, ...args], {
3441
+ encoding: "utf8",
3442
+ stdio: ["ignore", "pipe", "pipe"]
3443
+ });
3444
+ if (result.status !== 0) return "[]";
3445
+ return result.stdout?.toString() ?? "[]";
3446
+ }
3447
+ function isPm2ConnectOnline() {
3448
+ try {
3449
+ const raw = runPm2Json(["jlist"]);
3450
+ const list = JSON.parse(raw);
3451
+ return list.some(
3452
+ (app) => app.name === PM2_APP_NAME && (app.pm2_env?.status === "online" || app.pm2_env?.status === "launching")
3453
+ );
3454
+ } catch {
3455
+ return false;
3456
+ }
3457
+ }
3458
+ function assertConnectNotRunning() {
3459
+ pruneStaleConnectLock();
3460
+ const lock = readConnectLock();
3461
+ if (lock && isProcessAlive(lock.pid)) {
3462
+ console.error(
3463
+ `[apm] \u5DF2\u6709 apm connect \u5728\u8FD0\u884C (pid=${lock.pid}, mode=${lock.mode})\uFF0C\u8BF7\u5148\u505C\u6B62\u540E\u518D\u542F\u52A8`
3464
+ );
3465
+ process.exit(1);
3466
+ }
3467
+ if (isPm2ConnectOnline()) {
3468
+ console.error(
3469
+ "[apm] \u5DF2\u6709 apm connect \u5B88\u62A4\u8FDB\u7A0B\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u6267\u884C apm daemon stop"
3470
+ );
3471
+ process.exit(1);
3472
+ }
3473
+ }
3474
+ function resolveRuntimePaths() {
3475
+ return {
3476
+ apmScript: resolveApmEntryPath(),
3477
+ nodePath: process.execPath
3478
+ };
3479
+ }
3480
+ function reexecConnectWithResolvedPath(options) {
3481
+ const apmScript = resolveApmEntryPath();
3482
+ const args = [apmScript, "connect"];
3483
+ const server = options.server?.trim();
3484
+ if (server) {
3485
+ args.push("--server", server);
3486
+ }
3487
+ const result = spawnSync2(process.execPath, args, { stdio: "inherit" });
3488
+ if (result.error) {
3489
+ console.error("[apm] \u91CD\u542F connect \u5931\u8D25:", result.error.message);
3490
+ process.exit(1);
3491
+ }
3492
+ process.exit(result.status ?? 0);
3493
+ }
3494
+ async function handleConnectAfterUpdate(options) {
3495
+ console.log("[apm] \u66F4\u65B0\u5B8C\u6210\uFF0C\u6B63\u5728\u4EE5\u65B0\u7248\u672C\u91CD\u65B0\u8FDE\u63A5\u2026");
3496
+ await prepareEcosystem(options.server);
3497
+ if (isRunningUnderPm2()) {
3498
+ runPm2(["startOrRestart", PM2_ECOSYSTEM_PATH, "--update-env"], {
3499
+ inherit: true
3500
+ });
3501
+ console.log("[apm] \u5DF2\u901A\u77E5 PM2 \u4F7F\u7528\u65B0\u7248\u672C\u91CD\u542F connect");
3502
+ process.exit(0);
3503
+ }
3504
+ reexecConnectWithResolvedPath(options);
3505
+ }
3506
+ async function resolveBaseUrl(server) {
3507
+ if (server?.trim()) {
3508
+ return server.trim().replace(/\/+$/, "");
3509
+ }
3510
+ const cfg = await tryReadApmConfig();
3511
+ return cfg?.baseUrl;
3512
+ }
3513
+ function writeEcosystemFile(options) {
3514
+ mkdirSync7(APM_CONFIG_DIR, { recursive: true });
3515
+ const ecosystem = buildConnectPm2Ecosystem(options);
3516
+ writeFileSync13(
3517
+ PM2_ECOSYSTEM_PATH,
3518
+ formatConnectPm2EcosystemFile(ecosystem),
3519
+ "utf8"
3520
+ );
3521
+ }
3522
+ async function prepareEcosystem(server) {
3523
+ await ensureLoggedConfig();
3524
+ const cfg = await ensureApmConfig();
3525
+ const { apmScript, nodePath } = resolveRuntimePaths();
3526
+ const connectArgs = resolveConnectArgs(server);
3527
+ const baseUrl = await resolveBaseUrl(server);
3528
+ writeEcosystemFile({
3529
+ apmScript,
3530
+ nodePath,
3531
+ connectArgs,
3532
+ baseUrl
3533
+ });
3534
+ return cfg;
3535
+ }
3536
+ async function runDaemonStart(options) {
3537
+ assertConnectNotRunning();
3538
+ await runUpdate();
3539
+ const cfg = await prepareEcosystem(options.server);
3540
+ runPm2(["start", PM2_ECOSYSTEM_PATH, "--update-env"], { inherit: true });
3541
+ console.log(
3542
+ `[apm] ${PM2_APP_NAME} \u5DF2\u7531 PM2 \u542F\u52A8\uFF08server=${options.server?.trim() || cfg.baseUrl}\uFF09`
3543
+ );
3544
+ if (options.follow) {
3545
+ console.log(
3546
+ "[apm] \u6B63\u5728\u8DDF\u8E2A\u65E5\u5FD7\uFF08Ctrl+C \u4EC5\u9000\u51FA\u65E5\u5FD7\u67E5\u770B\uFF0C\u5B88\u62A4\u8FDB\u7A0B\u7EE7\u7EED\u8FD0\u884C\uFF09"
3547
+ );
3548
+ runDaemonLogs({ follow: true });
3549
+ return;
3550
+ }
3551
+ console.log("[apm] \u67E5\u770B\u72B6\u6001: apm daemon status");
3552
+ console.log("[apm] \u67E5\u770B\u65E5\u5FD7: apm daemon logs -f");
3553
+ console.log("[apm] \u505C\u6B62: apm daemon stop");
3554
+ }
3555
+ async function runDaemonStop() {
3556
+ runPm2(["stop", PM2_APP_NAME], { inherit: true });
3557
+ forceReleaseConnectLock();
3558
+ console.log(`[apm] ${PM2_APP_NAME} \u5DF2\u505C\u6B62`);
3559
+ }
3560
+ async function runDaemonRestart(options) {
3561
+ pruneStaleConnectLock();
3562
+ const lock = readConnectLock();
3563
+ if (lock && isProcessAlive(lock.pid) && !isPm2ConnectOnline()) {
3564
+ console.error(
3565
+ `[apm] \u5DF2\u6709\u524D\u53F0 apm connect \u5728\u8FD0\u884C (pid=${lock.pid})\uFF0C\u8BF7\u5148\u505C\u6B62\u540E\u518D\u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B`
3566
+ );
3567
+ process.exit(1);
3568
+ }
3569
+ await runUpdate();
3570
+ await prepareEcosystem(options.server);
3571
+ runPm2(["startOrRestart", PM2_ECOSYSTEM_PATH, "--update-env"], {
3572
+ inherit: true
3573
+ });
3574
+ console.log(`[apm] ${PM2_APP_NAME} \u5DF2\u91CD\u542F`);
3575
+ }
3576
+ async function runDaemonDelete() {
3577
+ runPm2(["delete", PM2_APP_NAME], { inherit: true });
3578
+ forceReleaseConnectLock();
3579
+ console.log(`[apm] ${PM2_APP_NAME} \u5DF2\u4ECE PM2 \u79FB\u9664`);
3580
+ }
3581
+ async function runDaemonStatus() {
3582
+ runPm2(["describe", PM2_APP_NAME], { inherit: true });
3583
+ }
3584
+ async function runDaemonLogs(options) {
3585
+ const args = ["logs", PM2_APP_NAME, "--lines", String(options.lines ?? 100)];
3586
+ if (!options.follow) {
3587
+ args.push("--nostream");
3588
+ }
3589
+ runPm2(args, { inherit: true });
3590
+ }
3591
+
3235
3592
  // src/commands/connect.ts
3236
3593
  var HEARTBEAT_MS = 3e4;
3237
3594
  async function updateMessageStatus(cfg, messageId, status) {
@@ -3401,23 +3758,10 @@ function startHeartbeat(ws, clientMachineId) {
3401
3758
  const timer = setInterval(send, HEARTBEAT_MS);
3402
3759
  return () => clearInterval(timer);
3403
3760
  }
3404
- function reexecConnect(options) {
3405
- const args = [process.argv[1], "connect"];
3406
- const server = options.server?.trim();
3407
- if (server) {
3408
- args.push("--server", server);
3409
- }
3410
- const result = spawnSync2(process.execPath, args, { stdio: "inherit" });
3411
- if (result.error) {
3412
- console.error("[apm] \u91CD\u542F connect \u5931\u8D25:", result.error.message);
3413
- process.exit(1);
3414
- }
3415
- process.exit(result.status ?? 0);
3416
- }
3417
3761
  async function runConnect(options) {
3418
3762
  const { didUpdate } = await runUpdate();
3419
3763
  if (didUpdate) {
3420
- reexecConnect(options);
3764
+ await handleConnectAfterUpdate(options);
3421
3765
  }
3422
3766
  const cfg = await ensureLoggedConfig();
3423
3767
  if (options.server?.trim()) {
@@ -3428,6 +3772,9 @@ async function runConnect(options) {
3428
3772
  console.error("[apm] config \u7F3A\u5C11 clientMachineId\uFF0C\u8BF7\u91CD\u65B0 apm login");
3429
3773
  process.exit(1);
3430
3774
  }
3775
+ assertConnectNotRunning();
3776
+ acquireConnectLock("foreground");
3777
+ process.on("exit", releaseConnectLock);
3431
3778
  const url = buildAgentWsUrl(cfg.baseUrl, resolveApiKey(cfg));
3432
3779
  console.log(`[apm] \u8FDE\u63A5 ${cfg.baseUrl} \u2026`);
3433
3780
  await new Promise((resolve5, reject) => {
@@ -3459,6 +3806,7 @@ async function runConnect(options) {
3459
3806
  ]);
3460
3807
  } catch {
3461
3808
  }
3809
+ releaseConnectLock();
3462
3810
  resolve5();
3463
3811
  process.exit(code);
3464
3812
  };
@@ -3615,20 +3963,20 @@ async function runCreatePr(options) {
3615
3963
  import { spawnSync as spawnSync5 } from "node:child_process";
3616
3964
 
3617
3965
  // src/commands/deploy/internal/apm-config.ts
3618
- import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
3966
+ import { existsSync as existsSync15, readFileSync as readFileSync12 } from "node:fs";
3619
3967
  import { homedir as homedir2 } from "node:os";
3620
- import { join as join14, resolve as resolve4 } from "node:path";
3968
+ import { join as join16, resolve as resolve4 } from "node:path";
3621
3969
  function loadApmConfig(options) {
3622
3970
  const p = resolve4(
3623
3971
  process.cwd(),
3624
3972
  options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
3625
3973
  );
3626
- if (!existsSync13(p)) {
3974
+ if (!existsSync15(p)) {
3627
3975
  console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
3628
3976
  process.exit(1);
3629
3977
  }
3630
3978
  try {
3631
- const raw = readFileSync11(p, "utf8");
3979
+ const raw = readFileSync12(p, "utf8");
3632
3980
  return JSON.parse(raw);
3633
3981
  } catch (e) {
3634
3982
  console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
@@ -3812,12 +4160,12 @@ function readMavenLocalRepoFromEnv(env = process.env) {
3812
4160
  return null;
3813
4161
  }
3814
4162
  function readMavenLocalRepoFromSettings() {
3815
- const settingsPath = join14(homedir2(), ".m2", "settings.xml");
3816
- if (!existsSync13(settingsPath)) {
4163
+ const settingsPath = join16(homedir2(), ".m2", "settings.xml");
4164
+ if (!existsSync15(settingsPath)) {
3817
4165
  return null;
3818
4166
  }
3819
4167
  try {
3820
- const xml = readFileSync11(settingsPath, "utf8");
4168
+ const xml = readFileSync12(settingsPath, "utf8");
3821
4169
  const match = xml.match(
3822
4170
  /<localRepository>\s*([^<]+?)\s*<\/localRepository>/
3823
4171
  );
@@ -3848,7 +4196,7 @@ function resolveMavenLocalRepoWithSource() {
3848
4196
  };
3849
4197
  }
3850
4198
  return {
3851
- path: join14(homedir2(), ".m2", "repository"),
4199
+ path: join16(homedir2(), ".m2", "repository"),
3852
4200
  source: "default",
3853
4201
  sourceDetail: "~/.m2/repository"
3854
4202
  };
@@ -3918,18 +4266,18 @@ function posixBasename(p) {
3918
4266
  }
3919
4267
 
3920
4268
  // src/commands/deploy/internal/wisdom-auto-deploy.ts
3921
- import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
4269
+ import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
3922
4270
  import path3 from "node:path";
3923
4271
  import { spawnSync as spawnSync4 } from "node:child_process";
3924
4272
 
3925
4273
  // src/commands/deploy/internal/wisdom-backend-deploy.ts
3926
4274
  import {
3927
- existsSync as existsSync14,
3928
- mkdirSync as mkdirSync6,
4275
+ existsSync as existsSync16,
4276
+ mkdirSync as mkdirSync8,
3929
4277
  readdirSync as readdirSync5,
3930
- readFileSync as readFileSync12,
4278
+ readFileSync as readFileSync13,
3931
4279
  statSync as statSync5,
3932
- writeFileSync as writeFileSync12
4280
+ writeFileSync as writeFileSync14
3933
4281
  } from "node:fs";
3934
4282
  import { spawnSync as spawnSync3 } from "node:child_process";
3935
4283
  import path2 from "node:path";
@@ -4136,15 +4484,15 @@ function fileSignature(filePath) {
4136
4484
  }
4137
4485
  function loadManifest4() {
4138
4486
  const manifestPath2 = manifestFilePath();
4139
- if (!existsSync14(manifestPath2)) {
4487
+ if (!existsSync16(manifestPath2)) {
4140
4488
  return {};
4141
4489
  }
4142
- return JSON.parse(readFileSync12(manifestPath2, "utf8"));
4490
+ return JSON.parse(readFileSync13(manifestPath2, "utf8"));
4143
4491
  }
4144
4492
  function saveManifest4(manifest) {
4145
4493
  const dir = deployCacheDir();
4146
- mkdirSync6(dir, { recursive: true });
4147
- writeFileSync12(manifestFilePath(), JSON.stringify(manifest, null, 2), "utf8");
4494
+ mkdirSync8(dir, { recursive: true });
4495
+ writeFileSync14(manifestFilePath(), JSON.stringify(manifest, null, 2), "utf8");
4148
4496
  }
4149
4497
  function isProjectLibJar(jarName) {
4150
4498
  return jarName.startsWith("jeecg-");
@@ -4200,12 +4548,12 @@ function updateManifestEntries(manifest, entries, projectRoot) {
4200
4548
  }
4201
4549
  async function createUpdatePackage(entries, packageName) {
4202
4550
  const dir = deployCacheDir();
4203
- mkdirSync6(dir, { recursive: true });
4551
+ mkdirSync8(dir, { recursive: true });
4204
4552
  const zipPath = path2.join(dir, packageName);
4205
4553
  log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${path2.basename(zipPath)}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
4206
4554
  const zip = new JSZip2();
4207
4555
  for (const entry of entries) {
4208
- const content = readFileSync12(entry.path);
4556
+ const content = readFileSync13(entry.path);
4209
4557
  zip.file(entry.arcname, content);
4210
4558
  log(` \u6253\u5305: ${entry.arcname} (${entry.reason})`);
4211
4559
  }
@@ -4214,7 +4562,7 @@ async function createUpdatePackage(entries, packageName) {
4214
4562
  compression: "DEFLATE",
4215
4563
  compressionOptions: { level: 6 }
4216
4564
  });
4217
- writeFileSync12(zipPath, buffer);
4565
+ writeFileSync14(zipPath, buffer);
4218
4566
  return zipPath;
4219
4567
  }
4220
4568
  function getMvnExecutable() {
@@ -4263,11 +4611,11 @@ function runMavenBuild(projectRoot, mavenLocalRepo, repoSource) {
4263
4611
  }
4264
4612
  function locateLibDir(projectRoot) {
4265
4613
  const targetDir = getTargetDir(projectRoot);
4266
- if (!existsSync14(targetDir)) {
4614
+ if (!existsSync16(targetDir)) {
4267
4615
  fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
4268
4616
  }
4269
4617
  const libDir = path2.join(targetDir, "lib");
4270
- if (!existsSync14(libDir) || !statSync5(libDir).isDirectory()) {
4618
+ if (!existsSync16(libDir) || !statSync5(libDir).isDirectory()) {
4271
4619
  fail(`lib \u76EE\u5F55\u4E0D\u5B58\u5728: ${libDir}`);
4272
4620
  }
4273
4621
  const libJars = readdirSync5(libDir).filter((name) => name.endsWith(".jar"));
@@ -4279,7 +4627,7 @@ function locateLibDir(projectRoot) {
4279
4627
  }
4280
4628
  function locateMainJar(projectRoot) {
4281
4629
  const targetDir = getTargetDir(projectRoot);
4282
- if (!existsSync14(targetDir)) {
4630
+ if (!existsSync16(targetDir)) {
4283
4631
  fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
4284
4632
  }
4285
4633
  const jarFiles = readdirSync5(targetDir).filter((name) => name.endsWith(".jar") && !name.endsWith(".jar.original")).map((name) => path2.join(targetDir, name)).sort((a, b) => statSync5(b).mtimeMs - statSync5(a).mtimeMs);
@@ -4698,15 +5046,15 @@ function isWisdomLegacyDeploy(cfg) {
4698
5046
  return !hasNewFrontend && !hasNewBackend;
4699
5047
  }
4700
5048
  function detectWisdomProjectType(cwd) {
4701
- return existsSync15(path3.join(cwd, "package.json")) ? "frontend" : "backend";
5049
+ return existsSync17(path3.join(cwd, "package.json")) ? "frontend" : "backend";
4702
5050
  }
4703
5051
  function readPackageScripts(cwd) {
4704
5052
  const pkgPath = path3.join(cwd, "package.json");
4705
- if (!existsSync15(pkgPath)) {
5053
+ if (!existsSync17(pkgPath)) {
4706
5054
  return {};
4707
5055
  }
4708
5056
  try {
4709
- const raw = readFileSync13(pkgPath, "utf8");
5057
+ const raw = readFileSync14(pkgPath, "utf8");
4710
5058
  const parsed = JSON.parse(raw);
4711
5059
  return parsed.scripts ?? {};
4712
5060
  } catch {
@@ -4816,7 +5164,7 @@ import path7 from "node:path";
4816
5164
  import Docker from "dockerode";
4817
5165
 
4818
5166
  // src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
4819
- import { existsSync as existsSync16, readFileSync as readFileSync14 } from "node:fs";
5167
+ import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
4820
5168
  import path4 from "node:path";
4821
5169
  function asOptionalTlsBuffer(value) {
4822
5170
  if (typeof value !== "string") {
@@ -4828,8 +5176,8 @@ function asOptionalTlsBuffer(value) {
4828
5176
  if (normalized === "") {
4829
5177
  return void 0;
4830
5178
  }
4831
- if (existsSync16(normalized)) {
4832
- return readFileSync14(normalized);
5179
+ if (existsSync18(normalized)) {
5180
+ return readFileSync15(normalized);
4833
5181
  }
4834
5182
  const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
4835
5183
  if (looksLikePath) {
@@ -5039,7 +5387,7 @@ var DockerodeClient = class {
5039
5387
  var createDockerodeClient = (config) => new DockerodeClient(config);
5040
5388
 
5041
5389
  // src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
5042
- import { existsSync as existsSync17, readFileSync as readFileSync15, statSync as statSync6 } from "node:fs";
5390
+ import { existsSync as existsSync19, readFileSync as readFileSync16, statSync as statSync6 } from "node:fs";
5043
5391
  import path5 from "node:path";
5044
5392
  function stripSurroundingQuotes(value) {
5045
5393
  const t = value.trim();
@@ -5056,10 +5404,10 @@ function loadEnvFromFile(envFilePath) {
5056
5404
  return {};
5057
5405
  }
5058
5406
  const targetPath = path5.resolve(envFilePath);
5059
- if (!existsSync17(targetPath) || !statSync6(targetPath).isFile()) {
5407
+ if (!existsSync19(targetPath) || !statSync6(targetPath).isFile()) {
5060
5408
  return {};
5061
5409
  }
5062
- const raw = readFileSync15(targetPath, "utf-8");
5410
+ const raw = readFileSync16(targetPath, "utf-8");
5063
5411
  const result = {};
5064
5412
  for (const line of raw.split(/\r?\n/)) {
5065
5413
  const normalized = line.trim();
@@ -5230,12 +5578,12 @@ function dockerPushImage(params, cwd) {
5230
5578
  }
5231
5579
 
5232
5580
  // src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
5233
- import { existsSync as existsSync18 } from "node:fs";
5581
+ import { existsSync as existsSync20 } from "node:fs";
5234
5582
  import path6 from "node:path";
5235
5583
  function resolveDockerBuildPaths(cwd) {
5236
5584
  const dockerfilePath = path6.join(cwd, "Dockerfile");
5237
5585
  Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
5238
- if (!existsSync18(dockerfilePath)) {
5586
+ if (!existsSync20(dockerfilePath)) {
5239
5587
  throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
5240
5588
  }
5241
5589
  Logger.info("\u2713 Dockerfile \u5B58\u5728");
@@ -5785,20 +6133,65 @@ function buildProgram() {
5785
6133
  });
5786
6134
  program.command("connect").description(
5787
6135
  "\u8FDE\u63A5\u5E73\u53F0 WebSocket\uFF08/ws/agent\uFF09\uFF0C\u7EF4\u6301\u5FC3\u8DF3\u5E76\u5904\u7406\u4E0B\u884C message\uFF08TYPING \u2192 Cursor \u2192 SUCCESS/FAILED\uFF09\uFF1B\u542F\u52A8\u524D\u81EA\u52A8 apm update \u5230\u5F53\u524D\u5927\u7248\u672C\u6700\u65B0\u7248"
5788
- ).option("--server <url>", "API \u6839\u5730\u5740\uFF0C\u8986\u76D6 config \u4E2D\u7684 baseUrl").action(async (opts) => {
5789
- await runConnect(opts);
6136
+ ).option("--server <url>", "API \u6839\u5730\u5740\uFF0C\u8986\u76D6 config \u4E2D\u7684 baseUrl").option(
6137
+ "--daemon",
6138
+ "\u540E\u53F0\u5B88\u62A4\u8FD0\u884C\uFF08\u5185\u7F6E PM2\uFF0C\u65AD\u7EBF\u6216\u9000\u51FA\u540E\u81EA\u52A8\u91CD\u542F\uFF1B\u63A8\u8350\u7528\u4E8E\u957F\u671F\u5728\u7EBF\uFF09"
6139
+ ).option("-f, --follow", "\u4E0E --daemon \u5408\u7528\uFF1A\u542F\u52A8\u540E\u5728\u5F53\u524D\u7EC8\u7AEF\u5B9E\u65F6\u8DDF\u8E2A\u65E5\u5FD7").action(
6140
+ async (opts) => {
6141
+ if (opts.follow && !opts.daemon) {
6142
+ console.error("[apm] --follow \u4EC5\u53EF\u4E0E --daemon \u540C\u65F6\u4F7F\u7528");
6143
+ process.exit(1);
6144
+ }
6145
+ if (opts.daemon === true) {
6146
+ await runDaemonStart({ server: opts.server, follow: opts.follow });
6147
+ return;
6148
+ }
6149
+ await runConnect({ server: opts.server });
6150
+ }
6151
+ );
6152
+ const daemon = program.command("daemon").description(
6153
+ "\u540E\u53F0 connect \u8FD0\u7EF4\u547D\u4EE4\uFF08\u542F\u52A8\u8BF7\u4F18\u5148\u4F7F\u7528 apm connect --daemon\uFF1B\u6B64\u5904\u4F9B status/logs/stop \u7B49\uFF09"
6154
+ );
6155
+ daemon.command("start").description("\u542F\u52A8\u540E\u53F0 connect\uFF08\u7B49\u6548 apm connect --daemon\uFF09").option("--server <url>", "API \u6839\u5730\u5740\uFF0C\u8986\u76D6 config \u4E2D\u7684 baseUrl").option("-f, --follow", "\u542F\u52A8\u540E\u5728\u5F53\u524D\u7EC8\u7AEF\u5B9E\u65F6\u8DDF\u8E2A\u65E5\u5FD7").action(async (opts) => {
6156
+ await runDaemonStart({ server: opts.server, follow: opts.follow });
6157
+ });
6158
+ daemon.command("stop").description("\u505C\u6B62 PM2 \u5B88\u62A4\u7684 apm connect").action(async () => {
6159
+ await runDaemonStop();
6160
+ });
6161
+ daemon.command("restart").description("\u91CD\u542F PM2 \u5B88\u62A4\u7684 apm connect\uFF08\u672A\u542F\u52A8\u65F6\u4F1A\u76F4\u63A5\u542F\u52A8\uFF09").option("--server <url>", "API \u6839\u5730\u5740\uFF0C\u8986\u76D6 config \u4E2D\u7684 baseUrl").action(async (opts) => {
6162
+ await runDaemonRestart(opts);
6163
+ });
6164
+ daemon.command("delete").description("\u4ECE PM2 \u4E2D\u79FB\u9664 apm connect \u8FDB\u7A0B").action(async () => {
6165
+ await runDaemonDelete();
6166
+ });
6167
+ daemon.command("status").description("\u67E5\u770B PM2 \u5B88\u62A4\u7684 apm connect \u72B6\u6001").action(async () => {
6168
+ await runDaemonStatus();
5790
6169
  });
5791
- program.command("branch").description("\u5207\u6362\u6216\u521B\u5EFA\u4F1A\u8BDD\u5206\u652F feat/session-<sessionId>").argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").option(
6170
+ daemon.command("logs").description("\u67E5\u770B PM2 \u5B88\u62A4\u7684 apm connect \u65E5\u5FD7").option("-f, --follow", "\u6301\u7EED\u8DDF\u8E2A\u65E5\u5FD7").option("-n, --lines <n>", "\u663E\u793A\u884C\u6570", "100").action(async (opts) => {
6171
+ const lines = Number.parseInt(opts.lines ?? "100", 10);
6172
+ await runDaemonLogs({
6173
+ follow: opts.follow === true,
6174
+ lines: Number.isFinite(lines) && lines > 0 ? lines : 100
6175
+ });
6176
+ });
6177
+ const branch = program.command("branch").description("\u4F1A\u8BDD\u5206\u652F feat/session-<sessionId>");
6178
+ branch.command("prune").description(
6179
+ "\u6E05\u7406\u672C\u5730\u4E0E\u8FDC\u7A0B feat/session-* \u5206\u652F\uFF08\u7C7B\u4F3C git fetch --prune\uFF09\uFF1A\u6C9F\u901A\u7FA4\u4E0D\u5728\u4EFB\u52A1\u5217\u8868\u4E2D\uFF0C\u6216\u5173\u8054\u4EFB\u52A1\u5DF2\u5B8C\u6210\u65F6\u5220\u9664"
6180
+ ).option("--dry-run", "\u4EC5\u5217\u51FA\u5C06\u88AB\u5220\u9664\u7684\u5206\u652F\uFF0C\u4E0D\u5B9E\u9645\u6267\u884C").option(
6181
+ "-A, --all",
6182
+ "\u5305\u542B\u4EC5\u5B58\u5728\u4E8E\u8FDC\u7A0B\u3001\u672C\u5730\u672A checkout \u7684 feat/session-* \u5206\u652F\uFF08\u9ED8\u8BA4\u4EC5\u5904\u7406\u672C\u5730\u5206\u652F\uFF09"
6183
+ ).action(async (opts) => {
6184
+ await runCleanBranches({
6185
+ dryRun: opts.dryRun,
6186
+ includeRemote: opts.all
6187
+ });
6188
+ });
6189
+ branch.description("\u5207\u6362\u6216\u521B\u5EFA\u4F1A\u8BDD\u5206\u652F feat/session-<sessionId>").argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").option(
5792
6190
  "-m, --message <text>",
5793
6191
  "\u5DF2\u5728\u76EE\u6807\u5206\u652F\u4E14\u9700\u63D0\u4EA4\u672C\u5730\u6539\u52A8\u65F6\u4F7F\u7528\u7684\u63D0\u4EA4\u8BF4\u660E"
5794
6192
  ).action(async (sessionId, opts) => {
5795
6193
  await runBranch(sessionId, { message: opts.message });
5796
6194
  });
5797
- program.command("clean-branches").description(
5798
- "\u6E05\u7406\u672C\u5730\u4E0E\u8FDC\u7A0B feat/session-* \u5206\u652F\uFF1A\u6C9F\u901A\u7FA4\u4E0D\u5728\u4EFB\u52A1\u5217\u8868\u4E2D\uFF0C\u6216\u5173\u8054\u4EFB\u52A1\u5DF2\u5B8C\u6210\u65F6\u5220\u9664"
5799
- ).option("--dry-run", "\u4EC5\u5217\u51FA\u5C06\u88AB\u5220\u9664\u7684\u5206\u652F\uFF0C\u4E0D\u5B9E\u9645\u6267\u884C").action(async (opts) => {
5800
- await runCleanBranches({ dryRun: opts.dryRun });
5801
- });
5802
6195
  program.command("create-pr").description(
5803
6196
  "\u4E3A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u7684\u4F1A\u8BDD\u7279\u6027\u5206\u652F\u521B\u5EFA PR\uFF08\u6807\u9898\u81EA\u52A8\u52A0 [AI] \u6807\u8BC6\uFF1B\u8FDC\u7A0B\u521B\u5EFA\u5931\u8D25\u65F6\u5E73\u53F0\u6570\u636E\u56DE\u6EDA\uFF09"
5804
6197
  ).requiredOption("--session <sessionId>", "\u6C9F\u901A\u7FA4 ID").requiredOption("--title <title>", "PR \u6807\u9898").option("--content <content>", "PR \u6B63\u6587\uFF08Markdown\uFF09", "").action(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "6.0.79",
3
+ "version": "6.0.81",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -44,6 +44,7 @@
44
44
  "dockerode": "~5.0.0",
45
45
  "ssh2": "~1.16.0",
46
46
  "ssh2-sftp-client": "~12.0.1",
47
- "jszip": "~3.10.1"
47
+ "jszip": "~3.10.1",
48
+ "pm2": "~5.4.3"
48
49
  }
49
50
  }