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.
- package/README.md +22 -0
- package/dist/index.js +410 -51
- 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
|
-
|
|
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
|
|
3939
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "node:fs";
|
|
3619
3940
|
import { homedir as homedir2 } from "node:os";
|
|
3620
|
-
import { join as
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
3816
|
-
if (!
|
|
4136
|
+
const settingsPath = join16(homedir2(), ".m2", "settings.xml");
|
|
4137
|
+
if (!existsSync15(settingsPath)) {
|
|
3817
4138
|
return null;
|
|
3818
4139
|
}
|
|
3819
4140
|
try {
|
|
3820
|
-
const xml =
|
|
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:
|
|
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
|
|
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
|
|
3928
|
-
mkdirSync as
|
|
4248
|
+
existsSync as existsSync16,
|
|
4249
|
+
mkdirSync as mkdirSync8,
|
|
3929
4250
|
readdirSync as readdirSync5,
|
|
3930
|
-
readFileSync as
|
|
4251
|
+
readFileSync as readFileSync13,
|
|
3931
4252
|
statSync as statSync5,
|
|
3932
|
-
writeFileSync as
|
|
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 (!
|
|
4460
|
+
if (!existsSync16(manifestPath2)) {
|
|
4140
4461
|
return {};
|
|
4141
4462
|
}
|
|
4142
|
-
return JSON.parse(
|
|
4463
|
+
return JSON.parse(readFileSync13(manifestPath2, "utf8"));
|
|
4143
4464
|
}
|
|
4144
4465
|
function saveManifest4(manifest) {
|
|
4145
4466
|
const dir = deployCacheDir();
|
|
4146
|
-
|
|
4147
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
5026
|
+
if (!existsSync17(pkgPath)) {
|
|
4706
5027
|
return {};
|
|
4707
5028
|
}
|
|
4708
5029
|
try {
|
|
4709
|
-
const raw =
|
|
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
|
|
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 (
|
|
4832
|
-
return
|
|
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
|
|
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 (!
|
|
5380
|
+
if (!existsSync19(targetPath) || !statSync6(targetPath).isFile()) {
|
|
5060
5381
|
return {};
|
|
5061
5382
|
}
|
|
5062
|
-
const raw =
|
|
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
|
|
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 (!
|
|
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").
|
|
5789
|
-
|
|
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.
|
|
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
|
}
|