ai-project-manage-cli 6.0.93 → 6.0.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +351 -168
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2216,6 +2216,7 @@ async function runUpdateMessageStatus(options) {
|
|
|
2216
2216
|
|
|
2217
2217
|
// src/commands/connect.ts
|
|
2218
2218
|
import WebSocket from "ws";
|
|
2219
|
+
import { setTimeout as delay2 } from "node:timers/promises";
|
|
2219
2220
|
|
|
2220
2221
|
// src/ws/protocol.ts
|
|
2221
2222
|
function nonEmptyString(v) {
|
|
@@ -3654,6 +3655,7 @@ function forceReleaseConnectLock() {
|
|
|
3654
3655
|
|
|
3655
3656
|
// src/commands/daemon.ts
|
|
3656
3657
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3658
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
3657
3659
|
import { createRequire } from "node:module";
|
|
3658
3660
|
import { existsSync as existsSync14, mkdirSync as mkdirSync7, writeFileSync as writeFileSync13 } from "fs";
|
|
3659
3661
|
import { join as join15 } from "path";
|
|
@@ -3682,10 +3684,12 @@ function buildConnectPm2Ecosystem(options) {
|
|
|
3682
3684
|
args: options.connectArgs,
|
|
3683
3685
|
autorestart: true,
|
|
3684
3686
|
min_uptime: "10s",
|
|
3685
|
-
max_restarts:
|
|
3687
|
+
max_restarts: 0,
|
|
3686
3688
|
restart_delay: 3e3,
|
|
3687
3689
|
exp_backoff_restart_delay: 1e3,
|
|
3688
3690
|
max_memory_restart: "1G",
|
|
3691
|
+
kill_timeout: 5e3,
|
|
3692
|
+
shutdown_with_message: true,
|
|
3689
3693
|
env
|
|
3690
3694
|
}
|
|
3691
3695
|
]
|
|
@@ -3878,10 +3882,44 @@ async function runDaemonStart(options) {
|
|
|
3878
3882
|
console.log("[apm] \u505C\u6B62: apm daemon stop");
|
|
3879
3883
|
}
|
|
3880
3884
|
async function runDaemonStop() {
|
|
3885
|
+
pruneStaleConnectLock();
|
|
3886
|
+
if (!isPm2ConnectOnline()) {
|
|
3887
|
+
killConnectLockProcessIfAlive();
|
|
3888
|
+
forceReleaseConnectLock();
|
|
3889
|
+
console.log(`[apm] ${PM2_APP_NAME} \u672A\u5728\u8FD0\u884C`);
|
|
3890
|
+
return;
|
|
3891
|
+
}
|
|
3881
3892
|
runPm2(["stop", PM2_APP_NAME], { inherit: true });
|
|
3893
|
+
for (let i = 0; i < 10; i += 1) {
|
|
3894
|
+
if (!isPm2ConnectOnline()) break;
|
|
3895
|
+
await delay(500);
|
|
3896
|
+
}
|
|
3897
|
+
if (isPm2ConnectOnline()) {
|
|
3898
|
+
console.log(`[apm] \u4F18\u96C5\u505C\u6B62\u8D85\u65F6\uFF0C\u5F3A\u5236\u79FB\u9664 ${PM2_APP_NAME}\u2026`);
|
|
3899
|
+
runPm2(["delete", PM2_APP_NAME], { inherit: true });
|
|
3900
|
+
}
|
|
3901
|
+
killConnectLockProcessIfAlive();
|
|
3882
3902
|
forceReleaseConnectLock();
|
|
3883
3903
|
console.log(`[apm] ${PM2_APP_NAME} \u5DF2\u505C\u6B62`);
|
|
3884
3904
|
}
|
|
3905
|
+
function killConnectLockProcessIfAlive() {
|
|
3906
|
+
pruneStaleConnectLock();
|
|
3907
|
+
const lock = readConnectLock();
|
|
3908
|
+
if (!lock || !isProcessAlive(lock.pid)) return;
|
|
3909
|
+
try {
|
|
3910
|
+
process.kill(lock.pid, "SIGTERM");
|
|
3911
|
+
} catch {
|
|
3912
|
+
forceReleaseConnectLock();
|
|
3913
|
+
return;
|
|
3914
|
+
}
|
|
3915
|
+
if (isProcessAlive(lock.pid)) {
|
|
3916
|
+
try {
|
|
3917
|
+
process.kill(lock.pid, "SIGKILL");
|
|
3918
|
+
} catch {
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
forceReleaseConnectLock();
|
|
3922
|
+
}
|
|
3885
3923
|
async function runDaemonRestart(options) {
|
|
3886
3924
|
pruneStaleConnectLock();
|
|
3887
3925
|
const lock = readConnectLock();
|
|
@@ -3916,6 +3954,8 @@ async function runDaemonLogs(options) {
|
|
|
3916
3954
|
|
|
3917
3955
|
// src/commands/connect.ts
|
|
3918
3956
|
var HEARTBEAT_MS = 3e4;
|
|
3957
|
+
var RECONNECT_MIN_MS = 1e3;
|
|
3958
|
+
var RECONNECT_MAX_MS = 6e4;
|
|
3919
3959
|
async function updateMessageStatus(cfg, messageId, status) {
|
|
3920
3960
|
const api = createApmApiClient(cfg);
|
|
3921
3961
|
await api.cli.updateMessageStatus({ id: messageId, status });
|
|
@@ -4068,6 +4108,17 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
|
|
|
4068
4108
|
}
|
|
4069
4109
|
}
|
|
4070
4110
|
}
|
|
4111
|
+
function interruptibleSleep(ms, signal) {
|
|
4112
|
+
if (signal.aborted) return Promise.resolve();
|
|
4113
|
+
return new Promise((resolve5) => {
|
|
4114
|
+
const timer = setTimeout(resolve5, ms);
|
|
4115
|
+
const onAbort = () => {
|
|
4116
|
+
clearTimeout(timer);
|
|
4117
|
+
resolve5();
|
|
4118
|
+
};
|
|
4119
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
4120
|
+
});
|
|
4121
|
+
}
|
|
4071
4122
|
function startHeartbeat(ws, clientMachineId) {
|
|
4072
4123
|
const send = () => {
|
|
4073
4124
|
if (ws.readyState === WebSocket.OPEN) {
|
|
@@ -4083,182 +4134,274 @@ function startHeartbeat(ws, clientMachineId) {
|
|
|
4083
4134
|
const timer = setInterval(send, HEARTBEAT_MS);
|
|
4084
4135
|
return () => clearInterval(timer);
|
|
4085
4136
|
}
|
|
4086
|
-
|
|
4087
|
-
const {
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
const
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
);
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
try {
|
|
4128
|
-
await Promise.race([
|
|
4129
|
-
Promise.all(activeTasks),
|
|
4130
|
-
new Promise((r) => setTimeout(r, SHUTDOWN_DRAIN_MS))
|
|
4131
|
-
]);
|
|
4132
|
-
} catch {
|
|
4133
|
-
}
|
|
4134
|
-
releaseConnectLock();
|
|
4135
|
-
resolve5();
|
|
4136
|
-
process.exit(code);
|
|
4137
|
-
};
|
|
4138
|
-
ws.on("open", () => {
|
|
4139
|
-
console.log("[apm] WebSocket \u5DF2\u8FDE\u63A5");
|
|
4140
|
-
stopHeartbeat = startHeartbeat(ws, clientMachineId);
|
|
4141
|
-
});
|
|
4142
|
-
ws.on("message", (data) => {
|
|
4143
|
-
if (shuttingDown) return;
|
|
4144
|
-
const text = Buffer.isBuffer(data) ? data.toString("utf8") : String(data);
|
|
4145
|
-
const parsed = parseAgentWsMessage(text);
|
|
4146
|
-
if (parsed === null) {
|
|
4147
|
-
console.error("[apm] \u6536\u5230\u65E0\u6548 JSON");
|
|
4148
|
-
return;
|
|
4149
|
-
}
|
|
4150
|
-
if (typeof parsed === "object" && parsed !== null && parsed.type === "heartbeat") {
|
|
4151
|
-
return;
|
|
4152
|
-
}
|
|
4153
|
-
const validated = validateAgentWsMessage(parsed, "outbound");
|
|
4154
|
-
if (!validated.ok) {
|
|
4155
|
-
console.error(`[apm] \u6536\u5230\u65E0\u6548 WS \u5305: ${validated.reason}`);
|
|
4156
|
-
return;
|
|
4157
|
-
}
|
|
4158
|
-
if (validated.data.type === "cancel") {
|
|
4159
|
-
const { messageId } = validated.data;
|
|
4160
|
-
pendingCancels.add(messageId);
|
|
4161
|
-
activeRuns.get(messageId)?.abort();
|
|
4162
|
-
return;
|
|
4163
|
-
}
|
|
4164
|
-
if (validated.data.type === "deploy") {
|
|
4165
|
-
const msg2 = validated.data;
|
|
4166
|
-
const perDeployController = new AbortController();
|
|
4167
|
-
const signal2 = AbortSignal.any([
|
|
4168
|
-
shutdownAbort.signal,
|
|
4169
|
-
perDeployController.signal
|
|
4170
|
-
]);
|
|
4171
|
-
const task2 = (async () => {
|
|
4172
|
-
await runSlots.acquire();
|
|
4173
|
-
try {
|
|
4174
|
-
await handleInboundDeploy(cfg, msg2, signal2);
|
|
4175
|
-
} finally {
|
|
4176
|
-
runSlots.release();
|
|
4177
|
-
}
|
|
4178
|
-
})();
|
|
4179
|
-
activeTasks.add(task2);
|
|
4180
|
-
void task2.finally(() => {
|
|
4181
|
-
activeTasks.delete(task2);
|
|
4182
|
-
});
|
|
4183
|
-
return;
|
|
4184
|
-
}
|
|
4185
|
-
if (validated.data.type !== "message") {
|
|
4186
|
-
return;
|
|
4187
|
-
}
|
|
4188
|
-
const msg = validated.data;
|
|
4189
|
-
const perMessageController = new AbortController();
|
|
4190
|
-
activeRuns.set(msg.messageId, perMessageController);
|
|
4191
|
-
if (pendingCancels.has(msg.messageId)) {
|
|
4192
|
-
activeRuns.delete(msg.messageId);
|
|
4193
|
-
pendingCancels.delete(msg.messageId);
|
|
4194
|
-
return;
|
|
4195
|
-
}
|
|
4196
|
-
const signal = AbortSignal.any([
|
|
4137
|
+
function attachWsHandlers(ws, ctx, onOpen) {
|
|
4138
|
+
const {
|
|
4139
|
+
cfg,
|
|
4140
|
+
clientMachineId,
|
|
4141
|
+
shutdownAbort,
|
|
4142
|
+
runSlots,
|
|
4143
|
+
activeTasks,
|
|
4144
|
+
activeRuns,
|
|
4145
|
+
pendingCancels,
|
|
4146
|
+
isShuttingDown
|
|
4147
|
+
} = ctx;
|
|
4148
|
+
ws.on("open", () => {
|
|
4149
|
+
console.log("[apm] WebSocket \u5DF2\u8FDE\u63A5");
|
|
4150
|
+
onOpen();
|
|
4151
|
+
});
|
|
4152
|
+
ws.on("message", (data) => {
|
|
4153
|
+
if (isShuttingDown()) return;
|
|
4154
|
+
const text = Buffer.isBuffer(data) ? data.toString("utf8") : String(data);
|
|
4155
|
+
const parsed = parseAgentWsMessage(text);
|
|
4156
|
+
if (parsed === null) {
|
|
4157
|
+
console.error("[apm] \u6536\u5230\u65E0\u6548 JSON");
|
|
4158
|
+
return;
|
|
4159
|
+
}
|
|
4160
|
+
if (typeof parsed === "object" && parsed !== null && parsed.type === "heartbeat") {
|
|
4161
|
+
return;
|
|
4162
|
+
}
|
|
4163
|
+
const validated = validateAgentWsMessage(parsed, "outbound");
|
|
4164
|
+
if (!validated.ok) {
|
|
4165
|
+
console.error(`[apm] \u6536\u5230\u65E0\u6548 WS \u5305: ${validated.reason}`);
|
|
4166
|
+
return;
|
|
4167
|
+
}
|
|
4168
|
+
if (validated.data.type === "cancel") {
|
|
4169
|
+
const { messageId } = validated.data;
|
|
4170
|
+
pendingCancels.add(messageId);
|
|
4171
|
+
activeRuns.get(messageId)?.abort();
|
|
4172
|
+
return;
|
|
4173
|
+
}
|
|
4174
|
+
if (validated.data.type === "deploy") {
|
|
4175
|
+
const msg2 = validated.data;
|
|
4176
|
+
const perDeployController = new AbortController();
|
|
4177
|
+
const signal2 = AbortSignal.any([
|
|
4197
4178
|
shutdownAbort.signal,
|
|
4198
|
-
|
|
4179
|
+
perDeployController.signal
|
|
4199
4180
|
]);
|
|
4200
|
-
const
|
|
4201
|
-
shutdownSignal: shutdownAbort.signal,
|
|
4202
|
-
perMessageSignal: perMessageController.signal
|
|
4203
|
-
};
|
|
4204
|
-
const task = (async () => {
|
|
4181
|
+
const task2 = (async () => {
|
|
4205
4182
|
await runSlots.acquire();
|
|
4206
4183
|
try {
|
|
4207
|
-
|
|
4208
|
-
try {
|
|
4209
|
-
await updateMessageStatus(cfg, msg.messageId, "TYPING");
|
|
4210
|
-
} catch (typingErr) {
|
|
4211
|
-
if (isUserCancelled(ctx)) return;
|
|
4212
|
-
console.error(
|
|
4213
|
-
"[apm] \u66F4\u65B0 TYPING \u72B6\u6001\u5931\u8D25:",
|
|
4214
|
-
typingErr instanceof Error ? typingErr.message : typingErr
|
|
4215
|
-
);
|
|
4216
|
-
try {
|
|
4217
|
-
await setMessageError(
|
|
4218
|
-
cfg,
|
|
4219
|
-
msg.messageId,
|
|
4220
|
-
typingErr instanceof Error ? typingErr.message : String(typingErr)
|
|
4221
|
-
);
|
|
4222
|
-
await updateMessageStatus(cfg, msg.messageId, "FAILED");
|
|
4223
|
-
} catch (statusErr) {
|
|
4224
|
-
console.error(
|
|
4225
|
-
"[apm] \u66F4\u65B0 FAILED \u72B6\u6001\u5931\u8D25:",
|
|
4226
|
-
statusErr instanceof Error ? statusErr.message : statusErr
|
|
4227
|
-
);
|
|
4228
|
-
}
|
|
4229
|
-
return;
|
|
4230
|
-
}
|
|
4231
|
-
await handleInboundMessage(cfg, msg, signal, ctx);
|
|
4184
|
+
await handleInboundDeploy(cfg, msg2, signal2);
|
|
4232
4185
|
} finally {
|
|
4233
4186
|
runSlots.release();
|
|
4234
|
-
activeRuns.delete(msg.messageId);
|
|
4235
|
-
pendingCancels.delete(msg.messageId);
|
|
4236
4187
|
}
|
|
4237
4188
|
})();
|
|
4238
|
-
activeTasks.add(
|
|
4239
|
-
void
|
|
4240
|
-
activeTasks.delete(
|
|
4189
|
+
activeTasks.add(task2);
|
|
4190
|
+
void task2.finally(() => {
|
|
4191
|
+
activeTasks.delete(task2);
|
|
4241
4192
|
});
|
|
4193
|
+
return;
|
|
4194
|
+
}
|
|
4195
|
+
if (validated.data.type !== "message") {
|
|
4196
|
+
return;
|
|
4197
|
+
}
|
|
4198
|
+
const msg = validated.data;
|
|
4199
|
+
const perMessageController = new AbortController();
|
|
4200
|
+
activeRuns.set(msg.messageId, perMessageController);
|
|
4201
|
+
if (pendingCancels.has(msg.messageId)) {
|
|
4202
|
+
activeRuns.delete(msg.messageId);
|
|
4203
|
+
pendingCancels.delete(msg.messageId);
|
|
4204
|
+
return;
|
|
4205
|
+
}
|
|
4206
|
+
const signal = AbortSignal.any([
|
|
4207
|
+
shutdownAbort.signal,
|
|
4208
|
+
perMessageController.signal
|
|
4209
|
+
]);
|
|
4210
|
+
const messageCtx = {
|
|
4211
|
+
shutdownSignal: shutdownAbort.signal,
|
|
4212
|
+
perMessageSignal: perMessageController.signal
|
|
4213
|
+
};
|
|
4214
|
+
const task = (async () => {
|
|
4215
|
+
await runSlots.acquire();
|
|
4216
|
+
try {
|
|
4217
|
+
if (signal.aborted || isUserCancelled(messageCtx)) return;
|
|
4218
|
+
try {
|
|
4219
|
+
await updateMessageStatus(cfg, msg.messageId, "TYPING");
|
|
4220
|
+
} catch (typingErr) {
|
|
4221
|
+
if (isUserCancelled(messageCtx)) return;
|
|
4222
|
+
console.error(
|
|
4223
|
+
"[apm] \u66F4\u65B0 TYPING \u72B6\u6001\u5931\u8D25:",
|
|
4224
|
+
typingErr instanceof Error ? typingErr.message : typingErr
|
|
4225
|
+
);
|
|
4226
|
+
try {
|
|
4227
|
+
await setMessageError(
|
|
4228
|
+
cfg,
|
|
4229
|
+
msg.messageId,
|
|
4230
|
+
typingErr instanceof Error ? typingErr.message : String(typingErr)
|
|
4231
|
+
);
|
|
4232
|
+
await updateMessageStatus(cfg, msg.messageId, "FAILED");
|
|
4233
|
+
} catch (statusErr) {
|
|
4234
|
+
console.error(
|
|
4235
|
+
"[apm] \u66F4\u65B0 FAILED \u72B6\u6001\u5931\u8D25:",
|
|
4236
|
+
statusErr instanceof Error ? statusErr.message : statusErr
|
|
4237
|
+
);
|
|
4238
|
+
}
|
|
4239
|
+
return;
|
|
4240
|
+
}
|
|
4241
|
+
await handleInboundMessage(cfg, msg, signal, messageCtx);
|
|
4242
|
+
} finally {
|
|
4243
|
+
runSlots.release();
|
|
4244
|
+
activeRuns.delete(msg.messageId);
|
|
4245
|
+
pendingCancels.delete(msg.messageId);
|
|
4246
|
+
}
|
|
4247
|
+
})();
|
|
4248
|
+
activeTasks.add(task);
|
|
4249
|
+
void task.finally(() => {
|
|
4250
|
+
activeTasks.delete(task);
|
|
4251
|
+
});
|
|
4252
|
+
});
|
|
4253
|
+
}
|
|
4254
|
+
function connectOnce(url, ctx, connectionAbort, onConnected) {
|
|
4255
|
+
return new Promise((resolve5, reject) => {
|
|
4256
|
+
const ws = new WebSocket(url);
|
|
4257
|
+
let stopHeartbeat;
|
|
4258
|
+
let settled = false;
|
|
4259
|
+
const finish = (fn) => {
|
|
4260
|
+
if (settled) return;
|
|
4261
|
+
settled = true;
|
|
4262
|
+
connectionAbort.removeEventListener("abort", onConnectionAbort);
|
|
4263
|
+
stopHeartbeat?.();
|
|
4264
|
+
fn();
|
|
4265
|
+
};
|
|
4266
|
+
const onConnectionAbort = () => {
|
|
4267
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
4268
|
+
ws.terminate();
|
|
4269
|
+
}
|
|
4270
|
+
};
|
|
4271
|
+
connectionAbort.addEventListener("abort", onConnectionAbort);
|
|
4272
|
+
attachWsHandlers(ws, ctx, () => {
|
|
4273
|
+
stopHeartbeat = startHeartbeat(ws, ctx.clientMachineId);
|
|
4274
|
+
onConnected();
|
|
4242
4275
|
});
|
|
4243
4276
|
ws.on("close", (code, reason) => {
|
|
4244
4277
|
console.log(
|
|
4245
4278
|
`[apm] \u8FDE\u63A5\u5DF2\u65AD\u5F00 code=${code}${reason ? ` reason=${reason.toString()}` : ""}`
|
|
4246
4279
|
);
|
|
4247
|
-
|
|
4280
|
+
if (ctx.isShuttingDown()) {
|
|
4281
|
+
finish(() => reject(new Error("shutdown")));
|
|
4282
|
+
return;
|
|
4283
|
+
}
|
|
4284
|
+
finish(resolve5);
|
|
4248
4285
|
});
|
|
4249
4286
|
ws.on("error", (err) => {
|
|
4250
4287
|
console.error("[apm] WebSocket \u9519\u8BEF:", err.message);
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
void shutdown();
|
|
4288
|
+
if (ctx.isShuttingDown()) {
|
|
4289
|
+
finish(() => reject(new Error("shutdown")));
|
|
4290
|
+
return;
|
|
4291
|
+
}
|
|
4292
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
4293
|
+
ws.terminate();
|
|
4294
|
+
}
|
|
4259
4295
|
});
|
|
4260
4296
|
});
|
|
4261
4297
|
}
|
|
4298
|
+
async function runConnect(options) {
|
|
4299
|
+
const { didUpdate } = await runUpdate();
|
|
4300
|
+
if (didUpdate) {
|
|
4301
|
+
await handleConnectAfterUpdate(options);
|
|
4302
|
+
}
|
|
4303
|
+
const cfg = await ensureLoggedConfig();
|
|
4304
|
+
if (options.server?.trim()) {
|
|
4305
|
+
cfg.baseUrl = options.server.trim().replace(/\/+$/, "");
|
|
4306
|
+
}
|
|
4307
|
+
const clientMachineId = resolveClientMachineId(cfg);
|
|
4308
|
+
if (!clientMachineId) {
|
|
4309
|
+
console.error("[apm] config \u7F3A\u5C11 clientMachineId\uFF0C\u8BF7\u91CD\u65B0 apm login");
|
|
4310
|
+
process.exit(1);
|
|
4311
|
+
}
|
|
4312
|
+
assertConnectNotRunning();
|
|
4313
|
+
const lockMode = isRunningUnderPm2() ? "pm2" : "foreground";
|
|
4314
|
+
acquireConnectLock(lockMode);
|
|
4315
|
+
process.on("exit", releaseConnectLock);
|
|
4316
|
+
let shuttingDown = false;
|
|
4317
|
+
let shutdownPromise = null;
|
|
4318
|
+
const lifecycleAbort = new AbortController();
|
|
4319
|
+
const shutdownAbort = new AbortController();
|
|
4320
|
+
const runSlots = createRunSlotPool();
|
|
4321
|
+
const activeTasks = /* @__PURE__ */ new Set();
|
|
4322
|
+
const activeRuns = /* @__PURE__ */ new Map();
|
|
4323
|
+
const pendingCancels = /* @__PURE__ */ new Set();
|
|
4324
|
+
let currentConnectionAbort = null;
|
|
4325
|
+
const sessionCtx = {
|
|
4326
|
+
cfg,
|
|
4327
|
+
clientMachineId,
|
|
4328
|
+
shutdownAbort,
|
|
4329
|
+
runSlots,
|
|
4330
|
+
activeTasks,
|
|
4331
|
+
activeRuns,
|
|
4332
|
+
pendingCancels,
|
|
4333
|
+
isShuttingDown: () => shuttingDown
|
|
4334
|
+
};
|
|
4335
|
+
const drainAndExit = async (code = 0) => {
|
|
4336
|
+
const drainMs = isRunningUnderPm2() ? 500 : SHUTDOWN_DRAIN_MS;
|
|
4337
|
+
lifecycleAbort.abort();
|
|
4338
|
+
currentConnectionAbort?.abort();
|
|
4339
|
+
logAbortSignalStats(shutdownAbort.signal, "connect:shutdown-before-abort");
|
|
4340
|
+
shutdownAbort.abort();
|
|
4341
|
+
logAbortSignalStats(shutdownAbort.signal, "connect:shutdown-after-abort");
|
|
4342
|
+
try {
|
|
4343
|
+
await Promise.race([Promise.all(activeTasks), delay2(drainMs)]);
|
|
4344
|
+
} catch {
|
|
4345
|
+
}
|
|
4346
|
+
releaseConnectLock();
|
|
4347
|
+
process.exit(code);
|
|
4348
|
+
};
|
|
4349
|
+
const shutdown = (code = 0) => {
|
|
4350
|
+
if (shuttingDown) return shutdownPromise ?? Promise.resolve();
|
|
4351
|
+
shuttingDown = true;
|
|
4352
|
+
shutdownPromise = drainAndExit(code);
|
|
4353
|
+
return shutdownPromise;
|
|
4354
|
+
};
|
|
4355
|
+
const onStopSignal = (code = 0) => {
|
|
4356
|
+
console.log("[apm] \u6B63\u5728\u5173\u95ED\u2026");
|
|
4357
|
+
void shutdown(code);
|
|
4358
|
+
};
|
|
4359
|
+
process.on("SIGINT", () => onStopSignal(0));
|
|
4360
|
+
process.on("SIGTERM", () => onStopSignal(0));
|
|
4361
|
+
if (isRunningUnderPm2()) {
|
|
4362
|
+
process.on("message", (msg) => {
|
|
4363
|
+
if (msg === "shutdown") onStopSignal(0);
|
|
4364
|
+
});
|
|
4365
|
+
}
|
|
4366
|
+
let reconnectDelay = RECONNECT_MIN_MS;
|
|
4367
|
+
while (!shuttingDown) {
|
|
4368
|
+
currentConnectionAbort = new AbortController();
|
|
4369
|
+
const connectionSignal = AbortSignal.any([
|
|
4370
|
+
lifecycleAbort.signal,
|
|
4371
|
+
currentConnectionAbort.signal
|
|
4372
|
+
]);
|
|
4373
|
+
const url = buildAgentWsUrl(cfg.baseUrl, resolveApiKey(cfg));
|
|
4374
|
+
console.log(`[apm] \u8FDE\u63A5 ${cfg.baseUrl} \u2026`);
|
|
4375
|
+
try {
|
|
4376
|
+
await connectOnce(url, sessionCtx, connectionSignal, () => {
|
|
4377
|
+
reconnectDelay = RECONNECT_MIN_MS;
|
|
4378
|
+
});
|
|
4379
|
+
if (shuttingDown) break;
|
|
4380
|
+
console.log(`[apm] ${reconnectDelay}ms \u540E\u5C1D\u8BD5\u91CD\u8FDE\u2026`);
|
|
4381
|
+
await interruptibleSleep(reconnectDelay, lifecycleAbort.signal);
|
|
4382
|
+
if (shuttingDown) break;
|
|
4383
|
+
reconnectDelay = Math.min(reconnectDelay * 2, RECONNECT_MAX_MS);
|
|
4384
|
+
} catch (err) {
|
|
4385
|
+
if (shuttingDown) break;
|
|
4386
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
4387
|
+
if (detail === "shutdown") break;
|
|
4388
|
+
console.error("[apm] \u8FDE\u63A5\u5931\u8D25:", detail);
|
|
4389
|
+
console.log(`[apm] ${reconnectDelay}ms \u540E\u5C1D\u8BD5\u91CD\u8FDE\u2026`);
|
|
4390
|
+
await interruptibleSleep(reconnectDelay, lifecycleAbort.signal);
|
|
4391
|
+
if (shuttingDown) break;
|
|
4392
|
+
reconnectDelay = Math.min(reconnectDelay * 2, RECONNECT_MAX_MS);
|
|
4393
|
+
} finally {
|
|
4394
|
+
currentConnectionAbort = null;
|
|
4395
|
+
}
|
|
4396
|
+
}
|
|
4397
|
+
if (!shuttingDown) {
|
|
4398
|
+
void shutdown(0);
|
|
4399
|
+
return;
|
|
4400
|
+
}
|
|
4401
|
+
if (shutdownPromise) {
|
|
4402
|
+
await shutdownPromise;
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4262
4405
|
|
|
4263
4406
|
// src/commands/create-pr-errors.ts
|
|
4264
4407
|
import { ApiError as ApiError2 } from "listpage-http";
|
|
@@ -4788,6 +4931,56 @@ async function zipDirectory(distDir, zipPath) {
|
|
|
4788
4931
|
console.error(`\u5DF2\u751F\u6210: ${zipPath} (${sizeMb} MB)`);
|
|
4789
4932
|
return content.length;
|
|
4790
4933
|
}
|
|
4934
|
+
var SFTP_UPLOAD_MAX_ATTEMPTS = 3;
|
|
4935
|
+
var SFTP_FAST_PUT_OPTIONS = {
|
|
4936
|
+
chunkSize: 64 * 1024,
|
|
4937
|
+
concurrency: 4
|
|
4938
|
+
};
|
|
4939
|
+
function buildSftpConnectOptions(settings) {
|
|
4940
|
+
return {
|
|
4941
|
+
host: settings.host,
|
|
4942
|
+
port: settings.port,
|
|
4943
|
+
username: settings.username,
|
|
4944
|
+
password: settings.password,
|
|
4945
|
+
readyTimeout: 3e4,
|
|
4946
|
+
tryKeyboard: true,
|
|
4947
|
+
keepaliveInterval: 1e4,
|
|
4948
|
+
keepaliveCountMax: 3
|
|
4949
|
+
};
|
|
4950
|
+
}
|
|
4951
|
+
async function sleep(ms) {
|
|
4952
|
+
await new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
4953
|
+
}
|
|
4954
|
+
async function uploadZipWithRetry(settings, localZip, remoteZipPath) {
|
|
4955
|
+
let lastError;
|
|
4956
|
+
for (let attempt = 1; attempt <= SFTP_UPLOAD_MAX_ATTEMPTS; attempt++) {
|
|
4957
|
+
const sftp = new SftpClient();
|
|
4958
|
+
try {
|
|
4959
|
+
if (attempt > 1) {
|
|
4960
|
+
console.error(
|
|
4961
|
+
`SFTP \u4E0A\u4F20\u91CD\u8BD5 (${attempt}/${SFTP_UPLOAD_MAX_ATTEMPTS})...`
|
|
4962
|
+
);
|
|
4963
|
+
}
|
|
4964
|
+
await sftp.connect(buildSftpConnectOptions(settings));
|
|
4965
|
+
await ensureRemoteDir(sftp, settings.remotePath);
|
|
4966
|
+
await sftp.fastPut(localZip, remoteZipPath, SFTP_FAST_PUT_OPTIONS);
|
|
4967
|
+
return sftp;
|
|
4968
|
+
} catch (err) {
|
|
4969
|
+
lastError = err;
|
|
4970
|
+
try {
|
|
4971
|
+
await sftp.end();
|
|
4972
|
+
} catch {
|
|
4973
|
+
}
|
|
4974
|
+
if (attempt < SFTP_UPLOAD_MAX_ATTEMPTS) {
|
|
4975
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4976
|
+
const delaySec = attempt * 2;
|
|
4977
|
+
console.error(`SFTP \u4E0A\u4F20\u5931\u8D25 (${message})\uFF0C${delaySec}s \u540E\u91CD\u8BD5...`);
|
|
4978
|
+
await sleep(delaySec * 1e3);
|
|
4979
|
+
}
|
|
4980
|
+
}
|
|
4981
|
+
}
|
|
4982
|
+
throw lastError;
|
|
4983
|
+
}
|
|
4791
4984
|
async function ensureRemoteDir(sftp, dir) {
|
|
4792
4985
|
const parts = dir.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
4793
4986
|
let current = dir.startsWith("/") ? "" : ".";
|
|
@@ -4847,18 +5040,8 @@ async function uploadAndMaybeExtract(settings, localZip, extract) {
|
|
|
4847
5040
|
console.error(
|
|
4848
5041
|
`\u8FDE\u63A5 ${settings.username}@${settings.host}:${settings.port} ...`
|
|
4849
5042
|
);
|
|
4850
|
-
const sftp =
|
|
5043
|
+
const sftp = await uploadZipWithRetry(settings, localZip, remoteZipPath);
|
|
4851
5044
|
try {
|
|
4852
|
-
await sftp.connect({
|
|
4853
|
-
host: settings.host,
|
|
4854
|
-
port: settings.port,
|
|
4855
|
-
username: settings.username,
|
|
4856
|
-
password: settings.password,
|
|
4857
|
-
readyTimeout: 2e4,
|
|
4858
|
-
tryKeyboard: true
|
|
4859
|
-
});
|
|
4860
|
-
await ensureRemoteDir(sftp, settings.remotePath);
|
|
4861
|
-
await sftp.put(localZip, remoteZipPath);
|
|
4862
5045
|
console.error(` \u2713 ${localZip} -> ${remoteZipPath}`);
|
|
4863
5046
|
if (extract) {
|
|
4864
5047
|
const target = settings.remotePath.replace(/\/$/, "");
|