@underpostnet/underpost 2.98.3 → 2.99.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/src/cli/run.js CHANGED
@@ -4,30 +4,138 @@
4
4
  * @namespace UnderpostRun
5
5
  */
6
6
 
7
- import { daemonProcess, getTerminalPid, openTerminal, pbcopy, shellCd, shellExec } from '../server/process.js';
7
+ import { daemonProcess, getTerminalPid, openTerminal, shellCd, shellExec } from '../server/process.js';
8
8
  import {
9
9
  awaitDeployMonitor,
10
10
  buildKindPorts,
11
11
  Config,
12
12
  getNpmRootPath,
13
- getUnderpostRootPath,
14
13
  isDeployRunnerContext,
15
14
  writeEnv,
16
15
  } from '../server/conf.js';
17
16
  import { actionInitLog, loggerFactory } from '../server/logger.js';
18
- import UnderpostTest from './test.js';
17
+
19
18
  import fs from 'fs-extra';
20
19
  import { range, setPad, timer } from '../client/components/core/CommonJs.js';
21
- import UnderpostDeploy from './deploy.js';
22
- import UnderpostDB from './db.js';
23
- import UnderpostRootEnv from './env.js';
24
- import UnderpostRepository from './repository.js';
20
+
25
21
  import os from 'os';
26
- import Underpost, { UnderpostSSH } from '../index.js';
22
+ import Underpost from '../index.js';
27
23
  import dotenv from 'dotenv';
28
24
 
29
25
  const logger = loggerFactory(import.meta);
30
26
 
27
+ /**
28
+ * @constant DEFAULT_OPTION
29
+ * @description Default options for the UnderpostRun class.
30
+ * @type {Object}
31
+ * @property {boolean} dev - Whether to run in development mode.
32
+ * @property {string} podName - The name of the pod to run.
33
+ * @property {string} nodeName - The name of the node to run.
34
+ * @property {number} port - Custom port to use.
35
+ * @property {boolean} etcHosts - Whether to modify /etc/hosts.
36
+ * @property {string} volumeHostPath - The host path for the volume.
37
+ * @property {string} volumeMountPath - The mount path for the volume.
38
+ * @property {string} imageName - The name of the image to run.
39
+ * @property {string} containerName - The name of the container to run.
40
+ * @property {string} namespace - The namespace to run in.
41
+ * @property {string} timeoutResponse - The response timeout duration.
42
+ * @property {string} timeoutIdle - The idle timeout duration.
43
+ * @property {string} retryCount - The number of retries.
44
+ * @property {string} retryPerTryTimeout - The timeout duration per retry.
45
+ * @property {boolean} build - Whether to build the image.
46
+ * @property {number} replicas - The number of replicas to run.
47
+ * @property {boolean} force - Whether to force the operation.
48
+ * @property {boolean} reset - Whether to reset the operation.
49
+ * @property {boolean} tls - Whether to use TLS.
50
+ * @property {string} cmd - The command to run in the container.
51
+ * @property {string} tty - The TTY option for the container.
52
+ * @property {string} stdin - The stdin option for the container.
53
+ * @property {string} restartPolicy - The restart policy for the container.
54
+ * @property {string} runtimeClassName - The runtime class name for the container.
55
+ * @property {string} imagePullPolicy - The image pull policy for the container.
56
+ * @property {string} apiVersion - The API version for the container.
57
+ * @property {string} claimName - The claim name for the volume.
58
+ * @property {string} kindType - The kind of resource to create.
59
+ * @property {boolean} terminal - Whether to open a terminal.
60
+ * @property {number} devProxyPortOffset - The port offset for the development proxy.
61
+ * @property {boolean} hostNetwork - Whether to use host networking.
62
+ * @property {string} requestsMemory - The memory request for the container.
63
+ * @property {string} requestsCpu - The CPU request for the container.
64
+ * @property {string} limitsMemory - The memory limit for the container.
65
+ * @property {string} limitsCpu - The CPU limit for the container.
66
+ * @property {string} resourceTemplateId - The resource template ID.
67
+ * @property {boolean} expose - Whether to expose the service.
68
+ * @property {boolean} etcHosts - Whether to modify /etc/hosts.
69
+ * @property {string} confServerPath - The configuration server path.
70
+ * @property {string} underpostRoot - The root path of the Underpost installation.
71
+ * @property {string} cronJobs - The cron jobs to run.
72
+ * @property {string} timezone - The timezone to set.
73
+ * @property {boolean} kubeadm - Whether to run in kubeadm mode.
74
+ * @property {boolean} kind - Whether to run in kind mode.
75
+ * @property {boolean} k3s - Whether to run in k3s mode.
76
+ * @property {string} logType - The type of log to generate.
77
+ * @property {string} hosts - The hosts to use.
78
+ * @property {string} deployId - The deployment ID.
79
+ * @property {string} instanceId - The instance ID.
80
+ * @property {string} user - The user to run as.
81
+ * @property {string} pid - The process ID.
82
+ * @property {boolean} disablePrivateConfUpdate - Whether to disable private configuration updates.
83
+ * @memberof UnderpostRun
84
+ */
85
+ const DEFAULT_OPTION = {
86
+ dev: false,
87
+ podName: '',
88
+ nodeName: '',
89
+ port: 0,
90
+ volumeHostPath: '',
91
+ volumeMountPath: '',
92
+ imageName: '',
93
+ containerName: '',
94
+ namespace: 'default',
95
+ timeoutResponse: '',
96
+ timeoutIdle: '',
97
+ retryCount: '',
98
+ retryPerTryTimeout: '',
99
+ build: false,
100
+ replicas: 1,
101
+ force: false,
102
+ reset: false,
103
+ tls: false,
104
+ cmd: '',
105
+ tty: '',
106
+ stdin: '',
107
+ restartPolicy: '',
108
+ runtimeClassName: '',
109
+ imagePullPolicy: '',
110
+ apiVersion: '',
111
+ claimName: '',
112
+ kindType: '',
113
+ terminal: false,
114
+ devProxyPortOffset: 0,
115
+ hostNetwork: false,
116
+ requestsMemory: '',
117
+ requestsCpu: '',
118
+ limitsMemory: '',
119
+ limitsCpu: '',
120
+ resourceTemplateId: '',
121
+ expose: false,
122
+ etcHosts: false,
123
+ confServerPath: '',
124
+ underpostRoot: '',
125
+ cronJobs: '',
126
+ timezone: '',
127
+ kubeadm: false,
128
+ kind: false,
129
+ k3s: false,
130
+ logType: '',
131
+ hosts: '',
132
+ deployId: '',
133
+ instanceId: '',
134
+ user: '',
135
+ pid: '',
136
+ disablePrivateConfUpdate: false,
137
+ };
138
+
31
139
  /**
32
140
  * @class UnderpostRun
33
141
  * @description Manages the execution of various CLI commands and operations.
@@ -38,107 +146,6 @@ const logger = loggerFactory(import.meta);
38
146
  * @memberof UnderpostRun
39
147
  */
