ai-project-manage-cli 6.0.79 → 6.0.80

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 +22 -0
  2. package/dist/index.js +410 -51
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -33,6 +33,28 @@ apm update-skills
33
33
  apm branch <sessionId>
34
34
  ```
35
35
 
36
+ ## 常驻连接(后台守护)
37
+
38
+ ```bash
39
+ apm connect --daemon # 长期在线(后台)
40
+ apm connect --daemon -f # 长期在线,并在当前终端实时看日志
41
+ apm connect --daemon --server https://your-server.com
42
+
43
+ # 运维
44
+ apm daemon status
45
+ apm daemon logs
46
+ apm daemon logs -f # 随时重新跟踪日志
47
+ apm daemon restart
48
+ apm daemon stop
49
+ apm daemon delete
50
+
51
+ # 备选启动
52
+ apm daemon start
53
+ apm daemon start -f
54
+ ```
55
+
56
+ PM2 已内置;同一时刻仅允许一个 connect。启动时会自动检测 CLI 更新并刷新守护配置。
57
+
36
58
  `apm pull` 会自动将平台登记的**仓库项目文档**同步到 `.apm/project/`(含 `manifest.json`)。Agent 修改 `.apm/project/` 下文件后,`apm connect` 处理完消息会自动推回平台。
37
59
 
38
60
  详见仓库根目录 [docs/CLI.md](../../docs/CLI.md)。
package/dist/index.js CHANGED
@@ -2126,7 +2126,6 @@ async function runUpdateMessageStatus(options) {
2126
2126
  }
2127
2127
 
2128
2128
  // src/commands/connect.ts
2129
- import { spawnSync as spawnSync2 } from "child_process";
2130
2129
  import WebSocket from "ws";
2131
2130
 
2132
2131
  // src/ws/protocol.ts
@@ -3232,6 +3231,337 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT) {
3232
3231
  return { acquire, release };
3233
3232
  }
3234
3233
 
3234
+ // src/commands/connect-lock.ts
3235
+ import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync11, unlinkSync, writeFileSync as writeFileSync12 } from "fs";
3236
+ import { join as join14 } from "path";
3237
+ var CONNECT_LOCK_PATH = join14(APM_CONFIG_DIR, "connect.lock");
3238
+ function isProcessAlive(pid) {
3239
+ if (!Number.isInteger(pid) || pid <= 0) return false;
3240
+ try {
3241
+ process.kill(pid, 0);
3242
+ return true;
3243
+ } catch {
3244
+ return false;
3245
+ }
3246
+ }
3247
+ function readConnectLock() {
3248
+ if (!existsSync13(CONNECT_LOCK_PATH)) return null;
3249
+ try {
3250
+ const raw = readFileSync11(CONNECT_LOCK_PATH, "utf8");
3251
+ const parsed = JSON.parse(raw);
3252
+ if (typeof parsed.pid !== "number" || parsed.mode !== "foreground" && parsed.mode !== "pm2" || typeof parsed.startedAt !== "string") {
3253
+ return null;
3254
+ }
3255
+ return parsed;
3256
+ } catch {
3257
+ return null;
3258
+ }
3259
+ }
3260
+ function pruneStaleConnectLock() {
3261
+ const lock = readConnectLock();
3262
+ if (!lock) return;
3263
+ if (isProcessAlive(lock.pid)) return;
3264
+ try {
3265
+ unlinkSync(CONNECT_LOCK_PATH);
3266
+ } catch {
3267
+ }
3268
+ }
3269
+ function acquireConnectLock(mode) {
3270
+ pruneStaleConnectLock();
3271
+ const existing = readConnectLock();
3272
+ if (existing && isProcessAlive(existing.pid)) {
3273
+ console.error(
3274
+ `[apm] \u5DF2\u6709 apm connect \u5728\u8FD0\u884C (pid=${existing.pid}, mode=${existing.mode})`
3275
+ );
3276
+ process.exit(1);
3277
+ }
3278
+ mkdirSync6(APM_CONFIG_DIR, { recursive: true });
3279
+ const lock = {
3280
+ pid: process.pid,
3281
+ mode,
3282
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
3283
+ };
3284
+ writeFileSync12(CONNECT_LOCK_PATH, JSON.stringify(lock, null, 2) + "\n", "utf8");
3285
+ }
3286
+ function releaseConnectLock() {
3287
+ const lock = readConnectLock();
3288
+ if (lock?.pid !== process.pid) return;
3289
+ try {
3290
+ unlinkSync(CONNECT_LOCK_PATH);
3291
+ } catch {
3292
+ }
3293
+ }
3294
+ function forceReleaseConnectLock() {
3295
+ try {
3296
+ if (existsSync13(CONNECT_LOCK_PATH)) {
3297
+ unlinkSync(CONNECT_LOCK_PATH);
3298
+ }
3299
+ } catch {
3300
+ }
3301
+ }
3302
+
3303
+ // src/commands/daemon.ts
3304
+ import { spawnSync as spawnSync2 } from "child_process";
3305
+ import { createRequire } from "node:module";
3306
+ import { existsSync as existsSync14, mkdirSync as mkdirSync7, writeFileSync as writeFileSync13 } from "fs";
3307
+ import { join as join15 } from "path";
3308
+ var PM2_APP_NAME = "apm-connect";
3309
+ var PM2_ECOSYSTEM_PATH = join15(APM_CONFIG_DIR, "connect.ecosystem.cjs");
3310
+ function resolveConnectArgs(server) {
3311
+ const args = ["connect"];
3312
+ const trimmed = server?.trim();
3313
+ if (trimmed) {
3314
+ args.push("--server", trimmed.replace(/\/+$/, ""));
3315
+ }
3316
+ return args;
3317
+ }
3318
+ function buildConnectPm2Ecosystem(options) {
3319
+ const env = {};
3320
+ const baseUrl = options.baseUrl?.trim().replace(/\/+$/, "");
3321
+ if (baseUrl) {
3322
+ env.AI_PM_SERVER = baseUrl;
3323
+ }
3324
+ return {
3325
+ apps: [
3326
+ {
3327
+ name: PM2_APP_NAME,
3328
+ script: options.apmScript,
3329
+ interpreter: options.nodePath,
3330
+ args: options.connectArgs,
3331
+ autorestart: true,
3332
+ min_uptime: "10s",
3333
+ max_restarts: 100,
3334
+ restart_delay: 3e3,
3335
+ exp_backoff_restart_delay: 1e3,
3336
+ max_memory_restart: "1G",
3337
+ env
3338
+ }
3339
+ ]
3340
+ };
3341
+ }
3342
+ function formatConnectPm2EcosystemFile(ecosystem) {
3343
+ return `module.exports = ${JSON.stringify(ecosystem, null, 2)};
3344
+ `;
3345
+ }
3346
+ function resolvePm2Bin() {
3347
+ const require2 = createRequire(import.meta.url);
3348
+ return require2.resolve("pm2/bin/pm2");
3349
+ }
3350
+ var useNpmShell2 = process.platform === "win32";
3351
+ function resolveApmEntryPath(entryArg = process.argv[1]) {
3352
+ const fromArgv = entryArg?.trim();
3353
+ if (fromArgv && existsSync14(fromArgv)) {
3354
+ return fromArgv;
3355
+ }
3356
+ const npmResult = spawnSync2(useNpmShell2 ? "npm.cmd" : "npm", ["root", "-g"], {
3357
+ encoding: "utf8",
3358
+ shell: useNpmShell2,
3359
+ stdio: ["ignore", "pipe", "pipe"]
3360
+ });
3361
+ if (npmResult.status === 0) {
3362
+ const globalRoot = npmResult.stdout?.toString().trim();
3363
+ if (globalRoot) {
3364
+ const candidate = join15(globalRoot, CLI_PACKAGE_NAME, "dist", "index.js");
3365
+ if (existsSync14(candidate)) {
3366
+ return candidate;
3367
+ }
3368
+ }
3369
+ }
3370
+ if (fromArgv) {
3371
+ return fromArgv;
3372
+ }
3373
+ console.error("[apm] \u65E0\u6CD5\u89E3\u6790 apm \u5165\u53E3\u8DEF\u5F84");
3374
+ process.exit(1);
3375
+ }
3376
+ function isRunningUnderPm2() {
3377
+ return process.env.name === PM2_APP_NAME && process.env.pm_id !== void 0;
3378
+ }
3379
+ function runPm2(args, options) {
3380
+ let pm2Bin;
3381
+ try {
3382
+ pm2Bin = resolvePm2Bin();
3383
+ } catch {
3384
+ console.error(
3385
+ "[apm] \u672A\u627E\u5230\u5185\u7F6E pm2\uFF0C\u8BF7\u91CD\u65B0\u5B89\u88C5 apm CLI\uFF1Anpm install -g ai-project-manage-cli@latest"
3386
+ );
3387
+ process.exit(1);
3388
+ }
3389
+ const spawnOptions = {
3390
+ encoding: "utf8",
3391
+ stdio: options?.inherit ? "inherit" : ["ignore", "pipe", "pipe"]
3392
+ };
3393
+ const result = spawnSync2(process.execPath, [pm2Bin, ...args], spawnOptions);
3394
+ if (result.error) {
3395
+ console.error("[apm] \u6267\u884C pm2 \u5931\u8D25:", result.error.message);
3396
+ process.exit(1);
3397
+ }
3398
+ if (result.status !== 0) {
3399
+ const stderr = result.stderr?.toString().trim();
3400
+ const stdout = result.stdout?.toString().trim();
3401
+ const detail = stderr || stdout || `exit code ${result.status}`;
3402
+ console.error(`[apm] pm2 ${args.join(" ")} \u5931\u8D25: ${detail}`);
3403
+ process.exit(result.status ?? 1);
3404
+ }
3405
+ }
3406
+ function runPm2Json(args) {
3407
+ let pm2Bin;
3408
+ try {
3409
+ pm2Bin = resolvePm2Bin();
3410
+ } catch {
3411
+ return "[]";
3412
+ }
3413
+ const result = spawnSync2(process.execPath, [pm2Bin, ...args], {
3414
+ encoding: "utf8",
3415
+ stdio: ["ignore", "pipe", "pipe"]
3416
+ });
3417
+ if (result.status !== 0) return "[]";
3418
+ return result.stdout?.toString() ?? "[]";
3419
+ }
3420
+ function isPm2ConnectOnline() {
3421
+ try {
3422
+ const raw = runPm2Json(["jlist"]);
3423
+ const list = JSON.parse(raw);
3424
+ return list.some(
3425
+ (app) => app.name === PM2_APP_NAME && (app.pm2_env?.status === "online" || app.pm2_env?.status === "launching")
3426
+ );
3427
+ } catch {
3428
+ return false;
3429
+ }
3430
+ }
3431
+ function assertConnectNotRunning() {
3432
+ pruneStaleConnectLock();
3433
+ const lock = readConnectLock();
3434
+ if (lock && isProcessAlive(lock.pid)) {
3435
+ console.error(
3436
+ `[apm] \u5DF2\u6709 apm connect \u5728\u8FD0\u884C (pid=${lock.pid}, mode=${lock.mode})\uFF0C\u8BF7\u5148\u505C\u6B62\u540E\u518D\u542F\u52A8`
3437
+ );
3438
+ process.exit(1);
3439
+ }
3440
+ if (isPm2ConnectOnline()) {
3441
+ console.error(
3442
+ "[apm] \u5DF2\u6709 apm connect \u5B88\u62A4\u8FDB\u7A0B\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u6267\u884C apm daemon stop"
3443
+ );
3444
+ process.exit(1);
3445
+ }
3446
+ }
3447
+ function resolveRuntimePaths() {
3448
+ return {
3449
+ apmScript: resolveApmEntryPath(),
3450
+ nodePath: process.execPath
3451
+ };
3452
+ }
3453
+ function reexecConnectWithResolvedPath(options) {
3454
+ const apmScript = resolveApmEntryPath();
3455
+ const args = [apmScript, "connect"];
3456
+ const server = options.server?.trim();
3457
+ if (server) {
3458
+ args.push("--server", server);
3459
+ }
3460
+ const result = spawnSync2(process.execPath, args, { stdio: "inherit" });
3461
+ if (result.error) {
3462
+ console.error("[apm] \u91CD\u542F connect \u5931\u8D25:", result.error.message);
3463
+ process.exit(1);
3464
+ }
3465
+ process.exit(result.status ?? 0);
3466
+ }
3467
+ async function handleConnectAfterUpdate(options) {
3468
+ console.log("[apm] \u66F4\u65B0\u5B8C\u6210\uFF0C\u6B63\u5728\u4EE5\u65B0\u7248\u672C\u91CD\u65B0\u8FDE\u63A5\u2026");
3469
+ await prepareEcosystem(options.server);
3470
+ if (isRunningUnderPm2()) {
3471
+ runPm2(["startOrRestart", PM2_ECOSYSTEM_PATH, "--update-env"], {
3472
+ inherit: true
3473
+ });
3474
+ console.log("[apm] \u5DF2\u901A\u77E5 PM2 \u4F7F\u7528\u65B0\u7248\u672C\u91CD\u542F connect");
3475
+ process.exit(0);
3476
+ }
3477
+ reexecConnectWithResolvedPath(options);
3478
+ }
3479
+ async function resolveBaseUrl(server) {
3480
+ if (server?.trim()) {
3481
+ return server.trim().replace(/\/+$/, "");
3482
+ }
3483
+ const cfg = await tryReadApmConfig();
3484
+ return cfg?.baseUrl;
3485
+ }
3486
+ function writeEcosystemFile(options) {
3487
+ mkdirSync7(APM_CONFIG_DIR, { recursive: true });
3488
+ const ecosystem = buildConnectPm2Ecosystem(options);
3489
+ writeFileSync13(
3490
+ PM2_ECOSYSTEM_PATH,
3491
+ formatConnectPm2EcosystemFile(ecosystem),
3492
+ "utf8"
3493
+ );
3494
+ }
3495
+ async function prepareEcosystem(server) {
3496
+ await ensureLoggedConfig();
3497
+ const cfg = await ensureApmConfig();
3498
+ const { apmScript, nodePath } = resolveRuntimePaths();
3499
+ const connectArgs = resolveConnectArgs(server);
3500
+ const baseUrl = await resolveBaseUrl(server);
3501
+ writeEcosystemFile({
3502
+ apmScript,
3503
+ nodePath,
3504
+ connectArgs,
3505
+ baseUrl
3506
+ });
3507
+ return cfg;
3508
+ }
3509
+ async function runDaemonStart(options) {
3510
+ assertConnectNotRunning();
3511
+ await runUpdate();
3512
+ const cfg = await prepareEcosystem(options.server);
3513
+ runPm2(["start", PM2_ECOSYSTEM_PATH, "--update-env"], { inherit: true });
3514
+ console.log(
3515
+ `[apm] ${PM2_APP_NAME} \u5DF2\u7531 PM2 \u542F\u52A8\uFF08server=${options.server?.trim() || cfg.baseUrl}\uFF09`
3516
+ );
3517
+ if (options.follow) {
3518
+ console.log(
3519
+ "[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"
3520
+ );
3521
+ runDaemonLogs({ follow: true });
3522
+ return;
3523
+ }
3524
+ console.log("[apm] \u67E5\u770B\u72B6\u6001: apm daemon status");
3525
+ console.log("[apm] \u67E5\u770B\u65E5\u5FD7: apm daemon logs -f");
3526
+ console.log("[apm] \u505C\u6B62: apm daemon stop");
3527
+ }
3528
+ async function runDaemonStop() {
3529
+ runPm2(["stop", PM2_APP_NAME], { inherit: true });
3530
+ forceReleaseConnectLock();
3531
+ console.log(`[apm] ${PM2_APP_NAME} \u5DF2\u505C\u6B62`);
3532
+ }
3533
+ async function runDaemonRestart(options) {
3534
+ pruneStaleConnectLock();
3535
+ const lock = readConnectLock();
3536
+ if (lock && isProcessAlive(lock.pid) && !isPm2ConnectOnline()) {
3537
+ console.error(
3538
+ `[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`
3539
+ );
3540
+ process.exit(1);
3541
+ }
3542
+ await runUpdate();
3543
+ await prepareEcosystem(options.server);
3544
+ runPm2(["startOrRestart", PM2_ECOSYSTEM_PATH, "--update-env"], {
3545
+ inherit: true
3546
+ });
3547
+ console.log(`[apm] ${PM2_APP_NAME} \u5DF2\u91CD\u542F`);
3548
+ }
3549
+ async function runDaemonDelete() {
3550
+ runPm2(["delete", PM2_APP_NAME], { inherit: true });
3551
+ forceReleaseConnectLock();
3552
+ console.log(`[apm] ${PM2_APP_NAME} \u5DF2\u4ECE PM2 \u79FB\u9664`);
3553
+ }
3554
+ async function runDaemonStatus() {
3555
+ runPm2(["describe", PM2_APP_NAME], { inherit: true });
3556
+ }
3557
+ async function runDaemonLogs(options) {
3558
+ const args = ["logs", PM2_APP_NAME, "--lines", String(options.lines ?? 100)];
3559
+ if (!options.follow) {
3560
+ args.push("--nostream");
3561
+ }
3562
+ runPm2(args, { inherit: true });
3563
+ }
3564
+
3235
3565
  // src/commands/connect.ts
3236
3566
  var HEARTBEAT_MS = 3e4;
3237
3567
  async function updateMessageStatus(cfg, messageId, status) {
@@ -3401,23 +3731,10 @@ function startHeartbeat(ws, clientMachineId) {
3401
3731
  const timer = setInterval(send, HEARTBEAT_MS);
3402
3732
  return () => clearInterval(timer);
3403
3733
  }
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
3734
  async function runConnect(options) {
3418
3735
  const { didUpdate } = await runUpdate();
3419
3736
  if (didUpdate) {
3420
- reexecConnect(options);
3737
+ await handleConnectAfterUpdate(options);
3421
3738
  }
3422
3739
  const cfg = await ensureLoggedConfig();
3423
3740
  if (options.server?.trim()) {
@@ -3428,6 +3745,9 @@ async function runConnect(options) {
3428
3745
  console.error("[apm] config \u7F3A\u5C11 clientMachineId\uFF0C\u8BF7\u91CD\u65B0 apm login");
3429
3746
  process.exit(1);
3430
3747
  }
3748
+ assertConnectNotRunning();
3749
+ acquireConnectLock("foreground");
3750
+ process.on("exit", releaseConnectLock);
3431
3751
  const url = buildAgentWsUrl(cfg.baseUrl, resolveApiKey(cfg));
3432
3752
  console.log(`[apm] \u8FDE\u63A5 ${cfg.baseUrl} \u2026`);
3433
3753
  await new Promise((resolve5, reject) => {
@@ -3459,6 +3779,7 @@ async function runConnect(options) {
3459
3779
  ]);
3460
3780
  } catch {
3461
3781
  }
3782
+ releaseConnectLock();
3462
3783
  resolve5();
3463
3784
  process.exit(code);
3464
3785
  };
@@ -3615,20 +3936,20 @@ async function runCreatePr(options) {
3615
3936
  import { spawnSync as spawnSync5 } from "node:child_process";
3616
3937
 
3617
3938
  // src/commands/deploy/internal/apm-config.ts
3618
- import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
3939
+ import { existsSync as existsSync15, readFileSync as readFileSync12 } from "node:fs";
3619
3940
  import { homedir as homedir2 } from "node:os";
3620
- import { join as join14, resolve as resolve4 } from "node:path";
3941
+ import { join as join16, resolve as resolve4 } from "node:path";
3621
3942
  function loadApmConfig(options) {
3622
3943
  const p = resolve4(
3623
3944
  process.cwd(),
3624
3945
  options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
3625
3946
  );
3626
- if (!existsSync13(p)) {
3947
+ if (!existsSync15(p)) {
3627
3948
  console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
3628
3949
  process.exit(1);
3629
3950
  }
3630
3951
  try {
3631
- const raw = readFileSync11(p, "utf8");
3952
+ const raw = readFileSync12(p, "utf8");
3632
3953
  return JSON.parse(raw);
3633
3954
  } catch (e) {
3634
3955
  console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
@@ -3812,12 +4133,12 @@ function readMavenLocalRepoFromEnv(env = process.env) {
3812
4133
  return null;
3813
4134
  }
3814
4135
  function readMavenLocalRepoFromSettings() {
3815
- const settingsPath = join14(homedir2(), ".m2", "settings.xml");
3816
- if (!existsSync13(settingsPath)) {
4136
+ const settingsPath = join16(homedir2(), ".m2", "settings.xml");
4137
+ if (!existsSync15(settingsPath)) {
3817
4138
  return null;
3818
4139
  }
3819
4140
  try {
3820
- const xml = readFileSync11(settingsPath, "utf8");
4141
+ const xml = readFileSync12(settingsPath, "utf8");
3821
4142
  const match = xml.match(
3822
4143
  /<localRepository>\s*([^<]+?)\s*<\/localRepository>/
3823
4144
  );
@@ -3848,7 +4169,7 @@ function resolveMavenLocalRepoWithSource() {
3848
4169
  };
3849
4170
  }
3850
4171
  return {
3851
- path: join14(homedir2(), ".m2", "repository"),
4172
+ path: join16(homedir2(), ".m2", "repository"),
3852
4173
  source: "default",
3853
4174
  sourceDetail: "~/.m2/repository"
3854
4175
  };
@@ -3918,18 +4239,18 @@ function posixBasename(p) {
3918
4239
  }
3919
4240
 
3920
4241
  // src/commands/deploy/internal/wisdom-auto-deploy.ts
3921
- import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
4242
+ import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
3922
4243
  import path3 from "node:path";
3923
4244
  import { spawnSync as spawnSync4 } from "node:child_process";
3924
4245
 
3925
4246
  // src/commands/deploy/internal/wisdom-backend-deploy.ts
3926
4247
  import {
3927
- existsSync as existsSync14,
3928
- mkdirSync as mkdirSync6,
4248
+ existsSync as existsSync16,
4249
+ mkdirSync as mkdirSync8,
3929
4250
  readdirSync as readdirSync5,
3930
- readFileSync as readFileSync12,
4251
+ readFileSync as readFileSync13,
3931
4252
  statSync as statSync5,
3932
- writeFileSync as writeFileSync12
4253
+ writeFileSync as writeFileSync14
3933
4254
  } from "node:fs";
3934
4255
  import { spawnSync as spawnSync3 } from "node:child_process";
3935
4256
  import path2 from "node:path";
@@ -4136,15 +4457,15 @@ function fileSignature(filePath) {
4136
4457
  }
4137
4458
  function loadManifest4() {
4138
4459
  const manifestPath2 = manifestFilePath();
4139
- if (!existsSync14(manifestPath2)) {
4460
+ if (!existsSync16(manifestPath2)) {
4140
4461
  return {};
4141
4462
  }
4142
- return JSON.parse(readFileSync12(manifestPath2, "utf8"));
4463
+ return JSON.parse(readFileSync13(manifestPath2, "utf8"));
4143
4464
  }
4144
4465
  function saveManifest4(manifest) {
4145
4466
  const dir = deployCacheDir();
4146
- mkdirSync6(dir, { recursive: true });
4147
- writeFileSync12(manifestFilePath(), JSON.stringify(manifest, null, 2), "utf8");
4467
+ mkdirSync8(dir, { recursive: true });
4468
+ writeFileSync14(manifestFilePath(), JSON.stringify(manifest, null, 2), "utf8");
4148
4469
  }
4149
4470
  function isProjectLibJar(jarName) {
4150
4471
  return jarName.startsWith("jeecg-");
@@ -4200,12 +4521,12 @@ function updateManifestEntries(manifest, entries, projectRoot) {
4200
4521
  }
4201
4522
  async function createUpdatePackage(entries, packageName) {
4202
4523
  const dir = deployCacheDir();
4203
- mkdirSync6(dir, { recursive: true });
4524
+ mkdirSync8(dir, { recursive: true });
4204
4525
  const zipPath = path2.join(dir, packageName);
4205
4526
  log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${path2.basename(zipPath)}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
4206
4527
  const zip = new JSZip2();
4207
4528
  for (const entry of entries) {
4208
- const content = readFileSync12(entry.path);
4529
+ const content = readFileSync13(entry.path);
4209
4530
  zip.file(entry.arcname, content);
4210
4531
  log(` \u6253\u5305: ${entry.arcname} (${entry.reason})`);
4211
4532
  }
@@ -4214,7 +4535,7 @@ async function createUpdatePackage(entries, packageName) {
4214
4535
  compression: "DEFLATE",
4215
4536
  compressionOptions: { level: 6 }
4216
4537
  });
4217
- writeFileSync12(zipPath, buffer);
4538
+ writeFileSync14(zipPath, buffer);
4218
4539
  return zipPath;
4219
4540
  }
4220
4541
  function getMvnExecutable() {
@@ -4263,11 +4584,11 @@ function runMavenBuild(projectRoot, mavenLocalRepo, repoSource) {
4263
4584
  }
4264
4585
  function locateLibDir(projectRoot) {
4265
4586
  const targetDir = getTargetDir(projectRoot);
4266
- if (!existsSync14(targetDir)) {
4587
+ if (!existsSync16(targetDir)) {
4267
4588
  fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
4268
4589
  }
4269
4590
  const libDir = path2.join(targetDir, "lib");
4270
- if (!existsSync14(libDir) || !statSync5(libDir).isDirectory()) {
4591
+ if (!existsSync16(libDir) || !statSync5(libDir).isDirectory()) {
4271
4592
  fail(`lib \u76EE\u5F55\u4E0D\u5B58\u5728: ${libDir}`);
4272
4593
  }
4273
4594
  const libJars = readdirSync5(libDir).filter((name) => name.endsWith(".jar"));
@@ -4279,7 +4600,7 @@ function locateLibDir(projectRoot) {
4279
4600
  }
4280
4601
  function locateMainJar(projectRoot) {
4281
4602
  const targetDir = getTargetDir(projectRoot);
4282
- if (!existsSync14(targetDir)) {
4603
+ if (!existsSync16(targetDir)) {
4283
4604
  fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
4284
4605
  }
4285
4606
  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 +5019,15 @@ function isWisdomLegacyDeploy(cfg) {
4698
5019
  return !hasNewFrontend && !hasNewBackend;
4699
5020
  }
4700
5021
  function detectWisdomProjectType(cwd) {
4701
- return existsSync15(path3.join(cwd, "package.json")) ? "frontend" : "backend";
5022
+ return existsSync17(path3.join(cwd, "package.json")) ? "frontend" : "backend";
4702
5023
  }
4703
5024
  function readPackageScripts(cwd) {
4704
5025
  const pkgPath = path3.join(cwd, "package.json");
4705
- if (!existsSync15(pkgPath)) {
5026
+ if (!existsSync17(pkgPath)) {
4706
5027
  return {};
4707
5028
  }
4708
5029
  try {
4709
- const raw = readFileSync13(pkgPath, "utf8");
5030
+ const raw = readFileSync14(pkgPath, "utf8");
4710
5031
  const parsed = JSON.parse(raw);
4711
5032
  return parsed.scripts ?? {};
4712
5033
  } catch {
@@ -4816,7 +5137,7 @@ import path7 from "node:path";
4816
5137
  import Docker from "dockerode";
4817
5138
 
4818
5139
  // src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
4819
- import { existsSync as existsSync16, readFileSync as readFileSync14 } from "node:fs";
5140
+ import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
4820
5141
  import path4 from "node:path";
4821
5142
  function asOptionalTlsBuffer(value) {
4822
5143
  if (typeof value !== "string") {
@@ -4828,8 +5149,8 @@ function asOptionalTlsBuffer(value) {
4828
5149
  if (normalized === "") {
4829
5150
  return void 0;
4830
5151
  }
4831
- if (existsSync16(normalized)) {
4832
- return readFileSync14(normalized);
5152
+ if (existsSync18(normalized)) {
5153
+ return readFileSync15(normalized);
4833
5154
  }
4834
5155
  const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
4835
5156
  if (looksLikePath) {
@@ -5039,7 +5360,7 @@ var DockerodeClient = class {
5039
5360
  var createDockerodeClient = (config) => new DockerodeClient(config);
5040
5361
 
5041
5362
  // src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
5042
- import { existsSync as existsSync17, readFileSync as readFileSync15, statSync as statSync6 } from "node:fs";
5363
+ import { existsSync as existsSync19, readFileSync as readFileSync16, statSync as statSync6 } from "node:fs";
5043
5364
  import path5 from "node:path";
5044
5365
  function stripSurroundingQuotes(value) {
5045
5366
  const t = value.trim();
@@ -5056,10 +5377,10 @@ function loadEnvFromFile(envFilePath) {
5056
5377
  return {};
5057
5378
  }
5058
5379
  const targetPath = path5.resolve(envFilePath);
5059
- if (!existsSync17(targetPath) || !statSync6(targetPath).isFile()) {
5380
+ if (!existsSync19(targetPath) || !statSync6(targetPath).isFile()) {
5060
5381
  return {};
5061
5382
  }
5062
- const raw = readFileSync15(targetPath, "utf-8");
5383
+ const raw = readFileSync16(targetPath, "utf-8");
5063
5384
  const result = {};
5064
5385
  for (const line of raw.split(/\r?\n/)) {
5065
5386
  const normalized = line.trim();
@@ -5230,12 +5551,12 @@ function dockerPushImage(params, cwd) {
5230
5551
  }
5231
5552
 
5232
5553
  // src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
5233
- import { existsSync as existsSync18 } from "node:fs";
5554
+ import { existsSync as existsSync20 } from "node:fs";
5234
5555
  import path6 from "node:path";
5235
5556
  function resolveDockerBuildPaths(cwd) {
5236
5557
  const dockerfilePath = path6.join(cwd, "Dockerfile");
5237
5558
  Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
5238
- if (!existsSync18(dockerfilePath)) {
5559
+ if (!existsSync20(dockerfilePath)) {
5239
5560
  throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
5240
5561
  }
5241
5562
  Logger.info("\u2713 Dockerfile \u5B58\u5728");
@@ -5785,8 +6106,46 @@ function buildProgram() {
5785
6106
  });
5786
6107
  program.command("connect").description(
5787
6108
  "\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);
6109
+ ).option("--server <url>", "API \u6839\u5730\u5740\uFF0C\u8986\u76D6 config \u4E2D\u7684 baseUrl").option(
6110
+ "--daemon",
6111
+ "\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"
6112
+ ).option("-f, --follow", "\u4E0E --daemon \u5408\u7528\uFF1A\u542F\u52A8\u540E\u5728\u5F53\u524D\u7EC8\u7AEF\u5B9E\u65F6\u8DDF\u8E2A\u65E5\u5FD7").action(
6113
+ async (opts) => {
6114
+ if (opts.follow && !opts.daemon) {
6115
+ console.error("[apm] --follow \u4EC5\u53EF\u4E0E --daemon \u540C\u65F6\u4F7F\u7528");
6116
+ process.exit(1);
6117
+ }
6118
+ if (opts.daemon === true) {
6119
+ await runDaemonStart({ server: opts.server, follow: opts.follow });
6120
+ return;
6121
+ }
6122
+ await runConnect({ server: opts.server });
6123
+ }
6124
+ );
6125
+ const daemon = program.command("daemon").description(
6126
+ "\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"
6127
+ );
6128
+ 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) => {
6129
+ await runDaemonStart({ server: opts.server, follow: opts.follow });
6130
+ });
6131
+ daemon.command("stop").description("\u505C\u6B62 PM2 \u5B88\u62A4\u7684 apm connect").action(async () => {
6132
+ await runDaemonStop();
6133
+ });
6134
+ 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) => {
6135
+ await runDaemonRestart(opts);
6136
+ });
6137
+ daemon.command("delete").description("\u4ECE PM2 \u4E2D\u79FB\u9664 apm connect \u8FDB\u7A0B").action(async () => {
6138
+ await runDaemonDelete();
6139
+ });
6140
+ daemon.command("status").description("\u67E5\u770B PM2 \u5B88\u62A4\u7684 apm connect \u72B6\u6001").action(async () => {
6141
+ await runDaemonStatus();
6142
+ });
6143
+ 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) => {
6144
+ const lines = Number.parseInt(opts.lines ?? "100", 10);
6145
+ await runDaemonLogs({
6146
+ follow: opts.follow === true,
6147
+ lines: Number.isFinite(lines) && lines > 0 ? lines : 100
6148
+ });
5790
6149
  });
5791
6150
  program.command("branch").description("\u5207\u6362\u6216\u521B\u5EFA\u4F1A\u8BDD\u5206\u652F feat/session-<sessionId>").argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").option(
5792
6151
  "-m, --message <text>",
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.80",
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
  }