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.
- package/README.md +23 -0
- package/dist/index.js +476 -83
- 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
|
|
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
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
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
|
-
|
|
1193
|
-
|
|
1194
|
-
const
|
|
1195
|
-
if (
|
|
1196
|
-
|
|
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
|
-
|
|
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
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1322
|
-
|
|
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
|
-
|
|
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
|
|
3966
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "node:fs";
|
|
3619
3967
|
import { homedir as homedir2 } from "node:os";
|
|
3620
|
-
import { join as
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
3816
|
-
if (!
|
|
4163
|
+
const settingsPath = join16(homedir2(), ".m2", "settings.xml");
|
|
4164
|
+
if (!existsSync15(settingsPath)) {
|
|
3817
4165
|
return null;
|
|
3818
4166
|
}
|
|
3819
4167
|
try {
|
|
3820
|
-
const xml =
|
|
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:
|
|
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
|
|
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
|
|
3928
|
-
mkdirSync as
|
|
4275
|
+
existsSync as existsSync16,
|
|
4276
|
+
mkdirSync as mkdirSync8,
|
|
3929
4277
|
readdirSync as readdirSync5,
|
|
3930
|
-
readFileSync as
|
|
4278
|
+
readFileSync as readFileSync13,
|
|
3931
4279
|
statSync as statSync5,
|
|
3932
|
-
writeFileSync as
|
|
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 (!
|
|
4487
|
+
if (!existsSync16(manifestPath2)) {
|
|
4140
4488
|
return {};
|
|
4141
4489
|
}
|
|
4142
|
-
return JSON.parse(
|
|
4490
|
+
return JSON.parse(readFileSync13(manifestPath2, "utf8"));
|
|
4143
4491
|
}
|
|
4144
4492
|
function saveManifest4(manifest) {
|
|
4145
4493
|
const dir = deployCacheDir();
|
|
4146
|
-
|
|
4147
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
5053
|
+
if (!existsSync17(pkgPath)) {
|
|
4706
5054
|
return {};
|
|
4707
5055
|
}
|
|
4708
5056
|
try {
|
|
4709
|
-
const raw =
|
|
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
|
|
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 (
|
|
4832
|
-
return
|
|
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
|
|
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 (!
|
|
5407
|
+
if (!existsSync19(targetPath) || !statSync6(targetPath).isFile()) {
|
|
5060
5408
|
return {};
|
|
5061
5409
|
}
|
|
5062
|
-
const raw =
|
|
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
|
|
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 (!
|
|
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").
|
|
5789
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
}
|