@nmakarov/cli-toolkit 0.47.0 → 0.50.0
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/cli-runner.cjs +380 -16
- package/dist/cli-runner.cjs.map +1 -1
- package/dist/cli-runner.js +380 -16
- package/dist/cli-runner.js.map +1 -1
- package/dist/deploy.cjs +59 -15
- package/dist/deploy.cjs.map +1 -1
- package/dist/deploy.js +58 -15
- package/dist/deploy.js.map +1 -1
- package/dist/index.cjs +377 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +367 -32
- package/dist/index.js.map +1 -1
- package/dist/init.cjs +3 -0
- package/dist/init.cjs.map +1 -1
- package/dist/init.js +3 -0
- package/dist/init.js.map +1 -1
- package/dist/tasks.cjs +318 -17
- package/dist/tasks.cjs.map +1 -1
- package/dist/tasks.js +309 -17
- package/dist/tasks.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1081,6 +1081,8 @@ __export(src_exports, {
|
|
|
1081
1081
|
FooterPresets: () => FooterPresets,
|
|
1082
1082
|
GridCell: () => GridCell,
|
|
1083
1083
|
InputField: () => InputField,
|
|
1084
|
+
LOGGER_RUNTIME_KEYS: () => LOGGER_RUNTIME_KEYS,
|
|
1085
|
+
LOOP_RUNTIME_KEYS: () => LOOP_RUNTIME_KEYS,
|
|
1084
1086
|
ListComponent: () => ListComponent,
|
|
1085
1087
|
ListItem: () => ListItem,
|
|
1086
1088
|
MultiColumnListComponent: () => MultiColumnListComponent,
|
|
@@ -1099,6 +1101,7 @@ __export(src_exports, {
|
|
|
1099
1101
|
TaskGetLogs: () => TaskGetLogs,
|
|
1100
1102
|
TaskPing: () => TaskPing,
|
|
1101
1103
|
TaskSampleProcess: () => TaskSampleProcess,
|
|
1104
|
+
TaskSetRuntimeParam: () => TaskSetRuntimeParam,
|
|
1102
1105
|
TaskShellCommand: () => TaskShellCommand,
|
|
1103
1106
|
TaskStopRunner: () => TaskStopRunner,
|
|
1104
1107
|
TaskSumAB: () => TaskSumAB,
|
|
@@ -1110,12 +1113,16 @@ __export(src_exports, {
|
|
|
1110
1113
|
activateRelease: () => activateRelease,
|
|
1111
1114
|
appendDeployLog: () => appendDeployLog,
|
|
1112
1115
|
appendTaskIpcLog: () => appendTaskIpcLog,
|
|
1116
|
+
applyRuntimeParam: () => applyRuntimeParam,
|
|
1117
|
+
applyRuntimePatch: () => applyRuntimePatch,
|
|
1113
1118
|
bootstrapHost: () => bootstrapHost,
|
|
1114
1119
|
buildBreadcrumb: () => buildBreadcrumb,
|
|
1115
1120
|
buildDetailBreadcrumb: () => buildDetailBreadcrumb,
|
|
1116
1121
|
buildFooter: () => buildFooter,
|
|
1117
1122
|
bumpPatchVersion: () => bumpPatchVersion,
|
|
1118
1123
|
cloneRepo: () => cloneRepo,
|
|
1124
|
+
coerceRuntimeValue: () => coerceRuntimeValue,
|
|
1125
|
+
controlLaneTaskNames: () => controlLaneTaskNames,
|
|
1119
1126
|
convertPattern: () => convertPattern,
|
|
1120
1127
|
createRelease: () => createRelease,
|
|
1121
1128
|
defaultFileSynopsisFunction: () => defaultFileSynopsisFunction,
|
|
@@ -1137,11 +1144,13 @@ __export(src_exports, {
|
|
|
1137
1144
|
ensureSchemaEverywhere: () => ensureSchemaEverywhere,
|
|
1138
1145
|
ensureTable: () => ensureTable,
|
|
1139
1146
|
ensureTaskTables: () => ensureTaskTables,
|
|
1147
|
+
ensureTasksRuntime: () => ensureTasksRuntime,
|
|
1140
1148
|
flushTaskIpcLogs: () => flushTaskIpcLogs,
|
|
1141
1149
|
getArgsInstance: () => getArgsInstance,
|
|
1142
1150
|
h: () => import_react5.createElement,
|
|
1143
1151
|
initServiceStructure: () => initServiceStructure,
|
|
1144
1152
|
installDeps: () => installDeps,
|
|
1153
|
+
installOperatorShell: () => installOperatorShell,
|
|
1145
1154
|
ipcFileLogsTableNameForSourceResource: () => ipcFileLogsTableNameForSourceResource,
|
|
1146
1155
|
joiEdateType: () => joiEdateType,
|
|
1147
1156
|
joiStringArrayType: () => joiStringArrayType,
|
|
@@ -1167,6 +1176,7 @@ __export(src_exports, {
|
|
|
1167
1176
|
pullRepo: () => pullRepo,
|
|
1168
1177
|
queueToTableNames: () => queueToTableNames,
|
|
1169
1178
|
readCurrentRelease: () => readCurrentRelease,
|
|
1179
|
+
readLoopRuntime: () => readLoopRuntime,
|
|
1170
1180
|
readReleaseBuildInfo: () => readReleaseBuildInfo,
|
|
1171
1181
|
readTaskIpcLogsSnapshot: () => readTaskIpcLogsSnapshot,
|
|
1172
1182
|
registerInServicesRegistry: () => registerInServicesRegistry,
|
|
@@ -5940,10 +5950,9 @@ function buildEcosystemConfig(service, paths) {
|
|
|
5940
5950
|
const outLog = (0, import_node_path8.join)(paths.logs, `${pm2.appName}.out.log`);
|
|
5941
5951
|
const errLog = (0, import_node_path8.join)(paths.logs, `${pm2.appName}.err.log`);
|
|
5942
5952
|
return `/**
|
|
5943
|
-
* pm2 ecosystem for ${service.name} \u2014
|
|
5944
|
-
*
|
|
5945
|
-
*
|
|
5946
|
-
* Tweak \`args\` here, then: pm2 reload ${paths.ecosystem} --update-env
|
|
5953
|
+
* pm2 ecosystem for ${service.name} \u2014 written by cli-toolkit deploy from the
|
|
5954
|
+
* service manifest (init/deploy). Do not hand-edit; change pm2.* in the
|
|
5955
|
+
* manifest and redeploy. cwd \u2192 \`current\` symlink.
|
|
5947
5956
|
*/
|
|
5948
5957
|
module.exports = {
|
|
5949
5958
|
apps: [
|
|
@@ -5951,7 +5960,7 @@ module.exports = {
|
|
|
5951
5960
|
name: "${pm2.appName}",
|
|
5952
5961
|
script: "${pm2.script}",
|
|
5953
5962
|
cwd: "${paths.current}",
|
|
5954
|
-
args: "${pm2.args}",
|
|
5963
|
+
args: "${pm2.args ?? ""}",
|
|
5955
5964
|
instances: 1,
|
|
5956
5965
|
exec_mode: "fork",
|
|
5957
5966
|
autorestart: true,
|
|
@@ -5990,17 +5999,17 @@ async function initServiceStructure(service, options = {}) {
|
|
|
5990
5999
|
if (!dryRun) await (0, import_promises11.mkdir)(dir, { recursive: true });
|
|
5991
6000
|
created.push(dir);
|
|
5992
6001
|
}
|
|
5993
|
-
|
|
5994
|
-
if (
|
|
5995
|
-
|
|
6002
|
+
const ecosystemExisted = await pathExists4(paths.ecosystem);
|
|
6003
|
+
if (!dryRun) {
|
|
6004
|
+
await (0, import_promises11.writeFile)(paths.ecosystem, buildEcosystemConfig(service, paths), { mode: 420 });
|
|
6005
|
+
}
|
|
6006
|
+
if (ecosystemExisted) {
|
|
6007
|
+
logger.info(`synced ${paths.ecosystem} from manifest`);
|
|
5996
6008
|
} else {
|
|
5997
|
-
if (!dryRun) {
|
|
5998
|
-
await (0, import_promises11.writeFile)(paths.ecosystem, buildEcosystemConfig(service, paths), { mode: 420 });
|
|
5999
|
-
}
|
|
6000
|
-
ecosystemCreated = true;
|
|
6001
6009
|
created.push(paths.ecosystem);
|
|
6010
|
+
logger.info(`seeded ${paths.ecosystem}`);
|
|
6002
6011
|
}
|
|
6003
|
-
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] init-structure service=${service.name} dryRun=${dryRun} created=${created.length} skipped=${skipped.length}
|
|
6012
|
+
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] init-structure service=${service.name} dryRun=${dryRun} created=${created.length} skipped=${skipped.length} ecosystem=synced
|
|
6004
6013
|
`;
|
|
6005
6014
|
if (!dryRun) {
|
|
6006
6015
|
await (0, import_promises11.mkdir)(paths.logs, { recursive: true });
|
|
@@ -6009,8 +6018,7 @@ async function initServiceStructure(service, options = {}) {
|
|
|
6009
6018
|
logger.info(`service=${service.name} appsRoot=${paths.root}`);
|
|
6010
6019
|
logger.info(`created: ${created.length ? created.join(", ") : "(none)"}`);
|
|
6011
6020
|
logger.info(`already present: ${skipped.length ? skipped.join(", ") : "(none)"}`);
|
|
6012
|
-
|
|
6013
|
-
return { paths, created, skipped, ecosystemCreated };
|
|
6021
|
+
return { paths, created, skipped, ecosystemCreated: !ecosystemExisted, ecosystemSynced: true };
|
|
6014
6022
|
}
|
|
6015
6023
|
|
|
6016
6024
|
// src/deploy/bootstrap-host.js
|
|
@@ -6107,11 +6115,55 @@ async function installLogrotate(service, options = {}) {
|
|
|
6107
6115
|
await runShell(`sudo cp '${tmp}' '${conf}'`, { logger });
|
|
6108
6116
|
logger.info(`logrotate config written: ${conf}`);
|
|
6109
6117
|
}
|
|
6118
|
+
var OPERATOR_SHELL_BEGIN = "# >>> mlsfarm-operator >>>";
|
|
6119
|
+
var OPERATOR_SHELL_END = "# <<< mlsfarm-operator <<<";
|
|
6120
|
+
async function installOperatorShell(service, options = {}) {
|
|
6121
|
+
const { user = "ubuntu", dryRun = false, logger = console } = options;
|
|
6122
|
+
const appsRoot = service.appsRoot;
|
|
6123
|
+
if (!appsRoot) {
|
|
6124
|
+
logger.warn("installOperatorShell: service.appsRoot missing \u2014 skipped");
|
|
6125
|
+
return;
|
|
6126
|
+
}
|
|
6127
|
+
const home = user === "ubuntu" || user === process.env.USER ? (0, import_node_path9.join)((0, import_node_os2.homedir)(), ".bashrc") : `/home/${user}/.bashrc`;
|
|
6128
|
+
const currentDir = (0, import_node_path9.join)(appsRoot, "current");
|
|
6129
|
+
const block = [
|
|
6130
|
+
OPERATOR_SHELL_BEGIN,
|
|
6131
|
+
"# Managed by cli-toolkit deploy bootstrap \u2014 do not edit by hand.",
|
|
6132
|
+
"export ENV=production",
|
|
6133
|
+
`if [ -d "${currentDir}" ]; then`,
|
|
6134
|
+
` cd "${currentDir}" || true`,
|
|
6135
|
+
"fi",
|
|
6136
|
+
OPERATOR_SHELL_END,
|
|
6137
|
+
""
|
|
6138
|
+
].join("\n");
|
|
6139
|
+
if (dryRun) {
|
|
6140
|
+
logger.info(`[dryRun] would update ${home} (ENV=production, cd ${currentDir})`);
|
|
6141
|
+
return;
|
|
6142
|
+
}
|
|
6143
|
+
let existing = "";
|
|
6144
|
+
if (await pathExists5(home)) {
|
|
6145
|
+
existing = await (0, import_promises12.readFile)(home, "utf8");
|
|
6146
|
+
}
|
|
6147
|
+
const begin = existing.indexOf(OPERATOR_SHELL_BEGIN);
|
|
6148
|
+
const end = existing.indexOf(OPERATOR_SHELL_END);
|
|
6149
|
+
let next;
|
|
6150
|
+
if (begin !== -1 && end !== -1 && end > begin) {
|
|
6151
|
+
const afterEnd = end + OPERATOR_SHELL_END.length;
|
|
6152
|
+
const rest = existing.slice(afterEnd).replace(/^\n/, "");
|
|
6153
|
+
next = `${existing.slice(0, begin).replace(/\s*$/, "\n")}${block}${rest}`;
|
|
6154
|
+
} else {
|
|
6155
|
+
next = `${existing.replace(/\s*$/, "\n")}
|
|
6156
|
+
${block}`;
|
|
6157
|
+
}
|
|
6158
|
+
await (0, import_promises12.writeFile)(home, next, { mode: 420 });
|
|
6159
|
+
logger.info(`operator shell: ${home} \u2192 ENV=production, cd ${currentDir}`);
|
|
6160
|
+
}
|
|
6110
6161
|
async function bootstrapHost(service, options = {}) {
|
|
6111
6162
|
const { deployKey = service.deployKey, user = "ubuntu", dryRun = false, logger = console } = options;
|
|
6112
6163
|
await ensurePm2Startup({ user, dryRun, logger });
|
|
6113
6164
|
if (deployKey) await installDeployKey(deployKey, { dryRun, logger });
|
|
6114
6165
|
await installLogrotate(service, { dryRun, logger });
|
|
6166
|
+
await installOperatorShell(service, { user, dryRun, logger });
|
|
6115
6167
|
logger.info("bootstrap-host complete");
|
|
6116
6168
|
}
|
|
6117
6169
|
|
|
@@ -7901,6 +7953,273 @@ var TaskGetLogs = class extends AbstractTask {
|
|
|
7901
7953
|
}
|
|
7902
7954
|
};
|
|
7903
7955
|
|
|
7956
|
+
// src/tasks/runtimeParams.js
|
|
7957
|
+
var LOOP_RUNTIME_KEYS = ["maxParallel", "pollMs", "claimJitterMs", "scanLimit"];
|
|
7958
|
+
var LOGGER_RUNTIME_KEYS = [
|
|
7959
|
+
"levels",
|
|
7960
|
+
"silent",
|
|
7961
|
+
"showLevel",
|
|
7962
|
+
"timestamp",
|
|
7963
|
+
"mode",
|
|
7964
|
+
"route",
|
|
7965
|
+
"prefix",
|
|
7966
|
+
"progressWithTimes",
|
|
7967
|
+
"progressThrottleMs"
|
|
7968
|
+
];
|
|
7969
|
+
var CONTROL_LANE_TASK_NAMES = ["stopRunner", "stop", "setRuntimeParam", "setRunnerParam"];
|
|
7970
|
+
function controlLaneTaskNames() {
|
|
7971
|
+
return [...CONTROL_LANE_TASK_NAMES];
|
|
7972
|
+
}
|
|
7973
|
+
function asPositiveInt(value, key, { min = 1 } = {}) {
|
|
7974
|
+
const n = Number(value);
|
|
7975
|
+
if (!Number.isFinite(n) || n < min) {
|
|
7976
|
+
throw new ParamError(
|
|
7977
|
+
`setRuntimeParam: ${key} must be a number >= ${min} (got ${JSON.stringify(value)})`
|
|
7978
|
+
);
|
|
7979
|
+
}
|
|
7980
|
+
return Math.floor(n);
|
|
7981
|
+
}
|
|
7982
|
+
function coerceRuntimeValue(key, value) {
|
|
7983
|
+
switch (key) {
|
|
7984
|
+
case "maxParallel":
|
|
7985
|
+
return asPositiveInt(value, key, { min: 1 });
|
|
7986
|
+
case "pollMs":
|
|
7987
|
+
return asPositiveInt(value, key, { min: 50 });
|
|
7988
|
+
case "claimJitterMs":
|
|
7989
|
+
return asPositiveInt(value, key, { min: 0 });
|
|
7990
|
+
case "scanLimit":
|
|
7991
|
+
return asPositiveInt(value, key, { min: 1 });
|
|
7992
|
+
case "silent":
|
|
7993
|
+
case "showLevel":
|
|
7994
|
+
case "timestamp":
|
|
7995
|
+
case "progressWithTimes":
|
|
7996
|
+
if (typeof value === "boolean") return value;
|
|
7997
|
+
if (value === "true" || value === "1") return true;
|
|
7998
|
+
if (value === "false" || value === "0") return false;
|
|
7999
|
+
throw new ParamError(
|
|
8000
|
+
`setRuntimeParam: ${key} must be boolean (got ${JSON.stringify(value)})`
|
|
8001
|
+
);
|
|
8002
|
+
case "progressThrottleMs":
|
|
8003
|
+
return asPositiveInt(value, key, { min: 0 });
|
|
8004
|
+
case "levels":
|
|
8005
|
+
case "mode":
|
|
8006
|
+
case "route":
|
|
8007
|
+
case "prefix":
|
|
8008
|
+
return value;
|
|
8009
|
+
default:
|
|
8010
|
+
return value;
|
|
8011
|
+
}
|
|
8012
|
+
}
|
|
8013
|
+
function ensureTasksRuntime(context, seed = {}) {
|
|
8014
|
+
if (!context.tasksRuntime || typeof context.tasksRuntime !== "object") {
|
|
8015
|
+
context.tasksRuntime = {};
|
|
8016
|
+
}
|
|
8017
|
+
const rt = context.tasksRuntime;
|
|
8018
|
+
if (rt.maxParallel === void 0) rt.maxParallel = seed.maxParallel ?? 32;
|
|
8019
|
+
if (rt.pollMs === void 0) rt.pollMs = seed.pollMs ?? 1e3;
|
|
8020
|
+
if (rt.claimJitterMs === void 0) rt.claimJitterMs = seed.claimJitterMs ?? 0;
|
|
8021
|
+
if (rt.scanLimit === void 0) rt.scanLimit = seed.scanLimit ?? 100;
|
|
8022
|
+
return rt;
|
|
8023
|
+
}
|
|
8024
|
+
async function applyRuntimeParam(context, key, value) {
|
|
8025
|
+
const k = String(key ?? "").trim();
|
|
8026
|
+
if (!k) throw new ParamError("setRuntimeParam: key is required");
|
|
8027
|
+
const runtime = ensureTasksRuntime(context);
|
|
8028
|
+
const next = coerceRuntimeValue(k, value);
|
|
8029
|
+
const previous = runtime[k];
|
|
8030
|
+
const applied = [];
|
|
8031
|
+
runtime[k] = next;
|
|
8032
|
+
applied.push("tasksRuntime");
|
|
8033
|
+
if (LOGGER_RUNTIME_KEYS.includes(k) && context.logger?.configure) {
|
|
8034
|
+
context.logger.configure({ [k]: next });
|
|
8035
|
+
applied.push("logger");
|
|
8036
|
+
}
|
|
8037
|
+
const hook = context.tasksRuntimeOnParam;
|
|
8038
|
+
if (typeof hook === "function") {
|
|
8039
|
+
await hook(k, next, runtime, context);
|
|
8040
|
+
applied.push("onRuntimeParam");
|
|
8041
|
+
}
|
|
8042
|
+
const reg = context.servicesRegistry;
|
|
8043
|
+
if (reg?.rowId && reg?.registryTable) {
|
|
8044
|
+
try {
|
|
8045
|
+
const loopSnapshot = {};
|
|
8046
|
+
for (const lk of LOOP_RUNTIME_KEYS) {
|
|
8047
|
+
if (runtime[lk] !== void 0) loopSnapshot[lk] = runtime[lk];
|
|
8048
|
+
}
|
|
8049
|
+
await updateServicesRegistryMetadata(context, reg, {
|
|
8050
|
+
runtime: loopSnapshot,
|
|
8051
|
+
runtimeUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8052
|
+
});
|
|
8053
|
+
applied.push("servicesRegistry");
|
|
8054
|
+
} catch (err) {
|
|
8055
|
+
context.logger?.warn?.(
|
|
8056
|
+
`[setRuntimeParam] registry metadata update failed: ${err?.message ?? String(err)}`
|
|
8057
|
+
);
|
|
8058
|
+
}
|
|
8059
|
+
}
|
|
8060
|
+
return { key: k, previous, next, applied };
|
|
8061
|
+
}
|
|
8062
|
+
async function applyRuntimePatch(context, patch) {
|
|
8063
|
+
if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
|
|
8064
|
+
throw new ParamError("setRuntimeParam: patch must be a plain object");
|
|
8065
|
+
}
|
|
8066
|
+
const entries = Object.entries(patch);
|
|
8067
|
+
if (entries.length === 0) {
|
|
8068
|
+
throw new ParamError("setRuntimeParam: patch is empty");
|
|
8069
|
+
}
|
|
8070
|
+
const out = [];
|
|
8071
|
+
for (const [key, value] of entries) {
|
|
8072
|
+
out.push(await applyRuntimeParam(context, key, value));
|
|
8073
|
+
}
|
|
8074
|
+
return out;
|
|
8075
|
+
}
|
|
8076
|
+
function readLoopRuntime(context) {
|
|
8077
|
+
const rt = ensureTasksRuntime(context);
|
|
8078
|
+
return {
|
|
8079
|
+
maxParallel: Math.max(1, Number(rt.maxParallel) || 1),
|
|
8080
|
+
pollMs: Math.max(50, Number(rt.pollMs) || 1e3),
|
|
8081
|
+
claimJitterMs: Math.max(0, Number(rt.claimJitterMs) || 0),
|
|
8082
|
+
scanLimit: Math.max(1, Number(rt.scanLimit) || 100)
|
|
8083
|
+
};
|
|
8084
|
+
}
|
|
8085
|
+
|
|
8086
|
+
// src/tasks/coreTasks/TaskSetRuntimeParam.js
|
|
8087
|
+
var TaskSetRuntimeParam = class extends AbstractTask {
|
|
8088
|
+
static defaultWaitForResult = true;
|
|
8089
|
+
/**
|
|
8090
|
+
* @param {object} context
|
|
8091
|
+
* @param {Record<string, unknown>} [overrides]
|
|
8092
|
+
* @returns {Promise<object>}
|
|
8093
|
+
*/
|
|
8094
|
+
static async resolveParams(context, overrides = {}) {
|
|
8095
|
+
const main = await super.resolveParams(context, overrides);
|
|
8096
|
+
if (!main.serviceName && !main.serviceGroup) {
|
|
8097
|
+
throw new ParamError(
|
|
8098
|
+
"setRuntimeParam requires --serviceName (one instance) or --serviceGroup (broadcast to alive instances)"
|
|
8099
|
+
);
|
|
8100
|
+
}
|
|
8101
|
+
return main;
|
|
8102
|
+
}
|
|
8103
|
+
/**
|
|
8104
|
+
* @param {object} context
|
|
8105
|
+
* @param {Record<string, unknown>} [overrides]
|
|
8106
|
+
* @returns {Promise<{ key?: string, value?: unknown, patch?: Record<string, unknown> }>}
|
|
8107
|
+
*/
|
|
8108
|
+
static async resolveCustomParams(context, overrides = {}) {
|
|
8109
|
+
const merged = AbstractTask._mergeTypedParams(
|
|
8110
|
+
context,
|
|
8111
|
+
"task-set-runtime-param",
|
|
8112
|
+
{
|
|
8113
|
+
paramKey: "string",
|
|
8114
|
+
paramValue: "string",
|
|
8115
|
+
key: "string",
|
|
8116
|
+
value: "string"
|
|
8117
|
+
},
|
|
8118
|
+
overrides
|
|
8119
|
+
);
|
|
8120
|
+
if (merged.patch && typeof merged.patch === "object" && !Array.isArray(merged.patch)) {
|
|
8121
|
+
if (Object.keys(merged.patch).length === 0) {
|
|
8122
|
+
throw new ParamError("setRuntimeParam: patch is empty");
|
|
8123
|
+
}
|
|
8124
|
+
return { patch: { ...merged.patch } };
|
|
8125
|
+
}
|
|
8126
|
+
const key = merged.paramKey || merged.key;
|
|
8127
|
+
const value = merged.paramValue !== void 0 ? merged.paramValue : merged.value;
|
|
8128
|
+
if (!key) {
|
|
8129
|
+
throw new ParamError(
|
|
8130
|
+
`setRuntimeParam requires --paramKey/--paramValue, or --paramsJson '{"key":"maxParallel","value":16}' / '{"patch":{...}}'`
|
|
8131
|
+
);
|
|
8132
|
+
}
|
|
8133
|
+
let parsed = value;
|
|
8134
|
+
if (typeof value === "string") {
|
|
8135
|
+
const t = value.trim();
|
|
8136
|
+
if (t === "true") parsed = true;
|
|
8137
|
+
else if (t === "false") parsed = false;
|
|
8138
|
+
else if (t !== "" && !Number.isNaN(Number(t)) && /^-?\d+(\.\d+)?$/.test(t)) {
|
|
8139
|
+
parsed = Number(t);
|
|
8140
|
+
} else if (t.startsWith("{") && t.endsWith("}") || t.startsWith("[") && t.endsWith("]") || t.startsWith('"') && t.endsWith('"')) {
|
|
8141
|
+
try {
|
|
8142
|
+
parsed = JSON.parse(t);
|
|
8143
|
+
} catch {
|
|
8144
|
+
parsed = value;
|
|
8145
|
+
}
|
|
8146
|
+
}
|
|
8147
|
+
}
|
|
8148
|
+
return { key: String(key), value: parsed };
|
|
8149
|
+
}
|
|
8150
|
+
/**
|
|
8151
|
+
* Enqueue one or many setRuntimeParam tasks. Prefer this over a bare
|
|
8152
|
+
* `enqueueTask` when broadcasting to a service group.
|
|
8153
|
+
*
|
|
8154
|
+
* @param {object} context
|
|
8155
|
+
* @param {Record<string, unknown>} [overrides]
|
|
8156
|
+
* @returns {Promise<{ ids: string[], targets: string[] }>}
|
|
8157
|
+
*/
|
|
8158
|
+
static async enqueue(context, overrides = {}) {
|
|
8159
|
+
const payload = await this.resolveParams(context, { ...overrides, name: "setRuntimeParam" });
|
|
8160
|
+
const queueName = payload.queueName ?? "tasks";
|
|
8161
|
+
if (payload.serviceName) {
|
|
8162
|
+
const id = await enqueueTask(context, payload);
|
|
8163
|
+
return { ids: [id], targets: [payload.serviceName] };
|
|
8164
|
+
}
|
|
8165
|
+
const group = String(payload.serviceGroup || "").trim();
|
|
8166
|
+
if (!group) {
|
|
8167
|
+
throw new ParamError("setRuntimeParam.enqueue: serviceGroup required for broadcast");
|
|
8168
|
+
}
|
|
8169
|
+
const alive = await listServicesRegistry(context, {
|
|
8170
|
+
queueName,
|
|
8171
|
+
serviceGroup: group,
|
|
8172
|
+
staleMs: overrides.staleMs ?? 45e3
|
|
8173
|
+
});
|
|
8174
|
+
if (!alive.length) {
|
|
8175
|
+
throw new ParamError(
|
|
8176
|
+
`setRuntimeParam: no alive services in group="${group}" queue="${queueName}"`
|
|
8177
|
+
);
|
|
8178
|
+
}
|
|
8179
|
+
const ids = [];
|
|
8180
|
+
const targets = [];
|
|
8181
|
+
for (const reg of alive) {
|
|
8182
|
+
const id = await enqueueTask(context, {
|
|
8183
|
+
...payload,
|
|
8184
|
+
serviceGroup: group,
|
|
8185
|
+
serviceName: reg.service_name,
|
|
8186
|
+
serverName: reg.server_name ?? null,
|
|
8187
|
+
instanceNumber: reg.instance_number ?? null
|
|
8188
|
+
});
|
|
8189
|
+
ids.push(id);
|
|
8190
|
+
targets.push(reg.service_name);
|
|
8191
|
+
}
|
|
8192
|
+
context.logger?.info?.(
|
|
8193
|
+
`[setRuntimeParam] broadcast to ${targets.length} instance(s) in group=${group}: ${targets.join(", ")}`
|
|
8194
|
+
);
|
|
8195
|
+
return { ids, targets };
|
|
8196
|
+
}
|
|
8197
|
+
/**
|
|
8198
|
+
* @returns {Promise<{ success: true, results: object }>}
|
|
8199
|
+
*/
|
|
8200
|
+
async run() {
|
|
8201
|
+
const params = this.task?.params ?? {};
|
|
8202
|
+
let changes;
|
|
8203
|
+
if (params.patch && typeof params.patch === "object") {
|
|
8204
|
+
changes = await applyRuntimePatch(this.context, params.patch);
|
|
8205
|
+
} else {
|
|
8206
|
+
changes = [await applyRuntimeParam(this.context, params.key, params.value)];
|
|
8207
|
+
}
|
|
8208
|
+
const summary = changes.map((c) => `${c.key}: ${JSON.stringify(c.previous)} \u2192 ${JSON.stringify(c.next)}`);
|
|
8209
|
+
this.context.logger.warn?.(
|
|
8210
|
+
`[TaskSetRuntimeParam] applied on ${this.context.servicesRegistry?.serviceName ?? "runner"}: ${summary.join("; ")}`
|
|
8211
|
+
);
|
|
8212
|
+
return {
|
|
8213
|
+
success: true,
|
|
8214
|
+
results: {
|
|
8215
|
+
runtimeParamApplied: true,
|
|
8216
|
+
changes,
|
|
8217
|
+
runtime: { ...this.context.tasksRuntime ?? {} }
|
|
8218
|
+
}
|
|
8219
|
+
};
|
|
8220
|
+
}
|
|
8221
|
+
};
|
|
8222
|
+
|
|
7904
8223
|
// src/tasks/TasksRegistry.js
|
|
7905
8224
|
var TasksRegistry = class _TasksRegistry {
|
|
7906
8225
|
/**
|
|
@@ -7919,7 +8238,7 @@ var TasksRegistry = class _TasksRegistry {
|
|
|
7919
8238
|
* @returns {TasksRegistry}
|
|
7920
8239
|
*/
|
|
7921
8240
|
static withCoreTasks() {
|
|
7922
|
-
return new _TasksRegistry().add("ping", TaskPing).add("sampleProcess", TaskSampleProcess).add("shellCommand", TaskShellCommand).add("systemInfo", TaskSystemInfo).add("info", TaskSystemInfo).add("taskSumAB", TaskSumAB).add("stopRunner", TaskStopRunner).add("stop", TaskStopRunner).add("getLogs", TaskGetLogs);
|
|
8241
|
+
return new _TasksRegistry().add("ping", TaskPing).add("sampleProcess", TaskSampleProcess).add("shellCommand", TaskShellCommand).add("systemInfo", TaskSystemInfo).add("info", TaskSystemInfo).add("taskSumAB", TaskSumAB).add("stopRunner", TaskStopRunner).add("stop", TaskStopRunner).add("getLogs", TaskGetLogs).add("setRuntimeParam", TaskSetRuntimeParam).add("setRunnerParam", TaskSetRuntimeParam);
|
|
7923
8242
|
}
|
|
7924
8243
|
/**
|
|
7925
8244
|
* Register a single task class under a name. Overwrites any previous entry.
|
|
@@ -8025,7 +8344,9 @@ var SERVICE_TASK_NAMES = [
|
|
|
8025
8344
|
"shellCommand",
|
|
8026
8345
|
"systemInfo",
|
|
8027
8346
|
"info",
|
|
8028
|
-
"getLogs"
|
|
8347
|
+
"getLogs",
|
|
8348
|
+
"setRuntimeParam",
|
|
8349
|
+
"setRunnerParam"
|
|
8029
8350
|
];
|
|
8030
8351
|
function normalizeAllowedTasks(value) {
|
|
8031
8352
|
if (!value) return void 0;
|
|
@@ -8452,18 +8773,23 @@ async function claimNextRunnableTask(context, tasksTable, serviceGroup, registry
|
|
|
8452
8773
|
async function runTasksLoop(context, options) {
|
|
8453
8774
|
const queueName = options.queueName ?? "tasks";
|
|
8454
8775
|
const target = options.target;
|
|
8455
|
-
const pollMs = options.pollMs ?? 1e3;
|
|
8456
|
-
const claimJitterMs = options.claimJitterMs ?? 0;
|
|
8457
|
-
const maxParallel = options.maxParallel ?? 32;
|
|
8458
|
-
const scanLimit = options.scanLimit ?? 100;
|
|
8459
8776
|
const allowedTasks = normalizeAllowedTasks(options.allowedTasks);
|
|
8460
8777
|
const registry = normalizeRegistry(options.registry);
|
|
8461
8778
|
const { tasksTable, historyTable } = queueToTableNames(queueName);
|
|
8462
8779
|
if (!target) throw new Error("runTasksLoop: target is required");
|
|
8463
8780
|
context.tasksQueueName = queueName;
|
|
8781
|
+
ensureTasksRuntime(context, {
|
|
8782
|
+
maxParallel: options.maxParallel ?? 32,
|
|
8783
|
+
pollMs: options.pollMs ?? 1e3,
|
|
8784
|
+
claimJitterMs: options.claimJitterMs ?? 0,
|
|
8785
|
+
scanLimit: options.scanLimit ?? 100
|
|
8786
|
+
});
|
|
8787
|
+
if (typeof options.onRuntimeParam === "function") {
|
|
8788
|
+
context.tasksRuntimeOnParam = options.onRuntimeParam;
|
|
8789
|
+
}
|
|
8464
8790
|
const runningPromises = /* @__PURE__ */ new Set();
|
|
8465
8791
|
const runningTaskInstances = /* @__PURE__ */ new Map();
|
|
8466
|
-
let
|
|
8792
|
+
let runningControlPromise = null;
|
|
8467
8793
|
let stopRequested = false;
|
|
8468
8794
|
let stopAllowanceMs = 5e3;
|
|
8469
8795
|
context.tasksRunnerStop = false;
|
|
@@ -8474,9 +8800,16 @@ async function runTasksLoop(context, options) {
|
|
|
8474
8800
|
if (hbGroup) {
|
|
8475
8801
|
const hbIntervalMs = options.runnerHeartbeatIntervalMs ?? 1e4;
|
|
8476
8802
|
const staleMs = options.runnerHeartbeatStaleMs ?? 45e3;
|
|
8803
|
+
const loop0 = readLoopRuntime(context);
|
|
8477
8804
|
const defaultMeta = {
|
|
8478
8805
|
component: "tasks-runner",
|
|
8479
|
-
allowedTasks: allowedTasks?.length ? allowedTasks.join(",") : "all"
|
|
8806
|
+
allowedTasks: allowedTasks?.length ? allowedTasks.join(",") : "all",
|
|
8807
|
+
runtime: {
|
|
8808
|
+
maxParallel: loop0.maxParallel,
|
|
8809
|
+
pollMs: loop0.pollMs,
|
|
8810
|
+
claimJitterMs: loop0.claimJitterMs,
|
|
8811
|
+
scanLimit: loop0.scanLimit
|
|
8812
|
+
}
|
|
8480
8813
|
};
|
|
8481
8814
|
registryReg = await registerInServicesRegistry(context, {
|
|
8482
8815
|
queueName,
|
|
@@ -8489,6 +8822,7 @@ async function runTasksLoop(context, options) {
|
|
|
8489
8822
|
enforceMaxInstances: options.runnerEnforceMaxInstances ?? true,
|
|
8490
8823
|
metadata: options.runnerMetadata ?? defaultMeta
|
|
8491
8824
|
});
|
|
8825
|
+
context.servicesRegistry = registryReg;
|
|
8492
8826
|
runnerIdentity = {
|
|
8493
8827
|
service_name: registryReg.serviceName,
|
|
8494
8828
|
server_name: import_node_os6.default.hostname(),
|
|
@@ -8502,22 +8836,23 @@ async function runTasksLoop(context, options) {
|
|
|
8502
8836
|
}
|
|
8503
8837
|
try {
|
|
8504
8838
|
while (!context.isStop() && !stopRequested && context.tasksRunnerStop !== true) {
|
|
8505
|
-
|
|
8506
|
-
|
|
8839
|
+
const { maxParallel, pollMs, claimJitterMs, scanLimit } = readLoopRuntime(context);
|
|
8840
|
+
if (!runningControlPromise) {
|
|
8841
|
+
const claimedControlTask = await claimNextRunnableTask(
|
|
8507
8842
|
context,
|
|
8508
8843
|
tasksTable,
|
|
8509
8844
|
target,
|
|
8510
8845
|
registry,
|
|
8511
8846
|
10,
|
|
8512
|
-
|
|
8847
|
+
controlLaneTaskNames(),
|
|
8513
8848
|
runnerIdentity
|
|
8514
8849
|
);
|
|
8515
|
-
if (
|
|
8516
|
-
|
|
8850
|
+
if (claimedControlTask) {
|
|
8851
|
+
runningControlPromise = executeClaimedTask(
|
|
8517
8852
|
context,
|
|
8518
8853
|
tasksTable,
|
|
8519
8854
|
historyTable,
|
|
8520
|
-
|
|
8855
|
+
claimedControlTask,
|
|
8521
8856
|
registry,
|
|
8522
8857
|
runningTaskInstances
|
|
8523
8858
|
).then(async (outcome) => {
|
|
@@ -8528,7 +8863,7 @@ async function runTasksLoop(context, options) {
|
|
|
8528
8863
|
await signalRunningTasksStop(context, runningTaskInstances, stopAllowanceMs);
|
|
8529
8864
|
}
|
|
8530
8865
|
}).finally(() => {
|
|
8531
|
-
|
|
8866
|
+
runningControlPromise = null;
|
|
8532
8867
|
});
|
|
8533
8868
|
}
|
|
8534
8869
|
}
|
|
@@ -8559,8 +8894,8 @@ async function runTasksLoop(context, options) {
|
|
|
8559
8894
|
runningPromises.add(p);
|
|
8560
8895
|
}
|
|
8561
8896
|
const wakePromises = [...runningPromises];
|
|
8562
|
-
if (
|
|
8563
|
-
wakePromises.push(
|
|
8897
|
+
if (runningControlPromise) {
|
|
8898
|
+
wakePromises.push(runningControlPromise);
|
|
8564
8899
|
}
|
|
8565
8900
|
if (wakePromises.length === 0) {
|
|
8566
8901
|
await sleepMs(pollMs);
|
|
@@ -8789,6 +9124,8 @@ var TasksManager = class _TasksManager {
|
|
|
8789
9124
|
FooterPresets,
|
|
8790
9125
|
GridCell,
|
|
8791
9126
|
InputField,
|
|
9127
|
+
LOGGER_RUNTIME_KEYS,
|
|
9128
|
+
LOOP_RUNTIME_KEYS,
|
|
8792
9129
|
ListComponent,
|
|
8793
9130
|
ListItem,
|
|
8794
9131
|
MultiColumnListComponent,
|
|
@@ -8807,6 +9144,7 @@ var TasksManager = class _TasksManager {
|
|
|
8807
9144
|
TaskGetLogs,
|
|
8808
9145
|
TaskPing,
|
|
8809
9146
|
TaskSampleProcess,
|
|
9147
|
+
TaskSetRuntimeParam,
|
|
8810
9148
|
TaskShellCommand,
|
|
8811
9149
|
TaskStopRunner,
|
|
8812
9150
|
TaskSumAB,
|
|
@@ -8818,12 +9156,16 @@ var TasksManager = class _TasksManager {
|
|
|
8818
9156
|
activateRelease,
|
|
8819
9157
|
appendDeployLog,
|
|
8820
9158
|
appendTaskIpcLog,
|
|
9159
|
+
applyRuntimeParam,
|
|
9160
|
+
applyRuntimePatch,
|
|
8821
9161
|
bootstrapHost,
|
|
8822
9162
|
buildBreadcrumb,
|
|
8823
9163
|
buildDetailBreadcrumb,
|
|
8824
9164
|
buildFooter,
|
|
8825
9165
|
bumpPatchVersion,
|
|
8826
9166
|
cloneRepo,
|
|
9167
|
+
coerceRuntimeValue,
|
|
9168
|
+
controlLaneTaskNames,
|
|
8827
9169
|
convertPattern,
|
|
8828
9170
|
createRelease,
|
|
8829
9171
|
defaultFileSynopsisFunction,
|
|
@@ -8845,11 +9187,13 @@ var TasksManager = class _TasksManager {
|
|
|
8845
9187
|
ensureSchemaEverywhere,
|
|
8846
9188
|
ensureTable,
|
|
8847
9189
|
ensureTaskTables,
|
|
9190
|
+
ensureTasksRuntime,
|
|
8848
9191
|
flushTaskIpcLogs,
|
|
8849
9192
|
getArgsInstance,
|
|
8850
9193
|
h,
|
|
8851
9194
|
initServiceStructure,
|
|
8852
9195
|
installDeps,
|
|
9196
|
+
installOperatorShell,
|
|
8853
9197
|
ipcFileLogsTableNameForSourceResource,
|
|
8854
9198
|
joiEdateType,
|
|
8855
9199
|
joiStringArrayType,
|
|
@@ -8875,6 +9219,7 @@ var TasksManager = class _TasksManager {
|
|
|
8875
9219
|
pullRepo,
|
|
8876
9220
|
queueToTableNames,
|
|
8877
9221
|
readCurrentRelease,
|
|
9222
|
+
readLoopRuntime,
|
|
8878
9223
|
readReleaseBuildInfo,
|
|
8879
9224
|
readTaskIpcLogsSnapshot,
|
|
8880
9225
|
registerInServicesRegistry,
|