@nocobase/cli 2.1.0-alpha.4 → 2.1.0-alpha.40
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/LICENSE.txt +107 -0
- package/README.md +393 -19
- package/README.zh-CN.md +343 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +135 -0
- package/bin/session-env.js +39 -0
- package/dist/commands/api/resource/create.js +15 -0
- package/dist/commands/api/resource/destroy.js +15 -0
- package/dist/commands/api/resource/get.js +15 -0
- package/dist/commands/api/resource/index.js +20 -0
- package/dist/commands/api/resource/list.js +16 -0
- package/dist/commands/api/resource/query.js +15 -0
- package/dist/commands/api/resource/update.js +15 -0
- package/dist/commands/app/down.js +301 -0
- package/dist/commands/app/logs.js +114 -0
- package/dist/commands/app/restart.js +158 -0
- package/dist/commands/app/start.js +305 -0
- package/dist/commands/app/stop.js +115 -0
- package/dist/commands/app/upgrade.js +636 -0
- package/dist/commands/backup/create.js +147 -0
- package/dist/commands/backup/index.js +20 -0
- package/dist/commands/backup/restore.js +105 -0
- package/{src/cli.js → dist/commands/build.js} +4 -11
- package/dist/commands/config/delete.js +30 -0
- package/dist/commands/config/get.js +29 -0
- package/dist/commands/config/index.js +20 -0
- package/dist/commands/config/list.js +29 -0
- package/dist/commands/config/set.js +35 -0
- package/dist/commands/db/check.js +240 -0
- package/dist/commands/db/logs.js +85 -0
- package/dist/commands/db/ps.js +60 -0
- package/dist/commands/db/shared.js +96 -0
- package/dist/commands/db/start.js +71 -0
- package/dist/commands/db/stop.js +71 -0
- package/{templates/plugin/src/client/models/index.ts → dist/commands/dev.js} +4 -4
- package/{src/commands/locale/react-js-cron/index.js → dist/commands/down.js} +3 -8
- package/dist/commands/download.js +13 -0
- package/dist/commands/env/add.js +366 -0
- package/dist/commands/env/auth.js +130 -0
- package/dist/commands/env/current.js +21 -0
- package/dist/commands/env/info.js +157 -0
- package/dist/commands/env/list.js +44 -0
- package/dist/commands/env/remove.js +84 -0
- package/dist/commands/env/shared.js +158 -0
- package/dist/commands/env/status.js +90 -0
- package/dist/commands/env/update.js +74 -0
- package/dist/commands/env/use.js +38 -0
- package/dist/commands/examples/prompts-stages.js +150 -0
- package/dist/commands/examples/prompts-test.js +181 -0
- package/dist/commands/init.js +1092 -0
- package/dist/commands/install.js +2378 -0
- package/dist/commands/license/activate.js +360 -0
- package/dist/commands/license/env.js +94 -0
- package/dist/commands/license/generate-id.js +108 -0
- package/dist/commands/license/id.js +70 -0
- package/dist/commands/license/index.js +20 -0
- package/dist/commands/license/plugins/clean.js +115 -0
- package/dist/commands/license/plugins/index.js +20 -0
- package/dist/commands/license/plugins/list.js +64 -0
- package/dist/commands/license/plugins/shared.js +325 -0
- package/dist/commands/license/plugins/sync.js +285 -0
- package/dist/commands/license/shared.js +423 -0
- package/dist/commands/license/status.js +64 -0
- package/dist/commands/logs.js +12 -0
- package/dist/commands/plugin/disable.js +86 -0
- package/dist/commands/plugin/enable.js +86 -0
- package/dist/commands/plugin/list.js +82 -0
- package/dist/commands/pm/disable.js +12 -0
- package/dist/commands/pm/enable.js +12 -0
- package/dist/commands/pm/list.js +12 -0
- package/dist/commands/restart.js +12 -0
- package/dist/commands/scaffold/migration.js +38 -0
- package/dist/commands/scaffold/plugin.js +37 -0
- package/dist/commands/self/check.js +71 -0
- package/dist/commands/self/index.js +20 -0
- package/dist/commands/self/update.js +95 -0
- package/dist/commands/session/id.js +24 -0
- package/dist/commands/session/remove.js +57 -0
- package/dist/commands/session/setup.js +62 -0
- package/dist/commands/skills/check.js +69 -0
- package/dist/commands/skills/index.js +20 -0
- package/dist/commands/skills/install.js +80 -0
- package/dist/commands/skills/remove.js +80 -0
- package/dist/commands/skills/update.js +87 -0
- package/dist/commands/source/build.js +58 -0
- package/dist/commands/source/dev.js +182 -0
- package/dist/commands/source/download.js +880 -0
- package/dist/commands/source/publish.js +109 -0
- package/dist/commands/source/registry/logs.js +70 -0
- package/dist/commands/source/registry/start.js +57 -0
- package/dist/commands/source/registry/status.js +33 -0
- package/dist/commands/source/registry/stop.js +48 -0
- package/dist/commands/source/test.js +477 -0
- package/dist/commands/start.js +12 -0
- package/dist/commands/stop.js +12 -0
- package/dist/commands/test.js +12 -0
- package/dist/commands/upgrade.js +12 -0
- package/dist/commands/v1.js +210 -0
- package/dist/generated/command-registry.js +133 -0
- package/dist/help/runtime-help.js +23 -0
- package/dist/lib/api-client.js +329 -0
- package/dist/lib/app-health.js +126 -0
- package/dist/lib/app-managed-resources.js +316 -0
- package/dist/lib/app-runtime.js +180 -0
- package/dist/lib/auth-store.js +368 -0
- package/dist/lib/backup.js +171 -0
- package/dist/lib/bootstrap.js +403 -0
- package/dist/lib/build-config.js +18 -0
- package/dist/lib/builtin-db.js +86 -0
- package/dist/lib/cli-config.js +176 -0
- package/dist/lib/cli-home.js +47 -0
- package/dist/lib/cli-locale.js +129 -0
- package/dist/lib/command-discovery.js +39 -0
- package/dist/lib/db-connection-check.js +158 -0
- package/dist/lib/docker-env-file.js +52 -0
- package/dist/lib/docker-image.js +37 -0
- package/dist/lib/env-auth.js +873 -0
- package/dist/lib/env-config.js +94 -0
- package/dist/lib/env-guard.js +62 -0
- package/dist/lib/generated-command.js +186 -0
- package/dist/lib/http-request.js +49 -0
- package/dist/lib/inquirer-theme.js +17 -0
- package/dist/lib/inquirer.js +244 -0
- package/dist/lib/naming.js +70 -0
- package/dist/lib/object-utils.js +76 -0
- package/dist/lib/openapi.js +62 -0
- package/dist/lib/plugin-storage.js +64 -0
- package/dist/lib/post-processors.js +23 -0
- package/dist/lib/prompt-catalog-core.js +185 -0
- package/dist/lib/prompt-catalog-terminal.js +375 -0
- package/{src/index.js → dist/lib/prompt-catalog.js} +2 -6
- package/dist/lib/prompt-validators.js +240 -0
- package/dist/lib/prompt-web-ui.js +2103 -0
- package/dist/lib/resource-command.js +357 -0
- package/dist/lib/resource-request.js +104 -0
- package/dist/lib/run-npm.js +275 -0
- package/dist/lib/runtime-env-vars.js +32 -0
- package/dist/lib/runtime-generator.js +498 -0
- package/dist/lib/runtime-store.js +56 -0
- package/dist/lib/self-manager.js +301 -0
- package/dist/lib/session-id.js +17 -0
- package/dist/lib/session-integration.js +703 -0
- package/dist/lib/session-store.js +118 -0
- package/dist/lib/skills-manager.js +360 -0
- package/dist/lib/source-publish.js +306 -0
- package/dist/lib/source-registry.js +188 -0
- package/dist/lib/startup-update.js +285 -0
- package/dist/lib/ui.js +155 -0
- package/dist/locale/en-US.json +344 -0
- package/dist/locale/zh-CN.json +344 -0
- package/dist/post-processors/data-modeling.js +84 -0
- package/dist/post-processors/data-source-manager.js +138 -0
- package/dist/post-processors/index.js +19 -0
- package/nocobase-ctl.config.json +388 -0
- package/package.json +100 -26
- package/LICENSE +0 -661
- package/bin/index.js +0 -39
- package/nocobase.conf.tpl +0 -95
- package/src/commands/benchmark.js +0 -73
- package/src/commands/build.js +0 -49
- package/src/commands/clean.js +0 -30
- package/src/commands/client.js +0 -166
- package/src/commands/create-nginx-conf.js +0 -37
- package/src/commands/create-plugin.js +0 -33
- package/src/commands/dev.js +0 -200
- package/src/commands/doc.js +0 -76
- package/src/commands/e2e.js +0 -265
- package/src/commands/global.js +0 -43
- package/src/commands/index.js +0 -45
- package/src/commands/instance-id.js +0 -47
- package/src/commands/locale/cronstrue.js +0 -122
- package/src/commands/locale/react-js-cron/en-US.json +0 -75
- package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
- package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
- package/src/commands/locale.js +0 -81
- package/src/commands/p-test.js +0 -88
- package/src/commands/perf.js +0 -63
- package/src/commands/pkg.js +0 -321
- package/src/commands/pm2.js +0 -37
- package/src/commands/postinstall.js +0 -88
- package/src/commands/start.js +0 -148
- package/src/commands/tar.js +0 -36
- package/src/commands/test-coverage.js +0 -55
- package/src/commands/test.js +0 -107
- package/src/commands/umi.js +0 -33
- package/src/commands/update-deps.js +0 -72
- package/src/commands/upgrade.js +0 -47
- package/src/commands/view-license-key.js +0 -44
- package/src/license.js +0 -76
- package/src/logger.js +0 -75
- package/src/plugin-generator.js +0 -80
- package/src/util.js +0 -517
- package/templates/bundle-status.html +0 -338
- package/templates/create-app-package.json +0 -39
- package/templates/plugin/.npmignore.tpl +0 -2
- package/templates/plugin/README.md.tpl +0 -1
- package/templates/plugin/client.d.ts +0 -2
- package/templates/plugin/client.js +0 -1
- package/templates/plugin/package.json.tpl +0 -11
- package/templates/plugin/server.d.ts +0 -2
- package/templates/plugin/server.js +0 -1
- package/templates/plugin/src/client/client.d.ts +0 -249
- package/templates/plugin/src/client/index.tsx.tpl +0 -1
- package/templates/plugin/src/client/locale.ts +0 -21
- package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
- package/templates/plugin/src/index.ts +0 -2
- package/templates/plugin/src/locale/en-US.json +0 -1
- package/templates/plugin/src/locale/zh-CN.json +0 -1
- package/templates/plugin/src/server/collections/.gitkeep +0 -0
- package/templates/plugin/src/server/index.ts.tpl +0 -1
- package/templates/plugin/src/server/plugin.ts.tpl +0 -19
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { confirm } from "./inquirer.js";
|
|
10
|
+
import { getCurrentEnvName, getEnv, setEnvRuntime, updateEnvConnection } from './auth-store.js';
|
|
11
|
+
import { resolveAccessToken } from './env-auth.js';
|
|
12
|
+
import { fetchWithPreservedAuthRedirect } from './http-request.js';
|
|
13
|
+
import { generateRuntime } from './runtime-generator.js';
|
|
14
|
+
import { hasRuntimeSync, saveRuntime } from './runtime-store.js';
|
|
15
|
+
import { printInfo, printVerbose, printWarningBlock, setVerboseMode, stopTask, updateTask } from './ui.js';
|
|
16
|
+
const APP_RETRY_INTERVAL = 2000;
|
|
17
|
+
const APP_RETRY_TIMEOUT = 120000;
|
|
18
|
+
function readFlag(argv, name) {
|
|
19
|
+
const exact = `--${name}`;
|
|
20
|
+
const prefix = `--${name}=`;
|
|
21
|
+
const alias = name === 'env' ? '-e' : name === 'scope' ? '-s' : undefined;
|
|
22
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
23
|
+
const value = argv[index];
|
|
24
|
+
if (value === exact) {
|
|
25
|
+
return argv[index + 1];
|
|
26
|
+
}
|
|
27
|
+
if (alias && value === alias) {
|
|
28
|
+
return argv[index + 1];
|
|
29
|
+
}
|
|
30
|
+
if (value.startsWith(prefix)) {
|
|
31
|
+
return value.slice(prefix.length);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
function hasBooleanFlag(argv, name) {
|
|
37
|
+
const exact = `--${name}`;
|
|
38
|
+
const negated = `--no-${name}`;
|
|
39
|
+
const prefix = `--${name}=`;
|
|
40
|
+
for (const value of argv) {
|
|
41
|
+
if (value === exact) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
if (value === negated) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (value.startsWith(prefix)) {
|
|
48
|
+
return value.slice(prefix.length) !== 'false';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
function getCommandToken(argv) {
|
|
54
|
+
const tokens = [];
|
|
55
|
+
for (const token of argv) {
|
|
56
|
+
if (!token || token.startsWith('-')) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
tokens.push(token);
|
|
60
|
+
}
|
|
61
|
+
if (tokens[0] === 'api') {
|
|
62
|
+
return tokens[1] ?? tokens[0];
|
|
63
|
+
}
|
|
64
|
+
return tokens[0];
|
|
65
|
+
}
|
|
66
|
+
function hasHelpFlag(argv) {
|
|
67
|
+
return argv.includes('--help') || argv.includes('-h');
|
|
68
|
+
}
|
|
69
|
+
function hasVersionFlag(argv) {
|
|
70
|
+
return argv.includes('--version') || argv.includes('-v');
|
|
71
|
+
}
|
|
72
|
+
function isBuiltinCommand(argv) {
|
|
73
|
+
const commandTokens = argv.filter((token) => token && !token.startsWith('-'));
|
|
74
|
+
const [topic, subtopic] = commandTokens;
|
|
75
|
+
return topic === 'env' || topic === 'resource' || (topic === 'api' && subtopic === 'resource');
|
|
76
|
+
}
|
|
77
|
+
export function shouldSkipRuntimeBootstrap(argv) {
|
|
78
|
+
return hasVersionFlag(argv) || isBuiltinCommand(argv);
|
|
79
|
+
}
|
|
80
|
+
function shouldIgnoreBootstrapFailure(argv, commandToken) {
|
|
81
|
+
return !commandToken || hasHelpFlag(argv) || (commandToken === 'api' && argv.length === 1);
|
|
82
|
+
}
|
|
83
|
+
async function requestJson(url, options) {
|
|
84
|
+
const headers = new Headers();
|
|
85
|
+
if (options.token) {
|
|
86
|
+
headers.set('authorization', `Bearer ${options.token}`);
|
|
87
|
+
}
|
|
88
|
+
if (options.role) {
|
|
89
|
+
headers.set('x-role', options.role);
|
|
90
|
+
}
|
|
91
|
+
let response;
|
|
92
|
+
try {
|
|
93
|
+
response = await fetchWithPreservedAuthRedirect(url, {
|
|
94
|
+
method: options.method ?? 'GET',
|
|
95
|
+
headers,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
return {
|
|
100
|
+
status: 0,
|
|
101
|
+
ok: false,
|
|
102
|
+
data: {
|
|
103
|
+
error: {
|
|
104
|
+
message: error?.message ?? 'fetch failed',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const text = await response.text();
|
|
110
|
+
let data = undefined;
|
|
111
|
+
if (text) {
|
|
112
|
+
try {
|
|
113
|
+
data = JSON.parse(text);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
data = text;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
status: response.status,
|
|
121
|
+
ok: response.ok,
|
|
122
|
+
data,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function sleep(ms) {
|
|
126
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
127
|
+
}
|
|
128
|
+
function isAppRestarting(response) {
|
|
129
|
+
return response.status === 503 && response.data?.error?.code === 'APP_COMMANDING';
|
|
130
|
+
}
|
|
131
|
+
function shouldRetryAppAvailability(response) {
|
|
132
|
+
return isAppRestarting(response) || response.status === 0;
|
|
133
|
+
}
|
|
134
|
+
function getSwaggerUrl(baseUrl) {
|
|
135
|
+
return `${baseUrl.replace(/\/+$/, '')}/swagger:get`;
|
|
136
|
+
}
|
|
137
|
+
function getHealthCheckUrl(baseUrl) {
|
|
138
|
+
return `${baseUrl.replace(/\/+$/, '')}/__health_check`;
|
|
139
|
+
}
|
|
140
|
+
async function waitForServiceReady(baseUrl, token, role, options) {
|
|
141
|
+
const healthCheckUrl = getHealthCheckUrl(baseUrl);
|
|
142
|
+
const startedAt = Date.now();
|
|
143
|
+
let notified = false;
|
|
144
|
+
while (Date.now() - startedAt < APP_RETRY_TIMEOUT) {
|
|
145
|
+
const response = await fetchWithPreservedAuthRedirect(healthCheckUrl, {
|
|
146
|
+
method: 'GET',
|
|
147
|
+
headers: token || role
|
|
148
|
+
? {
|
|
149
|
+
...(token ? { authorization: `Bearer ${token}` } : undefined),
|
|
150
|
+
...(role ? { 'x-role': role } : undefined),
|
|
151
|
+
}
|
|
152
|
+
: undefined,
|
|
153
|
+
}).catch((error) => {
|
|
154
|
+
return {
|
|
155
|
+
ok: false,
|
|
156
|
+
status: 0,
|
|
157
|
+
text: async () => error?.message ?? 'fetch failed',
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
const text = await response.text();
|
|
161
|
+
if (response.ok && text.trim().toLowerCase() === 'ok') {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (!notified) {
|
|
165
|
+
if (!options?.quiet) {
|
|
166
|
+
printVerbose(`Waiting for health check: ${healthCheckUrl}`);
|
|
167
|
+
updateTask(`Waiting for application readiness (${healthCheckUrl})`);
|
|
168
|
+
}
|
|
169
|
+
notified = true;
|
|
170
|
+
}
|
|
171
|
+
await sleep(APP_RETRY_INTERVAL);
|
|
172
|
+
}
|
|
173
|
+
throw new Error(`The application did not become ready in time. Expected \`${healthCheckUrl}\` to respond with \`ok\`.`);
|
|
174
|
+
}
|
|
175
|
+
async function waitForSwaggerSchema(baseUrl, token, role, options) {
|
|
176
|
+
const swaggerUrl = getSwaggerUrl(baseUrl);
|
|
177
|
+
const startedAt = Date.now();
|
|
178
|
+
if (!options?.quiet) {
|
|
179
|
+
printVerbose(`Checking swagger schema: ${swaggerUrl}`);
|
|
180
|
+
}
|
|
181
|
+
while (Date.now() - startedAt < APP_RETRY_TIMEOUT) {
|
|
182
|
+
const response = await requestJson(swaggerUrl, { token, role });
|
|
183
|
+
if (response.ok) {
|
|
184
|
+
return response;
|
|
185
|
+
}
|
|
186
|
+
if (!shouldRetryAppAvailability(response)) {
|
|
187
|
+
return response;
|
|
188
|
+
}
|
|
189
|
+
await waitForServiceReady(baseUrl, token, role, options);
|
|
190
|
+
}
|
|
191
|
+
return await requestJson(swaggerUrl, { token, role });
|
|
192
|
+
}
|
|
193
|
+
async function confirmEnableApiDoc() {
|
|
194
|
+
try {
|
|
195
|
+
return await confirm({
|
|
196
|
+
message: 'Enable the API documentation plugin now?',
|
|
197
|
+
default: false,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async function fetchSwaggerSchema(baseUrl, token, role, context = {}, options = {}) {
|
|
205
|
+
let response = options.retryAppAvailability === false
|
|
206
|
+
? await requestJson(getSwaggerUrl(baseUrl), { token, role })
|
|
207
|
+
: await waitForSwaggerSchema(baseUrl, token, role, { quiet: options.quiet });
|
|
208
|
+
if (response.status === 404) {
|
|
209
|
+
if (options.allowEnableApiDoc === false || options.quiet) {
|
|
210
|
+
throw new Error('`swagger:get` returned 404. Check the base URL and enable the `API documentation plugin` if needed.');
|
|
211
|
+
}
|
|
212
|
+
printInfo('The API documentation plugin is not enabled.');
|
|
213
|
+
const shouldEnable = await confirmEnableApiDoc();
|
|
214
|
+
if (!shouldEnable) {
|
|
215
|
+
throw new Error('`swagger:get` returned 404. Enable the `API documentation plugin` first.');
|
|
216
|
+
}
|
|
217
|
+
const enableUrl = `${baseUrl.replace(/\/+$/, '')}/pm:enable?filterByTk=api-doc`;
|
|
218
|
+
printVerbose(`Enabling API documentation plugin via ${enableUrl}`);
|
|
219
|
+
const enableResponse = await requestJson(enableUrl, { method: 'POST', token, role });
|
|
220
|
+
if (!enableResponse.ok) {
|
|
221
|
+
throw new Error(`Failed to enable the \`API documentation plugin\` via \`pm:enable\`.\n${JSON.stringify(enableResponse.data, null, 2)}`);
|
|
222
|
+
}
|
|
223
|
+
updateTask('Enabled the API documentation plugin. Waiting for application readiness...');
|
|
224
|
+
await waitForServiceReady(baseUrl, token, role);
|
|
225
|
+
response = await waitForSwaggerSchema(baseUrl, token, role);
|
|
226
|
+
}
|
|
227
|
+
if (!response.ok) {
|
|
228
|
+
throw new Error(formatSwaggerSchemaError(response, { baseUrl, token, ...context }));
|
|
229
|
+
}
|
|
230
|
+
return (response.data?.data ?? response.data);
|
|
231
|
+
}
|
|
232
|
+
function collectErrorEntries(data) {
|
|
233
|
+
if (Array.isArray(data?.errors)) {
|
|
234
|
+
return data.errors.filter(Boolean);
|
|
235
|
+
}
|
|
236
|
+
if (data?.error) {
|
|
237
|
+
return [data.error];
|
|
238
|
+
}
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
function hasAuthenticationError(data) {
|
|
242
|
+
return collectErrorEntries(data).some((entry) => entry?.code === 'INVALID_TOKEN' || entry?.code === 'EMPTY_TOKEN');
|
|
243
|
+
}
|
|
244
|
+
function isNetworkFetchFailure(response) {
|
|
245
|
+
return response.status === 0;
|
|
246
|
+
}
|
|
247
|
+
export function formatSwaggerSchemaError(response, context) {
|
|
248
|
+
if (hasAuthenticationError(response.data)) {
|
|
249
|
+
const entries = collectErrorEntries(response.data);
|
|
250
|
+
const details = entries
|
|
251
|
+
.map((entry) => {
|
|
252
|
+
const code = entry?.code ? `[${entry.code}] ` : '';
|
|
253
|
+
return `${code}${entry?.message ?? 'Authentication failed.'}`;
|
|
254
|
+
})
|
|
255
|
+
.join('\n');
|
|
256
|
+
const envLabel = context.envName ? ` for env "${context.envName}"` : '';
|
|
257
|
+
const commandHint = context.commandToken
|
|
258
|
+
? `If \`${context.commandToken}\` is a runtime command, refresh the runtime after updating the token with \`nb env update\`. If it is a typo, run \`nb --help\` to inspect available commands.`
|
|
259
|
+
: 'Run `nb --help` to inspect built-in commands, then refresh runtime commands with `nb env update` after updating the token.';
|
|
260
|
+
return [
|
|
261
|
+
`Authentication failed while loading the command runtime from \`swagger:get\`${envLabel}.`,
|
|
262
|
+
`Base URL: ${context.baseUrl}`,
|
|
263
|
+
details,
|
|
264
|
+
'Update the API key with `nb env add <name> --api-base-url <url> --auth-type token --access-token <api-key>`, log in with `nb env auth <name>`, or rerun the command with `--access-token <api-key>`.',
|
|
265
|
+
commandHint,
|
|
266
|
+
].join('\n');
|
|
267
|
+
}
|
|
268
|
+
if (isNetworkFetchFailure(response)) {
|
|
269
|
+
const rawMessage = response.data?.error?.message || 'fetch failed';
|
|
270
|
+
return [
|
|
271
|
+
'Failed to reach the NocoBase server while loading the command runtime from `swagger:get`.',
|
|
272
|
+
`Base URL: ${context.baseUrl}`,
|
|
273
|
+
`Network error: ${rawMessage}`,
|
|
274
|
+
'Check that the NocoBase app is running, the base URL is correct, and the server is reachable from this machine.',
|
|
275
|
+
'If you recently changed the server address, update it with `nb env add <name> --api-base-url <url>` and retry `nb env update`.',
|
|
276
|
+
'Use `nb env list` to inspect the current env configuration.',
|
|
277
|
+
].join('\n');
|
|
278
|
+
}
|
|
279
|
+
return `Failed to load swagger schema from \`swagger:get\`.\n${JSON.stringify(response.data, null, 2)}`;
|
|
280
|
+
}
|
|
281
|
+
export function formatMissingRuntimeEnvError(commandToken) {
|
|
282
|
+
if (!commandToken) {
|
|
283
|
+
return [
|
|
284
|
+
'No env is configured for runtime commands.',
|
|
285
|
+
'Run `nb env add <name> --api-base-url <url>` first.',
|
|
286
|
+
'If you configure multiple environments later, switch with `nb env use <name>`.',
|
|
287
|
+
].join('\n');
|
|
288
|
+
}
|
|
289
|
+
return [
|
|
290
|
+
`Unable to resolve runtime command \`${commandToken}\`.`,
|
|
291
|
+
'No env is configured, so the CLI cannot load runtime commands from `swagger:get`.',
|
|
292
|
+
'If this is a built-in command or a typo, run `nb --help` to inspect available commands.',
|
|
293
|
+
'If this should be an application runtime command, run `nb env add <name> --api-base-url <url>` and then `nb env update`.',
|
|
294
|
+
].join('\n');
|
|
295
|
+
}
|
|
296
|
+
export async function ensureRuntimeFromArgv(argv, options) {
|
|
297
|
+
const commandToken = getCommandToken(argv);
|
|
298
|
+
const isRootInvocation = !commandToken;
|
|
299
|
+
const canContinueWithoutRuntime = shouldIgnoreBootstrapFailure(argv, commandToken);
|
|
300
|
+
setVerboseMode(hasBooleanFlag(argv, 'verbose'));
|
|
301
|
+
if (shouldSkipRuntimeBootstrap(argv)) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
try {
|
|
305
|
+
const envName = readFlag(argv, 'env') ?? (await getCurrentEnvName());
|
|
306
|
+
const env = await getEnv(envName);
|
|
307
|
+
const baseUrl = readFlag(argv, 'api-base-url') ?? env?.baseUrl;
|
|
308
|
+
const role = readFlag(argv, 'role');
|
|
309
|
+
const token = await resolveAccessToken({
|
|
310
|
+
envName,
|
|
311
|
+
baseUrl,
|
|
312
|
+
token: readFlag(argv, 'token'),
|
|
313
|
+
});
|
|
314
|
+
const runtimeVersion = env?.runtime?.version;
|
|
315
|
+
if (runtimeVersion && hasRuntimeSync(runtimeVersion)) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (!baseUrl) {
|
|
319
|
+
if (isRootInvocation) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
throw new Error(formatMissingRuntimeEnvError(commandToken));
|
|
323
|
+
}
|
|
324
|
+
updateTask('Loading command runtime...');
|
|
325
|
+
try {
|
|
326
|
+
printVerbose(`Runtime source: ${baseUrl}`);
|
|
327
|
+
const document = await fetchSwaggerSchema(baseUrl, token, role, { envName, commandToken }, isRootInvocation
|
|
328
|
+
? {
|
|
329
|
+
allowEnableApiDoc: false,
|
|
330
|
+
retryAppAvailability: false,
|
|
331
|
+
}
|
|
332
|
+
: undefined);
|
|
333
|
+
const runtime = await generateRuntime(document, options.configFile, baseUrl);
|
|
334
|
+
await saveRuntime(runtime);
|
|
335
|
+
await setEnvRuntime(envName, {
|
|
336
|
+
version: runtime.version,
|
|
337
|
+
schemaHash: runtime.schemaHash,
|
|
338
|
+
generatedAt: runtime.generatedAt,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
finally {
|
|
342
|
+
stopTask();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
if (!canContinueWithoutRuntime) {
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
349
|
+
stopTask();
|
|
350
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
351
|
+
printWarningBlock(`Unable to load runtime commands. Showing built-in help instead.\n\n${message}`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
export async function updateEnvRuntime(options) {
|
|
355
|
+
setVerboseMode(Boolean(options.verbose));
|
|
356
|
+
const envName = options.envName ?? (await getCurrentEnvName({ scope: options.scope }));
|
|
357
|
+
const env = await getEnv(envName, { scope: options.scope });
|
|
358
|
+
const baseUrl = options.baseUrl ?? env?.baseUrl;
|
|
359
|
+
const token = await resolveAccessToken({
|
|
360
|
+
envName,
|
|
361
|
+
baseUrl,
|
|
362
|
+
token: options.token,
|
|
363
|
+
scope: options.scope,
|
|
364
|
+
});
|
|
365
|
+
if (!baseUrl) {
|
|
366
|
+
throw new Error([
|
|
367
|
+
env
|
|
368
|
+
? `Env "${envName}" is missing a base URL.`
|
|
369
|
+
: `Env "${envName}" is not configured. Run \`nb env add ${envName}\` first.`,
|
|
370
|
+
env ? 'Update it with `nb env add <name> --api-base-url <url>` first.' : '',
|
|
371
|
+
]
|
|
372
|
+
.filter(Boolean)
|
|
373
|
+
.join('\n'));
|
|
374
|
+
}
|
|
375
|
+
if (!options.quiet) {
|
|
376
|
+
updateTask('Loading command runtime...');
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
if (!options.quiet) {
|
|
380
|
+
printVerbose(`Runtime source: ${baseUrl}`);
|
|
381
|
+
}
|
|
382
|
+
const document = await fetchSwaggerSchema(baseUrl, token, options.role, { envName }, { quiet: options.quiet });
|
|
383
|
+
const runtime = await generateRuntime(document, options.configFile, baseUrl);
|
|
384
|
+
await saveRuntime(runtime, { scope: options.scope });
|
|
385
|
+
if (options.baseUrl !== undefined || options.token !== undefined) {
|
|
386
|
+
await updateEnvConnection(envName, {
|
|
387
|
+
apiBaseUrl: options.baseUrl,
|
|
388
|
+
accessToken: options.token,
|
|
389
|
+
}, { scope: options.scope });
|
|
390
|
+
}
|
|
391
|
+
await setEnvRuntime(envName, {
|
|
392
|
+
version: runtime.version,
|
|
393
|
+
schemaHash: runtime.schemaHash,
|
|
394
|
+
generatedAt: runtime.generatedAt,
|
|
395
|
+
}, { scope: options.scope });
|
|
396
|
+
return runtime;
|
|
397
|
+
}
|
|
398
|
+
finally {
|
|
399
|
+
if (!options.quiet) {
|
|
400
|
+
stopTask();
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { promises as fs } from 'node:fs';
|
|
10
|
+
export async function loadBuildConfig(filePath) {
|
|
11
|
+
try {
|
|
12
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
13
|
+
return JSON.parse(content);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { buildDockerDbContainerName } from './app-runtime.js';
|
|
10
|
+
import { commandOutput } from './run-npm.js';
|
|
11
|
+
function trimValue(value) {
|
|
12
|
+
return String(value ?? '').trim();
|
|
13
|
+
}
|
|
14
|
+
export function defaultBuiltinDbPortForDialect(value) {
|
|
15
|
+
const dialect = trimValue(value) || 'postgres';
|
|
16
|
+
switch (dialect) {
|
|
17
|
+
case 'mysql':
|
|
18
|
+
case 'mariadb':
|
|
19
|
+
return '3306';
|
|
20
|
+
case 'kingbase':
|
|
21
|
+
return '54321';
|
|
22
|
+
case 'postgres':
|
|
23
|
+
default:
|
|
24
|
+
return '5432';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function resolveBuiltinDbContainerName(runtime, dbDialect) {
|
|
28
|
+
const dialect = trimValue(dbDialect ?? runtime.env.config.dbDialect) || 'postgres';
|
|
29
|
+
return buildDockerDbContainerName(runtime.envName, dialect, runtime.dockerContainerPrefix || runtime.workspaceName);
|
|
30
|
+
}
|
|
31
|
+
export function deriveBuiltinDbConnection(runtime, overrides = {}) {
|
|
32
|
+
const dbDialect = trimValue(overrides.dbDialect ?? runtime.env.config.dbDialect) || 'postgres';
|
|
33
|
+
const containerName = resolveBuiltinDbContainerName(runtime, dbDialect);
|
|
34
|
+
const networkName = trimValue(runtime.dockerNetworkName || runtime.workspaceName) || undefined;
|
|
35
|
+
if (runtime.source === 'docker') {
|
|
36
|
+
return {
|
|
37
|
+
builtinDb: true,
|
|
38
|
+
dbDialect,
|
|
39
|
+
dbHost: containerName,
|
|
40
|
+
dbPort: defaultBuiltinDbPortForDialect(dbDialect),
|
|
41
|
+
containerName,
|
|
42
|
+
networkName,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const dbPort = trimValue(overrides.dbPort ?? runtime.env.config.dbPort) || defaultBuiltinDbPortForDialect(dbDialect);
|
|
46
|
+
return {
|
|
47
|
+
builtinDb: true,
|
|
48
|
+
dbDialect,
|
|
49
|
+
dbHost: '127.0.0.1',
|
|
50
|
+
dbPort,
|
|
51
|
+
containerName,
|
|
52
|
+
networkName,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export async function resolveBuiltinDbConnection(runtime) {
|
|
56
|
+
const derived = deriveBuiltinDbConnection(runtime);
|
|
57
|
+
if (runtime.source === 'docker') {
|
|
58
|
+
return derived;
|
|
59
|
+
}
|
|
60
|
+
const mappedPort = await inspectBuiltinDbPublishedPort(derived.containerName, derived.dbDialect);
|
|
61
|
+
if (mappedPort) {
|
|
62
|
+
return {
|
|
63
|
+
...derived,
|
|
64
|
+
dbPort: mappedPort,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return derived;
|
|
68
|
+
}
|
|
69
|
+
async function inspectBuiltinDbPublishedPort(containerName, dbDialect) {
|
|
70
|
+
const containerPort = defaultBuiltinDbPortForDialect(dbDialect);
|
|
71
|
+
try {
|
|
72
|
+
const output = await commandOutput('docker', [
|
|
73
|
+
'inspect',
|
|
74
|
+
'--format',
|
|
75
|
+
`{{with index .NetworkSettings.Ports "${containerPort}/tcp"}}{{(index . 0).HostPort}}{{end}}`,
|
|
76
|
+
containerName,
|
|
77
|
+
], {
|
|
78
|
+
errorName: 'docker inspect',
|
|
79
|
+
});
|
|
80
|
+
const hostPort = trimValue(output);
|
|
81
|
+
return hostPort || undefined;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { loadExactAuthConfig, saveAuthConfig } from './auth-store.js';
|
|
10
|
+
import { resolveDefaultConfigScope } from './cli-home.js';
|
|
11
|
+
export const DEFAULT_LICENSE_PKG_URL = 'https://pkg.nocobase.com/';
|
|
12
|
+
export const DEFAULT_DOCKER_NETWORK = 'nocobase';
|
|
13
|
+
export const DEFAULT_DOCKER_CONTAINER_PREFIX = 'nb';
|
|
14
|
+
export const SUPPORTED_CLI_CONFIG_KEYS = [
|
|
15
|
+
'license.pkg-url',
|
|
16
|
+
'docker.network',
|
|
17
|
+
'docker.container-prefix',
|
|
18
|
+
];
|
|
19
|
+
function trimValue(value) {
|
|
20
|
+
const text = String(value ?? '').trim();
|
|
21
|
+
return text || undefined;
|
|
22
|
+
}
|
|
23
|
+
function resolveScope(options = {}) {
|
|
24
|
+
return {
|
|
25
|
+
scope: options.scope ?? resolveDefaultConfigScope(),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function isSupportedCliConfigKey(value) {
|
|
29
|
+
return SUPPORTED_CLI_CONFIG_KEYS.includes(value);
|
|
30
|
+
}
|
|
31
|
+
export function assertSupportedCliConfigKey(value) {
|
|
32
|
+
if (!isSupportedCliConfigKey(value)) {
|
|
33
|
+
throw new Error(`Unsupported config key "${value}". Supported keys: ${SUPPORTED_CLI_CONFIG_KEYS.join(', ')}`);
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
function cloneSettings(config) {
|
|
38
|
+
return {
|
|
39
|
+
license: config.settings?.license ? { ...config.settings.license } : undefined,
|
|
40
|
+
docker: config.settings?.docker ? { ...config.settings.docker } : undefined,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function pruneSettings(config) {
|
|
44
|
+
const license = config.settings?.license;
|
|
45
|
+
if (license && !trimValue(license.pkgUrl)) {
|
|
46
|
+
delete config.settings?.license;
|
|
47
|
+
}
|
|
48
|
+
const docker = config.settings?.docker;
|
|
49
|
+
if (docker && !trimValue(docker.network) && !trimValue(docker.containerPrefix)) {
|
|
50
|
+
delete config.settings?.docker;
|
|
51
|
+
}
|
|
52
|
+
if (config.settings
|
|
53
|
+
&& !config.settings.license
|
|
54
|
+
&& !config.settings.docker) {
|
|
55
|
+
delete config.settings;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function getExplicitCliConfigValue(config, key) {
|
|
59
|
+
switch (key) {
|
|
60
|
+
case 'license.pkg-url':
|
|
61
|
+
return trimValue(config.settings?.license?.pkgUrl);
|
|
62
|
+
case 'docker.network':
|
|
63
|
+
return trimValue(config.settings?.docker?.network);
|
|
64
|
+
case 'docker.container-prefix':
|
|
65
|
+
return trimValue(config.settings?.docker?.containerPrefix);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export function getEffectiveCliConfigValue(config, key) {
|
|
69
|
+
const explicit = getExplicitCliConfigValue(config, key);
|
|
70
|
+
if (explicit) {
|
|
71
|
+
return explicit;
|
|
72
|
+
}
|
|
73
|
+
switch (key) {
|
|
74
|
+
case 'license.pkg-url':
|
|
75
|
+
return DEFAULT_LICENSE_PKG_URL;
|
|
76
|
+
case 'docker.network':
|
|
77
|
+
return trimValue(config.name) || DEFAULT_DOCKER_NETWORK;
|
|
78
|
+
case 'docker.container-prefix':
|
|
79
|
+
return trimValue(config.name) || DEFAULT_DOCKER_CONTAINER_PREFIX;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export function normalizeCliConfigValue(key, value) {
|
|
83
|
+
const normalized = value.trim();
|
|
84
|
+
if (!normalized) {
|
|
85
|
+
throw new Error(`Config key "${key}" requires a non-empty value.`);
|
|
86
|
+
}
|
|
87
|
+
if (key === 'license.pkg-url') {
|
|
88
|
+
return normalized.replace(/\/+$/, '') + '/';
|
|
89
|
+
}
|
|
90
|
+
return normalized;
|
|
91
|
+
}
|
|
92
|
+
export async function loadCliConfig(options = {}) {
|
|
93
|
+
return await loadExactAuthConfig(resolveScope(options));
|
|
94
|
+
}
|
|
95
|
+
export async function getCliConfigValue(key, options = {}) {
|
|
96
|
+
const config = await loadCliConfig(options);
|
|
97
|
+
return getEffectiveCliConfigValue(config, key);
|
|
98
|
+
}
|
|
99
|
+
export async function listExplicitCliConfigValues(options = {}) {
|
|
100
|
+
const config = await loadCliConfig(options);
|
|
101
|
+
const out = {};
|
|
102
|
+
for (const key of SUPPORTED_CLI_CONFIG_KEYS) {
|
|
103
|
+
const value = getExplicitCliConfigValue(config, key);
|
|
104
|
+
if (value) {
|
|
105
|
+
out[key] = value;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
export async function setCliConfigValue(key, value, options = {}) {
|
|
111
|
+
const scope = resolveScope(options);
|
|
112
|
+
const config = await loadExactAuthConfig(scope);
|
|
113
|
+
const normalized = normalizeCliConfigValue(key, value);
|
|
114
|
+
config.settings = cloneSettings(config);
|
|
115
|
+
switch (key) {
|
|
116
|
+
case 'license.pkg-url':
|
|
117
|
+
config.settings.license = {
|
|
118
|
+
...(config.settings.license ?? {}),
|
|
119
|
+
pkgUrl: normalized,
|
|
120
|
+
};
|
|
121
|
+
break;
|
|
122
|
+
case 'docker.network':
|
|
123
|
+
config.settings.docker = {
|
|
124
|
+
...(config.settings.docker ?? {}),
|
|
125
|
+
network: normalized,
|
|
126
|
+
};
|
|
127
|
+
break;
|
|
128
|
+
case 'docker.container-prefix':
|
|
129
|
+
config.settings.docker = {
|
|
130
|
+
...(config.settings.docker ?? {}),
|
|
131
|
+
containerPrefix: normalized,
|
|
132
|
+
};
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
pruneSettings(config);
|
|
136
|
+
await saveAuthConfig(config, scope);
|
|
137
|
+
return normalized;
|
|
138
|
+
}
|
|
139
|
+
export async function deleteCliConfigValue(key, options = {}) {
|
|
140
|
+
const scope = resolveScope(options);
|
|
141
|
+
const config = await loadExactAuthConfig(scope);
|
|
142
|
+
const hadValue = Boolean(getExplicitCliConfigValue(config, key));
|
|
143
|
+
if (!hadValue) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
config.settings = cloneSettings(config);
|
|
147
|
+
switch (key) {
|
|
148
|
+
case 'license.pkg-url':
|
|
149
|
+
if (config.settings.license) {
|
|
150
|
+
delete config.settings.license.pkgUrl;
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
case 'docker.network':
|
|
154
|
+
if (config.settings.docker) {
|
|
155
|
+
delete config.settings.docker.network;
|
|
156
|
+
}
|
|
157
|
+
break;
|
|
158
|
+
case 'docker.container-prefix':
|
|
159
|
+
if (config.settings.docker) {
|
|
160
|
+
delete config.settings.docker.containerPrefix;
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
pruneSettings(config);
|
|
165
|
+
await saveAuthConfig(config, scope);
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
export async function resolveDockerNetworkName(options = {}) {
|
|
169
|
+
return await getCliConfigValue('docker.network', options);
|
|
170
|
+
}
|
|
171
|
+
export async function resolveDockerContainerPrefix(options = {}) {
|
|
172
|
+
return await getCliConfigValue('docker.container-prefix', options);
|
|
173
|
+
}
|
|
174
|
+
export async function resolveLicensePkgUrlFromConfig(options = {}) {
|
|
175
|
+
return await getCliConfigValue('license.pkg-url', options);
|
|
176
|
+
}
|