40
148
  class UnderpostRun {
41
- /**
42
- * @static
43
- * @description Default options for the UnderpostRun class.
44
- * @type {Object}
45
- * @property {boolean} dev - Whether to run in development mode.
46
- * @property {string} podName - The name of the pod to run.
47
- * @property {string} nodeName - The name of the node to run.
48
- * @property {number} port - Custom port to use.
49
- * @property {boolean} etcHosts - Whether to modify /etc/hosts.
50
- * @property {string} volumeHostPath - The host path for the volume.
51
- * @property {string} volumeMountPath - The mount path for the volume.
52
- * @property {string} imageName - The name of the image to run.
53
- * @property {string} containerName - The name of the container to run.
54
- * @property {string} namespace - The namespace to run in.
55
- * @property {boolean} build - Whether to build the image.
56
- * @property {number} replicas - The number of replicas to run.
57
- * @property {boolean} force - Whether to force the operation.
58
- * @property {boolean} reset - Whether to reset the operation.
59
- * @property {boolean} tls - Whether to use TLS.
60
- * @property {string} cmd - The command to run in the container.
61
- * @property {string} tty - The TTY option for the container.
62
- * @property {string} stdin - The stdin option for the container.
63
- * @property {string} restartPolicy - The restart policy for the container.
64
- * @property {string} runtimeClassName - The runtime class name for the container.
65
- * @property {string} imagePullPolicy - The image pull policy for the container.
66
- * @property {string} apiVersion - The API version for the container.
67
- * @property {string} claimName - The claim name for the volume.
68
- * @property {string} kindType - The kind of resource to create.
69
- * @property {boolean} terminal - Whether to open a terminal.
70
- * @property {number} devProxyPortOffset - The port offset for the development proxy.
71
- * @property {boolean} hostNetwork - Whether to use host networking.
72
- * @property {string} requestsMemory - The memory request for the container.
73
- * @property {string} requestsCpu - The CPU request for the container.
74
- * @property {string} limitsMemory - The memory limit for the container.
75
- * @property {string} limitsCpu - The CPU limit for the container.
76
- * @property {string} resourceTemplateId - The resource template ID.
77
- * @property {boolean} expose - Whether to expose the service.
78
- * @property {boolean} etcHosts - Whether to modify /etc/hosts.
79
- * @property {string} confServerPath - The configuration server path.
80
- * @property {string} underpostRoot - The root path of the Underpost installation.
81
- * @property {string} cronJobs - The cron jobs to run.
82
- * @property {string} timezone - The timezone to set.
83
- * @property {boolean} kubeadm - Whether to run in kubeadm mode.
84
- * @property {boolean} kind - Whether to run in kind mode.
85
- * @property {boolean} k3s - Whether to run in k3s mode.
86
- * @property {string} logType - The type of log to generate.
87
- * @property {string} hosts - The hosts to use.
88
- * @property {string} deployId - The deployment ID.
89
- * @property {string} instanceId - The instance ID.
90
- * @property {string} user - The user to run as.
91
- * @property {string} pid - The process ID.
92
- * @memberof UnderpostRun
93
- */
94
- static DEFAULT_OPTION = {
95
- dev: false,
96
- podName: '',
97
- nodeName: '',
98
- port: 0,
99
- volumeHostPath: '',
100
- volumeMountPath: '',
101
- imageName: '',
102
- containerName: '',
103
- namespace: 'default',
104
- build: false,
105
- replicas: 1,
106
- force: false,
107
- reset: false,
108
- tls: false,
109
- cmd: '',
110
- tty: '',
111
- stdin: '',
112
- restartPolicy: '',
113
- runtimeClassName: '',
114
- imagePullPolicy: '',
115
- apiVersion: '',
116
- claimName: '',
117
- kindType: '',
118
- terminal: false,
119
- devProxyPortOffset: 0,
120
- hostNetwork: false,
121
- requestsMemory: '',
122
- requestsCpu: '',
123
- limitsMemory: '',
124
- limitsCpu: '',
125
- resourceTemplateId: '',
126
- expose: false,
127
- etcHosts: false,
128
- confServerPath: '',
129
- underpostRoot: '',
130
- cronJobs: '',
131
- timezone: '',
132
- kubeadm: false,
133
- kind: false,
134
- k3s: false,
135
- logType: '',
136
- hosts: '',
137
- deployId: '',
138
- instanceId: '',
139
- user: '',
140
- pid: '',
141
- };
142
149
  /**
143
150
  * @static
144
151
  * @description Collection of runners for executing specific commands.
@@ -153,7 +160,7 @@ class UnderpostRun {
153
160
  * @param {Object} options - The default underpost runner options for customizing workflow
154
161
  * @memberof UnderpostRun
155
162
  */
156
- 'dev-cluster': (path, options = UnderpostRun.DEFAULT_OPTION) => {
163
+ 'dev-cluster': (path, options = DEFAULT_OPTION) => {
157
164
  const baseCommand = options.dev ? 'node bin' : 'underpost';
158
165
  const mongoHosts = ['mongodb-0.mongodb-service'];
159
166
  if (!options.expose) {
@@ -172,7 +179,7 @@ class UnderpostRun {
172
179
  // Detect MongoDB primary pod using centralized method
173
180
  let primaryMongoHost = 'mongodb-0.mongodb-service';
174
181
  try {
175
- const primaryPodName = UnderpostDB.API.getMongoPrimaryPodName({
182
+ const primaryPodName = Underpost.db.getMongoPrimaryPodName({
176
183
  namespace: options.namespace,
177
184
  podName: 'mongodb-0',
178
185
  });
@@ -189,7 +196,7 @@ class UnderpostRun {
189
196
  });
190
197
  }
191
198
 
192
- const hostListenResult = UnderpostDeploy.API.etcHostFactory([primaryMongoHost]);
199
+ const hostListenResult = Underpost.deploy.etcHostFactory([primaryMongoHost]);
193
200
  logger.info(hostListenResult.renderHosts);
194
201
  }
195
202
  },
@@ -201,7 +208,7 @@ class UnderpostRun {
201
208
  * @param {Object} options - The default underpost runner options for customizing workflow
202
209
  * @memberof UnderpostRun
203
210
  */
204
- metadata: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
211
+ metadata: async (path, options = DEFAULT_OPTION) => {
205
212
  const ports = '6379,27017';
206
213
  shellExec(`node bin run kill '${ports}'`);
207
214
  shellExec(`node bin run dev-cluster --dev --expose --namespace ${options.namespace}`, { async: true });
@@ -219,7 +226,7 @@ class UnderpostRun {
219
226
  * @param {Object} options - The default underpost runner options for customizing workflow
220
227
  * @memberof UnderpostRun
221
228
  */
222
- 'svc-ls': (path, options = UnderpostRun.DEFAULT_OPTION) => {
229
+ 'svc-ls': (path, options = DEFAULT_OPTION) => {
223
230
  const log = shellExec(`systemctl list-units --type=service${path ? ` | grep ${path}` : ''}`, {
224
231
  silent: true,
225
232
  stdout: true,
@@ -239,7 +246,7 @@ class UnderpostRun {
239
246
  * @param {Object} options - The default underpost runner options for customizing workflow
240
247
  * @memberof UnderpostRun
241
248
  */
242
- 'svc-rm': (path, options = UnderpostRun.DEFAULT_OPTION) => {
249
+ 'svc-rm': (path, options = DEFAULT_OPTION) => {
243
250
  shellExec(`sudo systemctl stop ${path}`);
244
251
  shellExec(`sudo systemctl disable --now ${path}`);
245
252
  shellExec(`sudo dnf remove -y ${path}*`);
@@ -254,9 +261,9 @@ class UnderpostRun {
254
261
  * @param {Object} options - The default underpost runner options for customizing workflow
255
262
  * @memberof UnderpostRun
256
263
  */
257
- 'ssh-cluster-info': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
264
+ 'ssh-cluster-info': async (path, options = DEFAULT_OPTION) => {
258
265
  const { underpostRoot } = options;
259
- if (options.deployId && options.user) await UnderpostSSH.API.setDefautlSshCredentials(options);
266
+ if (options.deployId && options.user) await Underpost.ssh.setDefautlSshCredentials(options);
260
267
  shellExec(`chmod +x ${underpostRoot}/scripts/ssh-cluster-info.sh`);
261
268
  shellExec(`${underpostRoot}/scripts/ssh-cluster-info.sh`);
262
269
  },
@@ -268,7 +275,7 @@ class UnderpostRun {
268
275
  * @param {Object} options - The default underpost runner options for customizing workflow
269
276
  * @memberof UnderpostRun
270
277
  */
271
- 'dev-hosts-expose': (path, options = UnderpostRun.DEFAULT_OPTION) => {
278
+ 'dev-hosts-expose': (path, options = DEFAULT_OPTION) => {
272
279
  shellExec(
273
280
  `node bin deploy ${path} development --disable-update-deployment --disable-update-proxy --kubeadm --etc-hosts`,
274
281
  );
@@ -281,7 +288,7 @@ class UnderpostRun {
281
288
  * @param {Object} options - The default underpost runner options for customizing workflow
282
289
  * @memberof UnderpostRun
283
290
  */
284
- 'dev-hosts-restore': (path, options = UnderpostRun.DEFAULT_OPTION) => {
291
+ 'dev-hosts-restore': (path, options = DEFAULT_OPTION) => {
285
292
  shellExec(`node bin deploy --restore-hosts`);
286
293
  },
287
294
 
@@ -292,7 +299,7 @@ class UnderpostRun {
292
299
  * @param {Object} options - The default underpost runner options for customizing workflow
293
300
  * @memberof UnderpostRun
294
301
  */
295
- 'cluster-build': (path, options = UnderpostRun.DEFAULT_OPTION) => {
302
+ 'cluster-build': (path, options = DEFAULT_OPTION) => {
296
303
  const nodeOptions = options.nodeName ? ` --node-name ${options.nodeName}` : '';
297
304
  shellExec(`node bin run clean`);
298
305
  shellExec(`node bin run --dev sync-replica template-deploy${nodeOptions}`);
@@ -312,16 +319,20 @@ class UnderpostRun {
312
319
  * @param {Object} options - The default underpost runner options for customizing workflow
313
320
  * @memberof UnderpostRun
314
321
  */
315
- 'template-deploy': (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
322
+ 'template-deploy': (path = '', options = DEFAULT_OPTION) => {
316
323
  const baseCommand = options.dev ? 'node bin' : 'underpost';
317
324
  shellExec(`${baseCommand} run clean`);
318
325
  shellExec(
319
- `${baseCommand} push ./engine-private ${options.force ? '-f ' : ''}${process.env.GITHUB_USERNAME}/engine-private`,
326
+ `${baseCommand} push ./engine-private ${options.force ? '-f ' : ''}${
327
+ process.env.GITHUB_USERNAME
328
+ }/engine-private`,
320
329
  );
321
330
  shellCd('/home/dd/engine');
322
331
  shellExec(`git reset`);
323
332
  shellExec(
324
- `${baseCommand} cmt . --empty ci package-pwa-microservices-template${path.startsWith('sync') ? `-${path}` : ''}`,
333
+ `${baseCommand} cmt . --empty ci package-pwa-microservices-template${
334
+ path.startsWith('sync') ? `-${path}` : ''
335
+ }`,
325
336
  );
326
337
  shellExec(`${baseCommand} push . ${options.force ? '-f ' : ''}${process.env.GITHUB_USERNAME}/engine`);
327
338
  },
@@ -333,10 +344,12 @@ class UnderpostRun {
333
344
  * @param {Object} options - The default underpost runner options for customizing workflow
334
345
  * @memberof UnderpostRun
335
346
  */
336
- 'template-deploy-image': (path, options = UnderpostRun.DEFAULT_OPTION) => {
347
+ 'template-deploy-image': (path, options = DEFAULT_OPTION) => {
337
348
  // const baseCommand = options.dev ? 'node bin' : 'underpost';
338
349
  shellExec(
339
- `cd /home/dd/engine && git reset && underpost cmt . --empty ci docker-image 'underpost-engine:${Underpost.version}' && underpost push . ${options.force ? '-f ' : ''}${process.env.GITHUB_USERNAME}/engine`,
350
+ `cd /home/dd/engine && git reset && underpost cmt . --empty ci docker-image 'underpost-engine:${
351
+ Underpost.version
352
+ }' && underpost push . ${options.force ? '-f ' : ''}${process.env.GITHUB_USERNAME}/engine`,
340
353
  );
341
354
  },
342
355
  /**
@@ -346,7 +359,7 @@ class UnderpostRun {
346
359
  * @param {Object} options - The default underpost runner options for customizing workflow
347
360
  * @memberof UnderpostRun
348
361
  */
349
- clean: (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
362
+ clean: (path = '', options = DEFAULT_OPTION) => {
350
363
  Underpost.repo.clean({ paths: path ? path.split(',') : ['/home/dd/engine', '/home/dd/engine/engine-private'] });
351
364
  },
352
365
  /**
@@ -356,7 +369,7 @@ class UnderpostRun {
356
369
  * @param {Object} options - The default underpost runner options for customizing workflow
357
370
  * @memberof UnderpostRun
358
371
  */
359
- pull: (path, options = UnderpostRun.DEFAULT_OPTION) => {
372
+ pull: (path, options = DEFAULT_OPTION) => {
360
373
  if (!fs.existsSync(`/home/dd`) || !fs.existsSync(`/home/dd/engine`)) {
361
374
  fs.mkdirSync(`/home/dd`, { recursive: true });
362
375
  shellExec(`cd /home/dd && underpost clone ${process.env.GITHUB_USERNAME}/engine`, {
@@ -387,7 +400,7 @@ class UnderpostRun {
387
400
  * @param {Object} options - The default underpost runner options for customizing workflow
388
401
  * @memberof UnderpostRun
389
402
  */
390
- 'release-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
403
+ 'release-deploy': (path, options = DEFAULT_OPTION) => {
391
404
  actionInitLog();
392
405
  shellExec(`underpost --version`);
393
406
  shellCd(`/home/dd/engine`);
@@ -403,7 +416,7 @@ class UnderpostRun {
403
416
  * @param {Object} options - The default underpost runner options for customizing workflow
404
417
  * @memberof UnderpostRun
405
418
  */
406
- 'ssh-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
419
+ 'ssh-deploy': (path, options = DEFAULT_OPTION) => {
407
420
  actionInitLog();
408
421
  const baseCommand = options.dev ? 'node bin' : 'underpost';
409
422
  shellCd('/home/dd/engine');
@@ -419,7 +432,7 @@ class UnderpostRun {
419
432
  * @param {Object} options - The default underpost runner options for customizing workflow
420
433
  * @memberof UnderpostRun
421
434
  */
422
- ide: (path, options = UnderpostRun.DEFAULT_OPTION) => {
435
+ ide: (path, options = DEFAULT_OPTION) => {
423
436
  const { underpostRoot } = options;
424
437
  if (path === 'install') {
425
438
  shellExec(`sudo curl -f https://zed.dev/install.sh | sh`);
@@ -429,6 +442,16 @@ class UnderpostRun {
429
442
  shellExec(`sudo dnf install -y sublime-text`);
430
443
  } else shellExec(`node ${underpostRoot}/bin/zed ${path}`);
431
444
  },
445
+ /**
446
+ * @method crypto-policy
447
+ * @description Sets the system's crypto policies to `DEFAULT:SHA1` using `update-crypto-policies` command.
448
+ * @param {string} path - The input value, identifier, or path for the operation.
449
+ * @param {Object} options - The default underpost runner options for customizing workflow
450
+ * @memberof UnderpostRun
451
+ */
452
+ 'crypto-policy': (path, options = DEFAULT_OPTION) => {
453
+ shellExec(`sudo update-crypto-policies --set DEFAULT:SHA1`);
454
+ },
432
455
  /**
433
456
  * @method sync
434
457
  * @description Cleans up, and then runs a deployment synchronization command (`underpost deploy --kubeadm --build-manifest --sync...`) using parameters parsed from `path` (deployId, replicas, versions, image, node).
@@ -436,7 +459,7 @@ class UnderpostRun {
436
459
  * @param {Object} options - The default underpost runner options for customizing workflow
437
460
  * @memberof UnderpostRun
438
461
  */
439
- sync: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
462
+ sync: async (path, options = DEFAULT_OPTION) => {
440
463
  // Dev usage: node bin run --dev --build sync dd-default
441
464
  const env = options.dev ? 'development' : 'production';
442
465
  const baseCommand = options.dev ? 'node bin' : 'underpost';
@@ -456,22 +479,28 @@ class UnderpostRun {
456
479
  node = node ? node : defaultPath[4];
457
480
  shellExec(`${baseCommand} cluster --ns-use ${options.namespace}`);
458
481
  if (isDeployRunnerContext(path, options)) {
459
- const { validVersion } = UnderpostRepository.API.privateConfUpdate(deployId);
460
- if (!validVersion) throw new Error('Version mismatch');
482
+ if (!options.disablePrivateConfUpdate) {
483
+ const { validVersion } = Underpost.repo.privateConfUpdate(deployId);
484
+ if (!validVersion) throw new Error('Version mismatch');
485
+ }
461
486
  if (options.timezone !== 'none') shellExec(`${baseCommand} run${baseClusterCommand} tz`);
462
487
  if (options.cronJobs !== 'none') shellExec(`${baseCommand} run${baseClusterCommand} cron`);
463
488
  }
464
489
 
465
490
  const currentTraffic = isDeployRunnerContext(path, options)
466
- ? UnderpostDeploy.API.getCurrentTraffic(deployId, { namespace: options.namespace })
491
+ ? Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace })
467
492
  : '';
468
493
  let targetTraffic = currentTraffic ? (currentTraffic === 'blue' ? 'green' : 'blue') : 'green';
469
- if (targetTraffic) versions = targetTraffic;
494
+ if (targetTraffic) versions = versions ? versions : targetTraffic;
495
+
496
+ const timeoutFlags = Underpost.deploy.timeoutFlagsFactory(options);
470
497
 
471
498
  shellExec(
472
- `${baseCommand} deploy --kubeadm --build-manifest --sync --info-router --replicas ${
473
- replicas
474
- } --node ${node}${image ? ` --image ${image}` : ''}${versions ? ` --versions ${versions}` : ''}${options.namespace ? ` --namespace ${options.namespace}` : ''} dd ${env}`,
499
+ `${baseCommand} deploy --kubeadm --build-manifest --sync --info-router --replicas ${replicas} --node ${node}${
500
+ image ? ` --image ${image}` : ''
501
+ }${versions ? ` --versions ${versions}` : ''}${
502
+ options.namespace ? ` --namespace ${options.namespace}` : ''
503
+ }${timeoutFlags} dd ${env}`,
475
504
  );
476
505
 
477
506
  if (isDeployRunnerContext(path, options)) {
@@ -479,19 +508,16 @@ class UnderpostRun {
479
508
  ? ` --cmd ${options.cmd.find((c) => c.match('"')) ? `"${options.cmd}"` : `'${options.cmd}'`}`
480
509
  : '';
481
510
  shellExec(
482
- `${baseCommand} deploy --kubeadm${cmdString} --replicas ${
483
- replicas
484
- } --disable-update-proxy ${deployId} ${env} --versions ${versions}${options.namespace ? ` --namespace ${options.namespace}` : ''}`,
511
+ `${baseCommand} deploy --kubeadm${cmdString} --replicas ${replicas} --disable-update-proxy ${deployId} ${env} --versions ${versions}${
512
+ options.namespace ? ` --namespace ${options.namespace}` : ''
513
+ }${timeoutFlags}`,
485
514
  );
486
515
  if (!targetTraffic)
487
- targetTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId, { namespace: options.namespace });
488
- await UnderpostDeploy.API.monitorReadyRunner(deployId, env, targetTraffic, [], options.namespace, 'underpost');
489
- UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic, replicas, options.namespace);
516
+ targetTraffic = Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace });
517
+ await Underpost.deploy.monitorReadyRunner(deployId, env, targetTraffic, [], options.namespace, 'underpost');
518
+ Underpost.deploy.switchTraffic(deployId, env, targetTraffic, replicas, options.namespace, options);
490
519
  } else
491
- logger.info(
492
- 'current traffic',
493
- UnderpostDeploy.API.getCurrentTraffic(deployId, { namespace: options.namespace }),
494
- );
520
+ logger.info('current traffic', Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace }));
495
521
  },
496
522
 
497
523
  /**
@@ -501,8 +527,8 @@ class UnderpostRun {
501
527
  * @param {Object} options - The default underpost runner options for customizing workflow
502
528
  * @memberof UnderpostRun
503
529
  */
504
- stop: async (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
505
- let currentTraffic = UnderpostDeploy.API.getCurrentTraffic(options.deployId, {
530
+ stop: async (path = '', options = DEFAULT_OPTION) => {
531
+ let currentTraffic = Underpost.deploy.getCurrentTraffic(options.deployId, {
506
532
  namespace: options.namespace,
507
533
  hostTest: options.hosts,
508
534
  });
@@ -510,7 +536,9 @@ class UnderpostRun {
510
536
 
511
537
  if (!path.match('current')) currentTraffic === 'blue' ? (currentTraffic = 'green') : (currentTraffic = 'blue');
512
538
  const [_deployId] = path.split(',');
513
- const deploymentId = `${_deployId ? _deployId : options.deployId}${options.instanceId ? `-${options.instanceId}` : ''}-${env}-${currentTraffic}`;
539
+ const deploymentId = `${_deployId ? _deployId : options.deployId}${
540
+ options.instanceId ? `-${options.instanceId}` : ''
541
+ }-${env}-${currentTraffic}`;
514
542
 
515
543
  shellExec(`kubectl delete deployment ${deploymentId} -n ${options.namespace}`);
516
544
  shellExec(`kubectl delete svc ${deploymentId}-service -n ${options.namespace}`);
@@ -523,11 +551,11 @@ class UnderpostRun {
523
551
  * @param {Object} options - The default underpost runner options for customizing workflow
524
552
  * @memberof UnderpostRun
525
553
  */
526
- 'ssh-deploy-stop': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
554
+ 'ssh-deploy-stop': async (path, options = DEFAULT_OPTION) => {
527
555
  const env = options.dev ? 'development' : 'production';
528
556
  const baseCommand = options.dev ? 'node bin' : 'underpost';
529
557
  const baseClusterCommand = options.dev ? ' --dev' : '';
530
- await UnderpostSSH.API.setDefautlSshCredentials(options);
558
+ await Underpost.ssh.setDefautlSshCredentials(options);
531
559
  shellExec(`#!/usr/bin/env bash
532
560
  set -euo pipefail
533
561
 
@@ -556,14 +584,14 @@ EOF
556
584
  * @param {Object} options - The default underpost runner options for customizing workflow
557
585
  * @memberof UnderpostRun
558
586
  */
559
- tz: (path, options = UnderpostRun.DEFAULT_OPTION) => {
587
+ tz: (path, options = DEFAULT_OPTION) => {
560
588
  const tz =
561
589
  options.timezone && options.timezone !== 'none'
562
590
  ? options.timezone
563
591
  : path
564
592
  ? path
565
- : UnderpostRootEnv.API.get('TIME_ZONE', undefined, { disableLog: true })
566
- ? UnderpostRootEnv.API.get('TIME_ZONE')
593
+ : Underpost.env.get('TIME_ZONE', undefined, { disableLog: true })
594
+ ? Underpost.env.get('TIME_ZONE')
567
595
  : process.env.TIME_ZONE
568
596
  ? process.env.TIME_ZONE
569
597
  : 'America/New_York';
@@ -577,7 +605,7 @@ EOF
577
605
  * @param {Object} options - The default underpost runner options for customizing workflow
578
606
  * @memberof UnderpostRun
579
607
  */
580
- cron: (path, options = UnderpostRun.DEFAULT_OPTION) => {
608
+ cron: (path, options = DEFAULT_OPTION) => {
581
609
  const env = options.dev ? 'development' : 'production';
582
610
  shellExec(`node bin env ${path ? path : 'dd-cron'} ${env}`);
583
611
  shellExec(`npm start`);
@@ -591,7 +619,7 @@ EOF
591
619
  * @param {Object} options - The default underpost runner options for customizing workflow
592
620
  * @memberof UnderpostRun
593
621
  */
594
- 'get-proxy': async (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
622
+ 'get-proxy': async (path = '', options = DEFAULT_OPTION) => {
595
623
  console.log(
596
624
  shellExec(`kubectl get HTTPProxy -n ${options.namespace} ${path} -o yaml`, {
597
625
  silent: true,
@@ -608,7 +636,7 @@ EOF
608
636
  );
609
637
  },
610
638
 
611
- 'instance-promote': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
639
+ 'instance-promote': async (path, options = DEFAULT_OPTION) => {
612
640
  const env = options.dev ? 'development' : 'production';
613
641
  const baseCommand = options.dev ? 'node bin' : 'underpost';
614
642
  const baseClusterCommand = options.dev ? ' --dev' : '';
@@ -630,14 +658,14 @@ EOF
630
658
  } = instance;
631
659
  if (id !== _id) continue;
632
660
  const _deployId = `${deployId}-${_id}`;
633
- const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(_deployId, {
661
+ const currentTraffic = Underpost.deploy.getCurrentTraffic(_deployId, {
634
662
  hostTest: _host,
635
663
  namespace: options.namespace,
636
664
  });
637
665
  const targetTraffic = currentTraffic ? (currentTraffic === 'blue' ? 'green' : 'blue') : 'blue';
638
666
  let proxyYaml =
639
- UnderpostDeploy.API.baseProxyYamlFactory({ host: _host, env: options.tls ? 'production' : env, options }) +
640
- UnderpostDeploy.API.deploymentYamlServiceFactory({
667
+ Underpost.deploy.baseProxyYamlFactory({ host: _host, env: options.tls ? 'production' : env, options }) +
668
+ Underpost.deploy.deploymentYamlServiceFactory({
641
669
  path: _path,
642
670
  port: _fromPort,
643
671
  // serviceId: deployId,
@@ -648,7 +676,7 @@ EOF
648
676
  });
649
677
  if (options.tls) {
650
678
  shellExec(`sudo kubectl delete Certificate ${_host} -n ${options.namespace} --ignore-not-found`);
651
- proxyYaml += UnderpostDeploy.API.buildCertManagerCertificate({ ...options, host: _host });
679
+ proxyYaml += Underpost.deploy.buildCertManagerCertificate({ ...options, host: _host });
652
680
  }
653
681
  // console.log(proxyYaml);
654
682
  shellExec(`kubectl delete HTTPProxy ${_host} --namespace ${options.namespace} --ignore-not-found`);
@@ -668,7 +696,7 @@ EOF
668
696
  * @param {Object} options - The default underpost runner options for customizing workflow
669
697
  * @memberof UnderpostRun
670
698
  */
671
- instance: async (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
699
+ instance: async (path = '', options = DEFAULT_OPTION) => {
672
700
  const env = options.dev ? 'development' : 'production';
673
701
  const baseCommand = options.dev ? 'node bin' : 'underpost';
674
702
  const baseClusterCommand = options.dev ? ' --dev' : '';
@@ -706,20 +734,20 @@ EOF
706
734
  shellExec(`sudo kind load docker-image ${_image}`);
707
735
  }
708
736
 
709
- const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(_deployId, {
737
+ const currentTraffic = Underpost.deploy.getCurrentTraffic(_deployId, {
710
738
  hostTest: _host,
711
739
  namespace: options.namespace,
712
740
  });
713
741
 
714
742
  const targetTraffic = currentTraffic ? (currentTraffic === 'blue' ? 'green' : 'blue') : 'blue';
715
743
  const podId = `${_deployId}-${env}-${targetTraffic}`;
716
- const ignorePods = UnderpostDeploy.API.get(podId, 'pods', options.namespace).map((p) => p.NAME);
717
- UnderpostDeploy.API.configMap(env, options.namespace);
744
+ const ignorePods = Underpost.deploy.get(podId, 'pods', options.namespace).map((p) => p.NAME);
745
+ Underpost.deploy.configMap(env, options.namespace);
718
746
  shellExec(`kubectl delete service ${podId}-service --namespace ${options.namespace} --ignore-not-found`);
719
747
  shellExec(`kubectl delete deployment ${podId} --namespace ${options.namespace} --ignore-not-found`);
720
748
  for (const _volume of _volumes)
721
749
  if (_volume.claimName)
722
- UnderpostDeploy.API.deployVolume(_volume, {
750
+ Underpost.deploy.deployVolume(_volume, {
723
751
  namespace: options.namespace,
724
752
  deployId: _deployId,
725
753
  env,
@@ -727,17 +755,19 @@ EOF
727
755
  nodeName: options.nodeName,
728
756
  });
729
757
  let deploymentYaml = `---
730
- ${UnderpostDeploy.API.deploymentYamlPartsFactory({
731
- deployId: _deployId,
732
- env,
733
- suffix: targetTraffic,
734
- resources: UnderpostDeploy.API.resourcesFactory(options),
735
- replicas,
736
- image: _image,
737
- namespace: options.namespace,
738
- volumes: _volumes,
739
- cmd: _cmd[env],
740
- }).replace('{{ports}}', buildKindPorts(_fromPort, _toPort))}
758
+ ${Underpost.deploy
759
+ .deploymentYamlPartsFactory({
760
+ deployId: _deployId,
761
+ env,
762
+ suffix: targetTraffic,
763
+ resources: Underpost.deploy.resourcesFactory(options),
764
+ replicas,
765
+ image: _image,
766
+ namespace: options.namespace,
767
+ volumes: _volumes,
768
+ cmd: _cmd[env],
769
+ })
770
+ .replace('{{ports}}', buildKindPorts(_fromPort, _toPort))}
741
771
  `;
742
772
  // console.log(deploymentYaml);
743
773
  shellExec(
@@ -747,7 +777,7 @@ EOF
747
777
  `,
748
778
  { disableLog: true },
749
779
  );
750
- const { ready, readyPods } = await UnderpostDeploy.API.monitorReadyRunner(
780
+ const { ready, readyPods } = await Underpost.deploy.monitorReadyRunner(
751
781
  _deployId,
752
782
  env,
753
783
  targetTraffic,
@@ -768,20 +798,20 @@ EOF
768
798
  );
769
799
  }
770
800
  if (options.etcHosts) {
771
- const hostListenResult = UnderpostDeploy.API.etcHostFactory(etcHosts);
801
+ const hostListenResult = Underpost.deploy.etcHostFactory(etcHosts);
772
802
  logger.info(hostListenResult.renderHosts);
773
803
  }
774
804
  },
775
805
 
776
806
  /**
777
807
  * @method ls-deployments
778
- * @description Retrieves and logs a table of Kubernetes deployments using `UnderpostDeploy.API.get`.
808
+ * @description Retrieves and logs a table of Kubernetes deployments using `Underpost.deploy.get`.
779
809
  * @param {string} path - The input value, identifier, or path for the operation (used as an optional deployment name filter).
780
810
  * @param {Object} options - The default underpost runner options for customizing workflow
781
811
  * @memberof UnderpostRun
782
812
  */
783
- 'ls-deployments': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
784
- console.table(await UnderpostDeploy.API.get(path, 'deployments', options.namespace));
813
+ 'ls-deployments': async (path, options = DEFAULT_OPTION) => {
814
+ console.table(await Underpost.deploy.get(path, 'deployments', options.namespace));
785
815
  },
786
816
 
787
817
  /**
@@ -791,7 +821,7 @@ EOF
791
821
  * @param {Object} options - The default underpost runner options for customizing workflow
792
822
  * @memberof UnderpostRun
793
823
  */
794
- 'host-update': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
824
+ 'host-update': async (path, options = DEFAULT_OPTION) => {
795
825
  // const baseCommand = options.dev ? 'node bin' : 'underpost';
796
826
  shellExec(`chmod +x ${options.underpostRoot}/scripts/rocky-setup.sh`);
797
827
  shellExec(`${options.underpostRoot}/scripts/rocky-setup.sh${options.dev ? ' --install-dev' : ``}`);
@@ -804,14 +834,14 @@ EOF
804
834
  * @param {Object} options - The default underpost runner options for customizing workflow
805
835
  * @memberof UnderpostRun
806
836
  */
807
- 'dd-container': async (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
837
+ 'dd-container': async (path = '', options = DEFAULT_OPTION) => {
808
838
  const baseCommand = options.dev ? 'node bin' : 'underpost';
809
839
  const baseClusterCommand = options.dev ? ' --dev' : '';
810
840
  const currentImage = options.imageName
811
841
  ? options.imageName
812
- : UnderpostDeploy.API.getCurrentLoadedImages(options.nodeName ? options.nodeName : 'kind-worker', false).find(
813
- (o) => o.IMAGE.match('underpost'),
814
- );
842
+ : Underpost.deploy
843
+ .getCurrentLoadedImages(options.nodeName ? options.nodeName : 'kind-worker', false)
844
+ .find((o) => o.IMAGE.match('underpost'));
815
845
  const podName = options.podName || `underpost-dev-container`;
816
846
  const volumeHostPath = options.claimName || '/home/dd';
817
847
  const claimName = options.claimName || `pvc-dd`;
@@ -851,7 +881,7 @@ EOF
851
881
  args: [daemonProcess(path ? path : `cd /home/dd/engine && npm install && npm run test`)],
852
882
  };
853
883
 
854
- await UnderpostRun.RUNNERS['deploy-job'](path, payload);
884
+ await Underpost.run.RUNNERS['deploy-job'](path, payload);
855
885
  },
856
886
 
857
887
  /**
@@ -861,7 +891,7 @@ EOF
861
891
  * @param {Object} options - The default underpost runner options for customizing workflow
862
892
  * @memberof UnderpostRun
863
893
  */
864
- 'ip-info': (path, options = UnderpostRun.DEFAULT_OPTION) => {
894
+ 'ip-info': (path, options = DEFAULT_OPTION) => {
865
895
  const { underpostRoot } = options;
866
896
  shellExec(`chmod +x ${underpostRoot}/scripts/ip-info.sh`);
867
897
  shellExec(`${underpostRoot}/scripts/ip-info.sh ${path}`);
@@ -874,12 +904,12 @@ EOF
874
904
  * @param {Object} options - The default underpost runner options for customizing workflow
875
905
  * @memberof UnderpostRun
876
906
  */
877
- monitor: (path, options = UnderpostRun.DEFAULT_OPTION) => {
907
+ monitor: (path, options = DEFAULT_OPTION) => {
878
908
  const pid = getTerminalPid();
879
909
  logger.info('monitor pid', pid);
880
910
  const checkPath = '/await';
881
911
  const _monitor = async () => {
882
- const result = UnderpostDeploy.API.existsContainerFile({ podName: path, path: checkPath });
912
+ const result = Underpost.deploy.existsContainerFile({ podName: path, path: checkPath });
883
913
  logger.info('monitor', result);
884
914
  if (result === true) {
885
915
  switch (path) {
@@ -916,7 +946,7 @@ EOF
916
946
  const checkPath = `/latent_space_plot.png`;
917
947
  const outsPaths = [];
918
948
  logger.info('monitor', checkPath);
919
- while (!UnderpostDeploy.API.existsContainerFile({ podName, path: `/home/dd/docs${checkPath}` }))
949
+ while (!Underpost.deploy.existsContainerFile({ podName, path: `/home/dd/docs${checkPath}` }))
920
950
  await timer(1000);
921
951
 
922
952
  {
@@ -954,7 +984,7 @@ EOF
954
984
  * @param {Object} options - The default underpost runner options for customizing workflow
955
985
  * @memberof UnderpostRun
956
986
  */
957
- 'db-client': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
987
+ 'db-client': async (path, options = DEFAULT_OPTION) => {
958
988
  const { underpostRoot } = options;
959
989
 
960
990
  const image = 'adminer:4.7.6-standalone';
@@ -969,7 +999,7 @@ EOF
969
999
 
970
1000
  shellExec(`kubectl delete deployment adminer -n ${options.namespace} --ignore-not-found`);
971
1001
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/deployment/adminer/. -n ${options.namespace}`);
972
- const successInstance = await UnderpostTest.API.statusMonitor('adminer', 'Running', 'pods', 1000, 60 * 10);
1002
+ const successInstance = await Underpost.test.statusMonitor('adminer', 'Running', 'pods', 1000, 60 * 10);
973
1003
 
974
1004
  if (successInstance) {
975
1005
  shellExec(`underpost deploy --expose adminer --namespace ${options.namespace}`);
@@ -983,16 +1013,16 @@ EOF
983
1013
  * @param {Object} options - The default underpost runner options for customizing workflow
984
1014
  * @memberof UnderpostRun
985
1015
  */
986
- 'git-conf': (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
987
- const defaultUsername = UnderpostRootEnv.API.get('GITHUB_USERNAME');
988
- const defaultEmail = UnderpostRootEnv.API.get('GITHUB_EMAIL');
1016
+ 'git-conf': (path = '', options = DEFAULT_OPTION) => {
1017
+ const defaultUsername = Underpost.env.get('GITHUB_USERNAME');
1018
+ const defaultEmail = Underpost.env.get('GITHUB_EMAIL');
989
1019
  const validPath = path && path.split(',').length;
990
1020
  const [username, email] = validPath ? path.split(',') : [defaultUsername, defaultEmail];
991
1021
  if (validPath) {
992
- UnderpostRootEnv.API.set('GITHUB_USERNAME', username);
993
- UnderpostRootEnv.API.set('GITHUB_EMAIL', email);
994
- UnderpostRootEnv.API.get('GITHUB_USERNAME');
995
- UnderpostRootEnv.API.get('GITHUB_EMAIL');
1022
+ Underpost.env.set('GITHUB_USERNAME', username);
1023
+ Underpost.env.set('GITHUB_EMAIL', email);
1024
+ Underpost.env.get('GITHUB_USERNAME');
1025
+ Underpost.env.get('GITHUB_EMAIL');
996
1026
  }
997
1027
  shellExec(
998
1028
  `git config --global credential.helper "" && ` +
@@ -1022,20 +1052,27 @@ EOF
1022
1052
  * @param {Object} options - The default underpost runner options for customizing workflow
1023
1053
  * @memberof UnderpostRun
1024
1054
  */
1025
- promote: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
1055
+ promote: async (path, options = DEFAULT_OPTION) => {
1026
1056
  let [inputDeployId, inputEnv, inputReplicas] = path.split(',');
1027
1057
  if (!inputEnv) inputEnv = 'production';
1028
1058
  if (!inputReplicas) inputReplicas = 1;
1029
1059
  if (inputDeployId === 'dd') {
1030
1060
  for (const deployId of fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',')) {
1031
- const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId, { namespace: options.namespace });
1061
+ const currentTraffic = Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace });
1032
1062
  const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
1033
- UnderpostDeploy.API.switchTraffic(deployId, inputEnv, targetTraffic, inputReplicas, options.namespace);
1063
+ Underpost.deploy.switchTraffic(deployId, inputEnv, targetTraffic, inputReplicas, options.namespace, options);
1034
1064
  }
1035
1065
  } else {
1036
- const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(inputDeployId, { namespace: options.namespace });
1066
+ const currentTraffic = Underpost.deploy.getCurrentTraffic(inputDeployId, { namespace: options.namespace });
1037
1067
  const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
1038
- UnderpostDeploy.API.switchTraffic(inputDeployId, inputEnv, targetTraffic, inputReplicas, options.namespace);
1068
+ Underpost.deploy.switchTraffic(
1069
+ inputDeployId,
1070
+ inputEnv,
1071
+ targetTraffic,
1072
+ inputReplicas,
1073
+ options.namespace,
1074
+ options,
1075
+ );
1039
1076
  }
1040
1077
  },
1041
1078
  /**
@@ -1045,7 +1082,7 @@ EOF
1045
1082
  * @param {Object} options - The default underpost runner options for customizing workflow
1046
1083
  * @memberof UnderpostRun
1047
1084
  */
1048
- metrics: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
1085
+ metrics: async (path, options = DEFAULT_OPTION) => {
1049
1086
  const deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
1050
1087
  let hosts = [];
1051
1088
  for (const deployId of deployList) {
@@ -1062,7 +1099,7 @@ EOF
1062
1099
  * @param {Object} options - The default underpost runner options for customizing workflow
1063
1100
  * @memberof UnderpostRun
1064
1101
  */
1065
- cluster: async (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
1102
+ cluster: async (path = '', options = DEFAULT_OPTION) => {
1066
1103
  const { underpostRoot } = options;
1067
1104
  const env = options.dev ? 'development' : 'production';
1068
1105
  const baseCommand = options.dev ? 'node bin' : 'underpost';
@@ -1122,20 +1159,20 @@ EOF
1122
1159
  * @param {Object} options - The default underpost runner options for customizing workflow
1123
1160
  * @memberof UnderpostRun
1124
1161
  */
1125
- deploy: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
1162
+ deploy: async (path, options = DEFAULT_OPTION) => {
1126
1163
  const deployId = path;
1127
- const { validVersion } = UnderpostRepository.API.privateConfUpdate(deployId);
1164
+ const { validVersion } = Underpost.repo.privateConfUpdate(deployId);
1128
1165
  if (!validVersion) throw new Error('Version mismatch');
1129
- const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId, { namespace: options.namespace });
1166
+ const currentTraffic = Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace });
1130
1167
  const targetTraffic = currentTraffic === 'blue' ? 'green' : 'blue';
1131
1168
  const env = 'production';
1132
- const ignorePods = UnderpostDeploy.API.get(`${deployId}-${env}-${targetTraffic}`, 'pods', options.namespace).map(
1133
- (p) => p.NAME,
1134
- );
1169
+ const ignorePods = Underpost.deploy
1170
+ .get(`${deployId}-${env}-${targetTraffic}`, 'pods', options.namespace)
1171
+ .map((p) => p.NAME);
1135
1172
 
1136
1173
  shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${targetTraffic} -n ${options.namespace}`);
1137
1174
 
1138
- await UnderpostDeploy.API.monitorReadyRunner(
1175
+ await Underpost.deploy.monitorReadyRunner(
1139
1176
  deployId,
1140
1177
  env,
1141
1178
  targetTraffic,
@@ -1144,7 +1181,7 @@ EOF
1144
1181
  'underpost',
1145
1182
  );
1146
1183
 
1147
- UnderpostDeploy.API.switchTraffic(deployId, env, targetTraffic, options.replicas, options.namespace);
1184
+ Underpost.deploy.switchTraffic(deployId, env, targetTraffic, options.replicas, options.namespace, options);
1148
1185
  },
1149
1186
 
1150
1187
  /**
@@ -1154,7 +1191,7 @@ EOF
1154
1191
  * @param {Object} options - The default underpost runner options for customizing workflow
1155
1192
  * @memberof UnderpostRun
1156
1193
  */
1157
- 'disk-clean': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
1194
+ 'disk-clean': async (path, options = DEFAULT_OPTION) => {
1158
1195
  const { underpostRoot } = options;
1159
1196
  shellExec(`chmod +x ${underpostRoot}/scripts/disk-clean.sh`);
1160
1197
  shellExec(`./scripts/disk-clean.sh`);
@@ -1167,7 +1204,7 @@ EOF
1167
1204
  * @param {Object} options - The default underpost runner options for customizing workflow
1168
1205
  * @memberof UnderpostRun
1169
1206
  */
1170
- 'disk-usage': async (path = '/', options = UnderpostRun.DEFAULT_OPTION) => {
1207
+ 'disk-usage': async (path = '/', options = DEFAULT_OPTION) => {
1171
1208
  if (!path) path = '/';
1172
1209
  logger.info('Mount filesystem');
1173
1210
  shellExec(`df -h${path === '/' ? '' : ` ${path}`}`);
@@ -1182,7 +1219,7 @@ EOF
1182
1219
  * @param {Object} options - The default underpost runner options for customizing workflow
1183
1220
  * @memberof UnderpostRun
1184
1221
  */
1185
- dev: async (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
1222
+ dev: async (path = '', options = DEFAULT_OPTION) => {
1186
1223
  let [deployId, subConf, host, _path, clientHostPort] = path.split(',');
1187
1224
  if (options.confServerPath) {
1188
1225
  const confServer = JSON.parse(fs.readFileSync(options.confServerPath, 'utf8'));
@@ -1216,7 +1253,9 @@ EOF
1216
1253
  }
1217
1254
  shellExec(`node bin run dev-cluster --expose --namespace ${options.namespace}`, { async: true });
1218
1255
  {
1219
- const cmd = `npm run dev-api ${deployId} ${subConf} ${host} ${_path} ${clientHostPort}${options.tls ? ' tls' : ''}`;
1256
+ const cmd = `npm run dev-api ${deployId} ${subConf} ${host} ${_path} ${clientHostPort}${
1257
+ options.tls ? ' tls' : ''
1258
+ }`;
1220
1259
  options.terminal ? openTerminal(cmd) : shellExec(cmd, { async: true });
1221
1260
  }
1222
1261
  await awaitDeployMonitor(true);
@@ -1239,7 +1278,7 @@ EOF
1239
1278
  * @param {Object} options - The default underpost runner options for customizing workflow
1240
1279
  * @memberof UnderpostRun
1241
1280
  */
1242
- service: async (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
1281
+ service: async (path = '', options = DEFAULT_OPTION) => {
1243
1282
  const env = options.dev ? 'development' : 'production';
1244
1283
  const baseCommand = options.dev ? 'node bin' : 'underpost';
1245
1284
  const baseClusterCommand = options.dev ? ' --dev' : '';
@@ -1304,25 +1343,49 @@ EOF
1304
1343
  break;
1305
1344
  }
1306
1345
  }
1307
- const success = await UnderpostTest.API.statusMonitor(podToMonitor);
1346
+ const success = await Underpost.test.statusMonitor(podToMonitor);
1308
1347
  if (success) {
1309
- const versions = UnderpostDeploy.API.getCurrentTraffic(deployId, { namespace: options.namespace }) || 'blue';
1348
+ const versions = Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace }) || 'blue';
1310
1349
  if (!node) node = os.hostname();
1350
+ const timeoutFlags = Underpost.deploy.timeoutFlagsFactory(options);
1311
1351
  shellExec(
1312
- `${baseCommand} deploy${options.dev ? '' : ' --kubeadm'}${options.devProxyPortOffset ? ' --disable-deployment-proxy' : ''} --build-manifest --sync --info-router --replicas ${
1313
- replicas
1314
- } --node ${node}${image ? ` --image ${image}` : ''}${versions ? ` --versions ${versions}` : ''} dd ${env}`,
1352
+ `${baseCommand} deploy${options.dev ? '' : ' --kubeadm'}${
1353
+ options.devProxyPortOffset ? ' --disable-deployment-proxy' : ''
1354
+ } --build-manifest --sync --info-router --replicas ${replicas} --node ${node}${
1355
+ image ? ` --image ${image}` : ''
1356
+ }${versions ? ` --versions ${versions}` : ''}${timeoutFlags} dd ${env}`,
1315
1357
  );
1316
1358
  shellExec(
1317
- `${baseCommand} deploy${options.dev ? '' : ' --kubeadm'}${options.devProxyPortOffset ? ' --disable-deployment-proxy' : ''} --disable-update-deployment ${deployId} ${env} --versions ${versions}`,
1359
+ `${baseCommand} deploy${options.dev ? '' : ' --kubeadm'}${
1360
+ options.devProxyPortOffset ? ' --disable-deployment-proxy' : ''
1361
+ } --disable-update-deployment ${deployId} ${env} --versions ${versions}`,
1318
1362
  );
1319
1363
  } else logger.error(`Service pod ${podToMonitor} failed to start in time.`);
1320
1364
  if (options.etcHosts === true) {
1321
- const hostListenResult = UnderpostDeploy.API.etcHostFactory([host]);
1365
+ const hostListenResult = Underpost.deploy.etcHostFactory([host]);
1322
1366
  logger.info(hostListenResult.renderHosts);
1323
1367
  }
1324
1368
  },
1325
1369
 
1370
+ /**
1371
+ * @method etc-hosts
1372
+ * @description Generates and logs the contents for the `/etc/hosts` file based on provided hosts or deployment configurations.
1373
+ * @param {string} path - The input value, identifier, or path for the operation (used as a comma-separated list of hosts).
1374
+ * @param {Object} options - The default underpost runner options for customizing workflow
1375
+ * @memberof UnderpostRun
1376
+ */
1377
+ 'etc-hosts': async (path = '', options = DEFAULT_OPTION) => {
1378
+ const hosts = path ? path.split(',') : [];
1379
+ if (options.deployId) {
1380
+ const confServer = JSON.parse(
1381
+ fs.readFileSync(`./engine-private/conf/${options.deployId}/conf.server.json`, 'utf8'),
1382
+ );
1383
+ hosts.push(...Object.keys(confServer));
1384
+ }
1385
+ const hostListenResult = Underpost.deploy.etcHostFactory(hosts);
1386
+ logger.info(hostListenResult.renderHosts);
1387
+ },
1388
+
1326
1389
  /**
1327
1390
  * @method sh
1328
1391
  * @description Enables remote control for the Kitty terminal emulator.
@@ -1330,11 +1393,13 @@ EOF
1330
1393
  * @param {Object} options - The default underpost runner options for customizing workflow
1331
1394
  * @memberof UnderpostRun
1332
1395
  */
1333
- sh: async (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
1396
+ sh: async (path = '', options = DEFAULT_OPTION) => {
1334
1397
  let [operator, arg0, arg1] = path.split(',');
1335
1398
  if (operator == 'copy') {
1336
1399
  shellExec(
1337
- `kitty @ get-text ${arg0 === 'all' ? '--match all' : '--self'} --extent all${arg1 === 'ansi' ? ' --ansi yes' : ''} | kitty +kitten clipboard`,
1400
+ `kitty @ get-text ${arg0 === 'all' ? '--match all' : '--self'} --extent all${
1401
+ arg1 === 'ansi' ? ' --ansi yes' : ''
1402
+ } | kitty +kitten clipboard`,
1338
1403
  );
1339
1404
  return;
1340
1405
  }
@@ -1348,7 +1413,7 @@ EOF
1348
1413
  * @param {Object} options - The default underpost runner options for customizing workflow
1349
1414
  * @memberof UnderpostRun
1350
1415
  */
1351
- log: async (path, options = UnderpostRun.DEFAULT_OPTION) => {
1416
+ log: async (path, options = DEFAULT_OPTION) => {
1352
1417
  const [filePath, keywords, lines] = path.split(',');
1353
1418
  let result = shellExec(`grep -i -E ${lines ? `-C ${lines} ` : ''}'${keywords}' ${filePath}`, {
1354
1419
  stdout: true,
@@ -1365,12 +1430,22 @@ EOF
1365
1430
  * @param {Object} options - The default underpost runner options for customizing workflow
1366
1431
  * @memberof UnderpostRun
1367
1432
  */
1368
- ps: async (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
1369
- const out = shellExec(`ps aux${path ? `| grep '${path}' | grep -v grep` : ''}`, {
1370
- stdout: true,
1371
- silent: true,
1372
- });
1373
- console.log(path ? out.replaceAll(path, path.bgYellow.black.bold) : out);
1433
+ ps: async (path = '', options = DEFAULT_OPTION) => {
1434
+ const out = shellExec(
1435
+ path.startsWith('top-consumers')
1436
+ ? `ps -eo pid,%cpu,%mem,rss,cmd --sort=-%cpu | head -n ${path.split(',')[1] || 15}`
1437
+ : path
1438
+ ? `(ps -eo pid,%cpu,%mem,rss,cmd -ww | head -n1; ps -eo pid,%cpu,%mem,rss,cmd -ww | tail -n +2 | grep -F ${path})`
1439
+ : `ps -eo pid,%cpu,%mem,rss,cmd -ww`,
1440
+ {
1441
+ stdout: true,
1442
+ silent: true,
1443
+ },
1444
+ );
1445
+
1446
+ console.log(
1447
+ path ? out.replaceAll(path.split(',')[2] || path, (path.split(',')[2] || path).bgYellow.black.bold) : out,
1448
+ );
1374
1449
  },
1375
1450
 
1376
1451
  /**
@@ -1380,7 +1455,7 @@ EOF
1380
1455
  * @param {Object} options - The default underpost runner options for customizing workflow
1381
1456
  * @memberof UnderpostRun
1382
1457
  */
1383
- ptls: async (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
1458
+ ptls: async (path = '', options = DEFAULT_OPTION) => {
1384
1459
  shellExec(`chmod +x ${options.underpostRoot}/scripts/ports-ls.sh`);
1385
1460
  shellExec(`${options.underpostRoot}/scripts/ports-ls.sh`);
1386
1461
  },
@@ -1391,7 +1466,7 @@ EOF
1391
1466
  * @param {Object} options - The default underpost runner options for customizing workflow
1392
1467
  * @memberof UnderpostRun
1393
1468
  */
1394
- 'release-cmt': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
1469
+ 'release-cmt': async (path, options = DEFAULT_OPTION) => {
1395
1470
  shellExec(`underpost run pull`);
1396
1471
  shellExec(`underpost run secret`);
1397
1472
  shellCd(`/home/dd/engine`);
@@ -1406,7 +1481,7 @@ EOF
1406
1481
  * @param {Object} options - The default underpost runner options for customizing workflow
1407
1482
  * @memberof UnderpostRun
1408
1483
  */
1409
- 'deploy-test': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
1484
+ 'deploy-test': async (path, options = DEFAULT_OPTION) => {
1410
1485
  // Note: use recomendation empty deploy cluster: node bin --dev cluster
1411
1486
  const env = options.dev ? 'development' : 'production';
1412
1487
  const baseCommand = options.dev ? 'node bin' : 'underpost';
@@ -1431,7 +1506,7 @@ EOF
1431
1506
  * @param {Object} options - The default underpost runner options for customizing workflow
1432
1507
  * @memberof UnderpostRun
1433
1508
  */
1434
- 'sync-replica': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
1509
+ 'sync-replica': async (path, options = DEFAULT_OPTION) => {
1435
1510
  const env = options.dev ? 'development' : 'production';
1436
1511
  const baseCommand = options.dev ? 'node bin' : 'underpost';
1437
1512
 
@@ -1462,10 +1537,10 @@ EOF
1462
1537
  * @param {Object} options - The default underpost runner options for customizing workflow
1463
1538
  * @memberof UnderpostRun
1464
1539
  */
1465
- 'tf-vae-test': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
1540
+ 'tf-vae-test': async (path, options = DEFAULT_OPTION) => {
1466
1541
  const { underpostRoot } = options;
1467
1542
  const podName = 'tf-vae-test';
1468
- await UnderpostRun.RUNNERS['deploy-job']('', {
1543
+ await Underpost.run.CALL('deploy-job', '', {
1469
1544
  podName,
1470
1545
  // volumeMountPath: '/custom_images',
1471
1546
  // volumeHostPath: '/home/dd/engine/src/client/public/cyberia/assets/skin',
@@ -1503,7 +1578,7 @@ EOF
1503
1578
  * @param {Object} options - The default underpost runner options for customizing workflow
1504
1579
  * @memberof UnderpostRun
1505
1580
  */
1506
- 'spark-template': (path, options = UnderpostRun.DEFAULT_OPTION) => {
1581
+ 'spark-template': (path, options = DEFAULT_OPTION) => {
1507
1582
  const dir = '/home/dd/spark-template';
1508
1583
  shellExec(`sudo rm -rf ${dir}`);
1509
1584
  shellCd('/home/dd');
@@ -1530,7 +1605,7 @@ EOF
1530
1605
  * @param {Object} options - The default underpost runner options for customizing workflow
1531
1606
  * @memberof UnderpostRun
1532
1607
  */
1533
- rmi: (path, options = UnderpostRun.DEFAULT_OPTION) => {
1608
+ rmi: (path, options = DEFAULT_OPTION) => {
1534
1609
  shellExec(`podman rmi $(podman images -qa) --force`);
1535
1610
  },
1536
1611
  /**
@@ -1540,7 +1615,7 @@ EOF
1540
1615
  * @param {Object} options - The default underpost runner options for customizing workflow
1541
1616
  * @memberof UnderpostRun
1542
1617
  */
1543
- kill: (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
1618
+ kill: (path = '', options = DEFAULT_OPTION) => {
1544
1619
  if (options.pid) return shellExec(`sudo kill -9 ${options.pid}`);
1545
1620
  for (const _path of path.split(',')) {
1546
1621
  if (_path.split('+')[1]) {
@@ -1559,7 +1634,7 @@ EOF
1559
1634
  * @param {Object} options - The default underpost runner options for customizing workflow
1560
1635
  * @memberof UnderpostRun
1561
1636
  */
1562
- secret: (path, options = UnderpostRun.DEFAULT_OPTION) => {
1637
+ secret: (path, options = DEFAULT_OPTION) => {
1563
1638
  const secretPath = path ? path : `/home/dd/engine/engine-private/conf/dd-cron/.env.production`;
1564
1639
  const command = options.dev
1565
1640
  ? `node bin secret underpost --create-from-file ${secretPath}`
@@ -1568,13 +1643,13 @@ EOF
1568
1643
  },
1569
1644
  /**
1570
1645
  * @method underpost-config
1571
- * @description Calls `UnderpostDeploy.API.configMap` to create a Kubernetes ConfigMap, defaulting to the 'production' environment.
1646
+ * @description Calls `Underpost.deploy.configMap` to create a Kubernetes ConfigMap, defaulting to the 'production' environment.
1572
1647
  * @param {string} path - The input value, identifier, or path for the operation (used as the optional configuration name/environment).
1573
1648
  * @param {Object} options - The default underpost runner options for customizing workflow
1574
1649
  * @memberof UnderpostRun
1575
1650
  */
1576
- 'underpost-config': (path = '', options = UnderpostRun.DEFAULT_OPTION) => {
1577
- UnderpostDeploy.API.configMap(path ? path : 'production', options.namespace);
1651
+ 'underpost-config': (path = '', options = DEFAULT_OPTION) => {
1652
+ Underpost.deploy.configMap(path ? path : 'production', options.namespace);
1578
1653
  },
1579
1654
  /**
1580
1655
  * @method gpu-env
@@ -1583,7 +1658,7 @@ EOF
1583
1658
  * @param {Object} options - The default underpost runner options for customizing workflow
1584
1659
  * @memberof UnderpostRun
1585
1660
  */
1586
- 'gpu-env': (path, options = UnderpostRun.DEFAULT_OPTION) => {
1661
+ 'gpu-env': (path, options = DEFAULT_OPTION) => {
1587
1662
  shellExec(
1588
1663
  `node bin cluster --dev --reset && node bin cluster --dev --dedicated-gpu --kubeadm && kubectl get pods --all-namespaces -o wide -w`,
1589
1664
  );
@@ -1595,7 +1670,7 @@ EOF
1595
1670
  * @param {Object} options - The default underpost runner options for customizing workflow
1596
1671
  * @memberof UnderpostRun
1597
1672
  */
1598
- 'tf-gpu-test': (path, options = UnderpostRun.DEFAULT_OPTION) => {
1673
+ 'tf-gpu-test': (path, options = DEFAULT_OPTION) => {
1599
1674
  const { underpostRoot, namespace } = options;
1600
1675
  shellExec(`kubectl delete configmap tf-gpu-test-script -n ${namespace} --ignore-not-found`);
1601
1676
  shellExec(`kubectl delete pod tf-gpu-test-pod -n ${namespace} --ignore-not-found`);
@@ -1609,7 +1684,7 @@ EOF
1609
1684
  * @param {Object} options - The default underpost runner options for customizing workflow
1610
1685
  * @memberof UnderpostRun
1611
1686
  */
1612
- 'deploy-job': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
1687
+ 'deploy-job': async (path, options = DEFAULT_OPTION) => {
1613
1688
  const podName = options.podName || 'deploy-job';
1614
1689
  const volumeName = `${podName}-volume`;
1615
1690
  if (typeof options.args === 'string') options.args = options.args.split(',');
@@ -1646,7 +1721,7 @@ EOF
1646
1721
  ? 'Directory'
1647
1722
  : 'File';
1648
1723
 
1649
- const envs = UnderpostRootEnv.API.list();
1724
+ const envs = Underpost.env.list();
1650
1725
 
1651
1726
  const cmd = `kubectl apply -f - <<EOF
1652
1727
  apiVersion: ${apiVersion}
@@ -1689,14 +1764,14 @@ ${Object.keys(envs)
1689
1764
  .join('\n')}`}
1690
1765
  ${
1691
1766
  enableVolumeMount
1692
- ? UnderpostDeploy.API.volumeFactory([{ volumeMountPath, volumeName, volumeHostPath, volumeType, claimName }]).render
1767
+ ? Underpost.deploy.volumeFactory([{ volumeMountPath, volumeName, volumeHostPath, volumeType, claimName }]).render
1693
1768
  : ''
1694
1769
  }
1695
1770
  EOF`;
1696
1771
  shellExec(`kubectl delete pod ${podName} -n ${namespace} --ignore-not-found`);
1697
1772
  console.log(cmd);
1698
1773
  shellExec(cmd, { disableLog: true });
1699
- const successInstance = await UnderpostTest.API.statusMonitor(podName);
1774
+ const successInstance = await Underpost.test.statusMonitor(podName);
1700
1775
  if (successInstance) {
1701
1776
  options.on?.init ? await options.on.init() : null;
1702
1777
  shellExec(`kubectl logs -f ${podName} -n ${namespace}`);
@@ -1705,6 +1780,39 @@ EOF`;
1705
1780
  };
1706
1781
 
1707
1782
  static API = {
1783
+ /**
1784
+ * @method DEFAULT_OPTION
1785
+ * @description The default options for Underpost runners, including development mode, namespace, replicas, and underpost root path.
1786
+ * @memberof UnderpostRun
1787
+ * @static
1788
+ * @returns {Object} The default options object.
1789
+ */
1790
+ get DEFAULT_OPTION() {
1791
+ return DEFAULT_OPTION;
1792
+ },
1793
+ /**
1794
+ * @method RUNNERS
1795
+ * @description Retrieves the list of available runner IDs from the UnderpostRun class.
1796
+ * @memberof UnderpostRun
1797
+ * @returns {string[]} An array of runner IDs.
1798
+ */
1799
+ get RUNNERS() {
1800
+ return Object.keys(UnderpostRun.RUNNERS);
1801
+ },
1802
+
1803
+ /**
1804
+ * @method CALL
1805
+ * @description Executes a specified runner function from the UnderpostRun class with the provided path and options.
1806
+ * @param {string} runner - The name of the runner to execute.
1807
+ * @param {string} path - The input value, identifier, or path for the operation.
1808
+ * @param {Object} options - The default underpost runner options for customizing workflow
1809
+ * @memberof UnderpostRun
1810
+ * @returns {Promise<any>} The result of the runner execution.
1811
+ */
1812
+ async CALL(runner = '', path = '', options = DEFAULT_OPTION) {
1813
+ return await UnderpostRun.RUNNERS[runner](path, options);
1814
+ },
1815
+
1708
1816
  /**
1709
1817
  * @method callback
1710
1818
  * @description Initiates the execution of a specified CLI command (runner) with the given input value (`path`) and processed options.
@@ -1714,7 +1822,7 @@ EOF`;
1714
1822
  * @memberof UnderpostRun
1715
1823
  * @returns {Promise<any>} The result of the callback execution.
1716
1824
  */
1717
- async callback(runner, path, options = UnderpostRun.DEFAULT_OPTION) {
1825
+ async callback(runner, path, options = DEFAULT_OPTION) {
1718
1826
  try {
1719
1827
  const npmRoot = getNpmRootPath();
1720
1828
  const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
@@ -1726,8 +1834,8 @@ EOF`;
1726
1834
  options.replicas = 1;
1727
1835
  options.npmRoot = npmRoot;
1728
1836
  logger.info('callback', { path, options });
1729
- if (!(runner in UnderpostRun.RUNNERS)) throw new Error(`Runner not found: ${runner}`);
1730
- const result = await UnderpostRun.RUNNERS[runner](path, options);
1837
+ if (!Underpost.run.RUNNERS.includes(runner)) throw new Error(`Runner not found: ${runner}`);
1838
+ const result = await Underpost.run.CALL(runner, path, options);
1731
1839
  return result;
1732
1840
  } catch (error) {
1733
1841
  console.log(error);