@underpostnet/underpost 2.99.1 → 2.99.5
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/.env.development +0 -3
- package/.env.production +1 -3
- package/.env.test +0 -3
- package/LICENSE +1 -1
- package/README.md +30 -30
- package/baremetal/commission-workflows.json +52 -0
- package/bin/deploy.js +101 -47
- package/cli.md +47 -43
- package/examples/static-page/README.md +55 -378
- package/examples/static-page/ssr-components/CustomPage.js +1 -13
- package/jsconfig.json +4 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/manifests/deployment/playwright/deployment.yaml +52 -0
- package/package.json +2 -2
- package/scripts/disk-devices.sh +13 -0
- package/scripts/rocky-pwa.sh +2 -2
- package/scripts/ssl.sh +12 -6
- package/src/api/user/user.model.js +1 -0
- package/src/cli/baremetal.js +576 -176
- package/src/cli/cloud-init.js +97 -79
- package/src/cli/deploy.js +6 -24
- package/src/cli/env.js +4 -1
- package/src/cli/image.js +7 -40
- package/src/cli/index.js +37 -7
- package/src/cli/repository.js +3 -1
- package/src/cli/run.js +109 -92
- package/src/cli/secrets.js +0 -34
- package/src/cli/static.js +0 -26
- package/src/cli/test.js +13 -1
- package/src/client/components/core/Polyhedron.js +896 -7
- package/src/client/components/core/Translate.js +4 -0
- package/src/client/services/default/default.management.js +12 -2
- package/src/index.js +27 -1
- package/src/runtime/express/Express.js +3 -3
- package/src/server/conf.js +6 -4
- package/src/server/logger.js +33 -31
- package/src/server/process.js +27 -2
- package/src/server/proxy.js +4 -6
- package/src/server/tls.js +30 -25
- package/examples/static-page/QUICK-REFERENCE.md +0 -481
- package/examples/static-page/STATIC-GENERATOR-GUIDE.md +0 -757
package/src/cli/run.js
CHANGED
|
@@ -80,6 +80,11 @@ const logger = loggerFactory(import.meta);
|
|
|
80
80
|
* @property {string} user - The user to run as.
|
|
81
81
|
* @property {string} pid - The process ID.
|
|
82
82
|
* @property {boolean} disablePrivateConfUpdate - Whether to disable private configuration updates.
|
|
83
|
+
* @property {string} monitorStatus - The monitor status option.
|
|
84
|
+
* @property {string} monitorStatusKindType - The monitor status kind type option.
|
|
85
|
+
* @property {string} monitorStatusDeltaMs - The monitor status delta in milliseconds.
|
|
86
|
+
* @property {string} monitorStatusMaxAttempts - The maximum number of attempts for monitor status.
|
|
87
|
+
* @property {boolean} logs - Whether to enable logs.
|
|
83
88
|
* @memberof UnderpostRun
|
|
84
89
|
*/
|
|
85
90
|
const DEFAULT_OPTION = {
|
|
@@ -134,6 +139,11 @@ const DEFAULT_OPTION = {
|
|
|
134
139
|
user: '',
|
|
135
140
|
pid: '',
|
|
136
141
|
disablePrivateConfUpdate: false,
|
|
142
|
+
monitorStatus: '',
|
|
143
|
+
monitorStatusKindType: '',
|
|
144
|
+
monitorStatusDeltaMs: '',
|
|
145
|
+
monitorStatusMaxAttempts: '',
|
|
146
|
+
logs: false,
|
|
137
147
|
};
|
|
138
148
|
|
|
139
149
|
/**
|
|
@@ -997,86 +1007,6 @@ EOF
|
|
|
997
1007
|
shellExec(`${underpostRoot}/scripts/ip-info.sh ${path}`);
|
|
998
1008
|
},
|
|
999
1009
|
|
|
1000
|
-
/**
|
|
1001
|
-
* @method monitor
|
|
1002
|
-
* @description Monitors a specific pod (identified by `path`) for the existence of a file (`/await`), and performs conditional actions (like file copying and opening Firefox) when the file is removed.
|
|
1003
|
-
* @param {string} path - The input value, identifier, or path for the operation (used as the name of the pod to monitor).
|
|
1004
|
-
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
1005
|
-
* @memberof UnderpostRun
|
|
1006
|
-
*/
|
|
1007
|
-
monitor: (path, options = DEFAULT_OPTION) => {
|
|
1008
|
-
const pid = getTerminalPid();
|
|
1009
|
-
logger.info('monitor pid', pid);
|
|
1010
|
-
const checkPath = '/await';
|
|
1011
|
-
const _monitor = async () => {
|
|
1012
|
-
const result = Underpost.deploy.existsContainerFile({ podName: path, path: checkPath });
|
|
1013
|
-
logger.info('monitor', result);
|
|
1014
|
-
if (result === true) {
|
|
1015
|
-
switch (path) {
|
|
1016
|
-
case 'tf-vae-test':
|
|
1017
|
-
{
|
|
1018
|
-
const nameSpace = options.namespace;
|
|
1019
|
-
const podName = path;
|
|
1020
|
-
const basePath = '/home/dd';
|
|
1021
|
-
const scriptPath = '/site/en/tutorials/generative/cvae.py';
|
|
1022
|
-
// shellExec(
|
|
1023
|
-
// `sudo kubectl cp ${nameSpace}/${podName}:${basePath}/docs${scriptPath} ${basePath}/lab/src/${scriptPath
|
|
1024
|
-
// .split('/')
|
|
1025
|
-
// .pop()}`,
|
|
1026
|
-
// );
|
|
1027
|
-
// const file = fs.readFileSync(`${basePath}/lab/src/${scriptPath.split('/').pop()}`, 'utf8');
|
|
1028
|
-
// fs.writeFileSync(
|
|
1029
|
-
// `${basePath}/lab/src/${scriptPath.split('/').pop()}`,
|
|
1030
|
-
// file.replace(
|
|
1031
|
-
// `import time`,
|
|
1032
|
-
// `import time
|
|
1033
|
-
// print('=== SCRIPT UPDATE TEST ===')`,
|
|
1034
|
-
// ),
|
|
1035
|
-
// 'utf8',
|
|
1036
|
-
// );
|
|
1037
|
-
shellExec(
|
|
1038
|
-
`sudo kubectl cp ${basePath}/lab/src/${scriptPath
|
|
1039
|
-
.split('/')
|
|
1040
|
-
.pop()} ${nameSpace}/${podName}:${basePath}/docs${scriptPath}`,
|
|
1041
|
-
);
|
|
1042
|
-
// shellExec(`sudo kubectl exec -i ${podName} -- sh -c "ipython ${basePath}/docs${scriptPath}"`);
|
|
1043
|
-
shellExec(`sudo kubectl exec -i ${podName} -- sh -c "rm -rf ${checkPath}"`);
|
|
1044
|
-
|
|
1045
|
-
{
|
|
1046
|
-
const checkPath = `/latent_space_plot.png`;
|
|
1047
|
-
const outsPaths = [];
|
|
1048
|
-
logger.info('monitor', checkPath);
|
|
1049
|
-
while (!Underpost.deploy.existsContainerFile({ podName, path: `/home/dd/docs${checkPath}` }))
|
|
1050
|
-
await timer(1000);
|
|
1051
|
-
|
|
1052
|
-
{
|
|
1053
|
-
const toPath = `${basePath}/lab${checkPath}`;
|
|
1054
|
-
outsPaths.push(toPath);
|
|
1055
|
-
shellExec(`sudo kubectl cp ${nameSpace}/${podName}:${basePath}/docs${checkPath} ${toPath}`);
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
for (let i of range(1, 10)) {
|
|
1059
|
-
i = `/image_at_epoch_${setPad(i, '0', 4)}.png`;
|
|
1060
|
-
const toPath = `${basePath}/lab/${i}`;
|
|
1061
|
-
outsPaths.push(toPath);
|
|
1062
|
-
shellExec(`sudo kubectl cp ${nameSpace}/${podName}:${basePath}/docs${i} ${toPath}`);
|
|
1063
|
-
}
|
|
1064
|
-
openTerminal(`firefox ${outsPaths.join(' ')}`, { single: true });
|
|
1065
|
-
}
|
|
1066
|
-
shellExec(`sudo kill -9 ${pid}`);
|
|
1067
|
-
}
|
|
1068
|
-
break;
|
|
1069
|
-
|
|
1070
|
-
default:
|
|
1071
|
-
break;
|
|
1072
|
-
}
|
|
1073
|
-
return;
|
|
1074
|
-
}
|
|
1075
|
-
await timer(1000);
|
|
1076
|
-
_monitor();
|
|
1077
|
-
};
|
|
1078
|
-
_monitor();
|
|
1079
|
-
},
|
|
1080
1010
|
/**
|
|
1081
1011
|
* @method db-client
|
|
1082
1012
|
* @description Deploys and exposes the Adminer database client application (using `adminer:4.7.6-standalone` image) on the cluster.
|
|
@@ -1134,15 +1064,20 @@ EOF
|
|
|
1134
1064
|
`git config user.email '${email}' && ` +
|
|
1135
1065
|
`git config credential.interactive always &&` +
|
|
1136
1066
|
`git config pull.rebase false`,
|
|
1067
|
+
{
|
|
1068
|
+
disableLog: true,
|
|
1069
|
+
silent: true,
|
|
1070
|
+
},
|
|
1137
1071
|
);
|
|
1138
1072
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1073
|
+
if (options.logs)
|
|
1074
|
+
console.log(
|
|
1075
|
+
shellExec(`git config list`, { silent: true, stdout: true })
|
|
1076
|
+
.replaceAll('user.email', 'user.email'.yellow)
|
|
1077
|
+
.replaceAll(username, username.green)
|
|
1078
|
+
.replaceAll('user.name', 'user.name'.yellow)
|
|
1079
|
+
.replaceAll(email, email.green),
|
|
1080
|
+
);
|
|
1146
1081
|
},
|
|
1147
1082
|
|
|
1148
1083
|
/**
|
|
@@ -1299,6 +1234,19 @@ EOF
|
|
|
1299
1234
|
shellExec(`./scripts/disk-clean.sh`);
|
|
1300
1235
|
},
|
|
1301
1236
|
|
|
1237
|
+
/**
|
|
1238
|
+
* @method disk-devices
|
|
1239
|
+
* @description Executes the `disk-devices.sh` script to display information about disk devices.
|
|
1240
|
+
* @param {string} path - The input value, identifier, or path for the operation.
|
|
1241
|
+
* @param {Object} options - The default underpost runner options for customizing workflow
|
|
1242
|
+
* @memberof UnderpostRun
|
|
1243
|
+
*/
|
|
1244
|
+
'disk-devices': async (path = '/', options = DEFAULT_OPTION) => {
|
|
1245
|
+
const { underpostRoot } = options;
|
|
1246
|
+
shellExec(`chmod +x ${underpostRoot}/scripts/disk-devices.sh`);
|
|
1247
|
+
shellExec(`${underpostRoot}/scripts/disk-devices.sh`);
|
|
1248
|
+
},
|
|
1249
|
+
|
|
1302
1250
|
/**
|
|
1303
1251
|
* @method disk-usage
|
|
1304
1252
|
* @description Displays disk usage statistics using the `du` command, sorted by size.
|
|
@@ -1370,7 +1318,11 @@ EOF
|
|
|
1370
1318
|
});
|
|
1371
1319
|
}
|
|
1372
1320
|
await awaitDeployMonitor(true);
|
|
1373
|
-
shellExec(
|
|
1321
|
+
shellExec(
|
|
1322
|
+
`./node_modules/.bin/env-cmd -f .env.development node src/proxy proxy ${deployId} ${subConf} ${host} ${_path}${
|
|
1323
|
+
options.tls ? ' tls' : ''
|
|
1324
|
+
}`,
|
|
1325
|
+
);
|
|
1374
1326
|
},
|
|
1375
1327
|
|
|
1376
1328
|
/**
|
|
@@ -1640,15 +1592,74 @@ EOF
|
|
|
1640
1592
|
* @memberof UnderpostRun
|
|
1641
1593
|
*/
|
|
1642
1594
|
'tf-vae-test': async (path, options = DEFAULT_OPTION) => {
|
|
1643
|
-
const { underpostRoot } = options;
|
|
1644
1595
|
const podName = 'tf-vae-test';
|
|
1645
1596
|
await Underpost.run.CALL('deploy-job', '', {
|
|
1597
|
+
logs: options.logs,
|
|
1646
1598
|
podName,
|
|
1647
1599
|
// volumeMountPath: '/custom_images',
|
|
1648
1600
|
// volumeHostPath: '/home/dd/engine/src/client/public/cyberia/assets/skin',
|
|
1649
1601
|
on: {
|
|
1650
1602
|
init: async () => {
|
|
1651
|
-
|
|
1603
|
+
// const pid = getTerminalPid();
|
|
1604
|
+
// shellExec(`sudo kill -9 ${pid}`);
|
|
1605
|
+
(async () => {
|
|
1606
|
+
const nameSpace = options.namespace;
|
|
1607
|
+
const basePath = '/home/dd';
|
|
1608
|
+
const scriptPath = '/site/en/tutorials/generative/cvae.py';
|
|
1609
|
+
|
|
1610
|
+
const { close } = await (async () => {
|
|
1611
|
+
const checkAwaitPath = '/await';
|
|
1612
|
+
while (!Underpost.deploy.existsContainerFile({ podName, path: checkAwaitPath })) {
|
|
1613
|
+
logger.info('monitor', checkAwaitPath);
|
|
1614
|
+
await timer(1000);
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
return {
|
|
1618
|
+
close: () => shellExec(`sudo kubectl exec -i ${podName} -- sh -c "rm -rf ${checkAwaitPath}"`),
|
|
1619
|
+
};
|
|
1620
|
+
})();
|
|
1621
|
+
|
|
1622
|
+
const localScriptPath = `${basePath}/lab/src/${scriptPath.split('/').pop()}`;
|
|
1623
|
+
if (!fs.existsSync(localScriptPath)) {
|
|
1624
|
+
throw new Error(`Local override script not found: ${localScriptPath}`);
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
shellExec(`sudo kubectl cp ${localScriptPath} ${nameSpace}/${podName}:${basePath}/docs${scriptPath}`);
|
|
1628
|
+
|
|
1629
|
+
close();
|
|
1630
|
+
|
|
1631
|
+
{
|
|
1632
|
+
const checkPath = `/latent_space_plot.png`;
|
|
1633
|
+
const outsPaths = [];
|
|
1634
|
+
const labDir = `${basePath}/lab`;
|
|
1635
|
+
|
|
1636
|
+
logger.info('monitor', checkPath);
|
|
1637
|
+
{
|
|
1638
|
+
const checkAwaitPath = `/home/dd/docs${checkPath}`;
|
|
1639
|
+
while (!Underpost.deploy.existsContainerFile({ podName, path: checkAwaitPath })) {
|
|
1640
|
+
logger.info('waiting for', checkAwaitPath);
|
|
1641
|
+
await timer(1000);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
{
|
|
1646
|
+
const toPath = `${labDir}${checkPath}`;
|
|
1647
|
+
outsPaths.push(toPath);
|
|
1648
|
+
shellExec(`sudo kubectl cp ${nameSpace}/${podName}:${basePath}/docs${checkPath} ${toPath}`);
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
for (let i of range(1, 10)) {
|
|
1652
|
+
const fileName = `image_at_epoch_${setPad(i, '0', 4)}.png`;
|
|
1653
|
+
const fromPath = `/${fileName}`;
|
|
1654
|
+
const toPath = `${labDir}/${fileName}`;
|
|
1655
|
+
outsPaths.push(toPath);
|
|
1656
|
+
shellExec(`sudo kubectl cp ${nameSpace}/${podName}:${basePath}/docs${fromPath} ${toPath}`);
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
openTerminal(`firefox ${outsPaths.join(' ')}`, { single: true });
|
|
1660
|
+
process.exit(0);
|
|
1661
|
+
}
|
|
1662
|
+
})();
|
|
1652
1663
|
},
|
|
1653
1664
|
},
|
|
1654
1665
|
args: [
|
|
@@ -1873,10 +1884,16 @@ EOF`;
|
|
|
1873
1884
|
shellExec(`kubectl delete pod ${podName} -n ${namespace} --ignore-not-found`);
|
|
1874
1885
|
console.log(cmd);
|
|
1875
1886
|
shellExec(cmd, { disableLog: true });
|
|
1876
|
-
const successInstance = await Underpost.test.statusMonitor(
|
|
1887
|
+
const successInstance = await Underpost.test.statusMonitor(
|
|
1888
|
+
podName,
|
|
1889
|
+
options.monitorStatus || 'Running',
|
|
1890
|
+
options.monitorStatusKindType || 'pods',
|
|
1891
|
+
options.monitorStatusDeltaMs || 1000,
|
|
1892
|
+
options.monitorStatusMaxAttempts || 600,
|
|
1893
|
+
);
|
|
1877
1894
|
if (successInstance) {
|
|
1878
1895
|
options.on?.init ? await options.on.init() : null;
|
|
1879
|
-
shellExec(`kubectl logs -f ${podName} -n ${namespace}
|
|
1896
|
+
if (options.logs) shellExec(`kubectl logs -f ${podName} -n ${namespace}`, { async: true });
|
|
1880
1897
|
}
|
|
1881
1898
|
},
|
|
1882
1899
|
};
|
package/src/cli/secrets.js
CHANGED
|
@@ -18,40 +18,6 @@ dotenv.config();
|
|
|
18
18
|
*/
|
|
19
19
|
class UnderpostSecret {
|
|
20
20
|
static API = {
|
|
21
|
-
/**
|
|
22
|
-
* @method docker
|
|
23
|
-
* @description Manages the secrets of the application.
|
|
24
|
-
* @memberof UnderpostSecret
|
|
25
|
-
*/
|
|
26
|
-
docker: {
|
|
27
|
-
/**
|
|
28
|
-
* @method init
|
|
29
|
-
* @description Initializes the docker secrets.
|
|
30
|
-
* @memberof UnderpostSecret
|
|
31
|
-
*/
|
|
32
|
-
init() {
|
|
33
|
-
shellExec(`docker swarm init`);
|
|
34
|
-
},
|
|
35
|
-
/**
|
|
36
|
-
* @method createFromEnvFile
|
|
37
|
-
* @description Creates a secret from an env file.
|
|
38
|
-
* @param {string} envPath - The path to the env file.
|
|
39
|
-
* @memberof UnderpostSecret
|
|
40
|
-
*/
|
|
41
|
-
createFromEnvFile(envPath) {
|
|
42
|
-
const envObj = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
|
|
43
|
-
for (const key of Object.keys(envObj)) {
|
|
44
|
-
Underpost.secret.docker.set(key, envObj[key]);
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
set(key, value) {
|
|
48
|
-
shellExec(`docker secret rm ${key}`);
|
|
49
|
-
shellExec(`echo "${value}" | docker secret create ${key} -`);
|
|
50
|
-
},
|
|
51
|
-
list() {
|
|
52
|
-
shellExec(`docker secret ls`);
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
21
|
/**
|
|
56
22
|
* @method underpost
|
|
57
23
|
* @description Manages the secrets of the application.
|
package/src/cli/static.js
CHANGED
|
@@ -68,11 +68,8 @@ const logger = loggerFactory(import.meta);
|
|
|
68
68
|
* @property {string} [page=''] - SSR component path to render
|
|
69
69
|
* @property {string} [title='Home'] - Page title (deprecated: use metadata.title)
|
|
70
70
|
* @property {string} [outputPath='.'] - Output file path
|
|
71
|
-
* @property {string} [deployId=''] - Deployment identifier
|
|
72
|
-
* @property {string} [buildHost=''] - Build host URL
|
|
73
71
|
* @property {string} [buildPath='/'] - Build path
|
|
74
72
|
* @property {string} [env='production'] - Environment (development/production)
|
|
75
|
-
* @property {boolean} [build=false] - Whether to trigger build
|
|
76
73
|
* @property {boolean} [dev=false] - Development mode flag
|
|
77
74
|
* @property {boolean} [minify=true] - Minify HTML output
|
|
78
75
|
* @property {MetadataOptions} [metadata={}] - Comprehensive metadata options
|
|
@@ -95,11 +92,8 @@ const DefaultStaticGenerationOptions = {
|
|
|
95
92
|
page: '',
|
|
96
93
|
title: '',
|
|
97
94
|
outputPath: '',
|
|
98
|
-
deployId: '',
|
|
99
|
-
buildHost: '',
|
|
100
95
|
buildPath: '/',
|
|
101
96
|
env: 'production',
|
|
102
|
-
build: false,
|
|
103
97
|
dev: false,
|
|
104
98
|
minify: true,
|
|
105
99
|
metadata: {},
|
|
@@ -385,11 +379,8 @@ class UnderpostStatic {
|
|
|
385
379
|
* @param {string} [options.page] - Path to the SSR component to render
|
|
386
380
|
* @param {string} [options.title] - Page title (deprecated: use metadata.title)
|
|
387
381
|
* @param {string} [options.outputPath] - Output file path
|
|
388
|
-
* @param {string} [options.deployId] - Deployment identifier
|
|
389
|
-
* @param {string} [options.buildHost] - Build host URL
|
|
390
382
|
* @param {string} [options.buildPath='/'] - Build path
|
|
391
383
|
* @param {string} [options.env='production'] - Environment (development/production)
|
|
392
|
-
* @param {boolean} [options.build=false] - Whether to trigger build
|
|
393
384
|
* @param {boolean} [options.minify=true] - Minify HTML output
|
|
394
385
|
* @param {MetadataOptions} [options.metadata={}] - Comprehensive metadata options
|
|
395
386
|
* @param {Object} [options.scripts={}] - Script injection options
|
|
@@ -601,23 +592,6 @@ class UnderpostStatic {
|
|
|
601
592
|
throw error;
|
|
602
593
|
}
|
|
603
594
|
}
|
|
604
|
-
|
|
605
|
-
// Trigger build if requested
|
|
606
|
-
if (options.deployId && options.build) {
|
|
607
|
-
try {
|
|
608
|
-
logger.info(`Triggering build for deployment: ${options.deployId}`);
|
|
609
|
-
|
|
610
|
-
shellExec(`underpost env ${options.deployId} ${options.env}`);
|
|
611
|
-
shellExec(
|
|
612
|
-
`npm run build ${options.deployId}${options.buildHost ? ` ${options.buildHost} ${options.buildPath}` : ``}`,
|
|
613
|
-
);
|
|
614
|
-
|
|
615
|
-
logger.info('Build completed successfully');
|
|
616
|
-
} catch (error) {
|
|
617
|
-
logger.error(`Build error: ${error.message}`);
|
|
618
|
-
throw error;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
595
|
},
|
|
622
596
|
|
|
623
597
|
/**
|
package/src/cli/test.js
CHANGED
|
@@ -59,7 +59,19 @@ class UnderpostTest {
|
|
|
59
59
|
* @param {number} options.maxAttempts - The maximum number of attempts.
|
|
60
60
|
* @memberof UnderpostTest
|
|
61
61
|
*/
|
|
62
|
-
async callback(
|
|
62
|
+
async callback(
|
|
63
|
+
deployList = '',
|
|
64
|
+
options = {
|
|
65
|
+
itc: false,
|
|
66
|
+
sh: false,
|
|
67
|
+
logs: false,
|
|
68
|
+
podName: '',
|
|
69
|
+
podStatus: '',
|
|
70
|
+
kindType: '',
|
|
71
|
+
deltaMs: 1000,
|
|
72
|
+
maxAttempts: 60 * 5,
|
|
73
|
+
},
|
|
74
|
+
) {
|
|
63
75
|
if (
|
|
64
76
|
options.podName &&
|
|
65
77
|
typeof options.podName === 'string' &&
|