simplemdg-dev-cli 2.0.4 → 2.4.4
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/README.md +63 -354
- package/USER_GUIDE.md +55 -378
- package/dist/commands/cds.command.js +69 -60
- package/dist/commands/cds.command.js.map +1 -1
- package/dist/commands/cf-db.command.d.ts +2 -0
- package/dist/commands/cf-db.command.js +606 -0
- package/dist/commands/cf-db.command.js.map +1 -0
- package/dist/commands/cf.command.js +291 -280
- package/dist/commands/cf.command.js.map +1 -1
- package/dist/commands/gitlab.command.d.ts +2 -0
- package/dist/commands/gitlab.command.js +351 -0
- package/dist/commands/gitlab.command.js.map +1 -0
- package/dist/commands/npmrc.command.js +50 -44
- package/dist/commands/npmrc.command.js.map +1 -1
- package/dist/core/cache.d.ts +1 -1
- package/dist/core/cache.js +58 -31
- package/dist/core/cache.js.map +1 -1
- package/dist/core/cds.js +32 -22
- package/dist/core/cds.js.map +1 -1
- package/dist/core/cf-env-parser.d.ts +1 -1
- package/dist/core/cf-env-parser.js +4 -1
- package/dist/core/cf-env-parser.js.map +1 -1
- package/dist/core/cf.d.ts +1 -1
- package/dist/core/cf.js +46 -31
- package/dist/core/cf.js.map +1 -1
- package/dist/core/db/db-btp.d.ts +48 -0
- package/dist/core/db/db-btp.js +162 -0
- package/dist/core/db/db-btp.js.map +1 -0
- package/dist/core/db/db-cache.d.ts +35 -0
- package/dist/core/db/db-cache.js +164 -0
- package/dist/core/db/db-cache.js.map +1 -0
- package/dist/core/db/db-connection.d.ts +22 -0
- package/dist/core/db/db-connection.js +73 -0
- package/dist/core/db/db-connection.js.map +1 -0
- package/dist/core/db/db-crypto.d.ts +3 -0
- package/dist/core/db/db-crypto.js +54 -0
- package/dist/core/db/db-crypto.js.map +1 -0
- package/dist/core/db/db-hana-adapter.d.ts +32 -0
- package/dist/core/db/db-hana-adapter.js +243 -0
- package/dist/core/db/db-hana-adapter.js.map +1 -0
- package/dist/core/db/db-metadata.d.ts +25 -0
- package/dist/core/db/db-metadata.js +150 -0
- package/dist/core/db/db-metadata.js.map +1 -0
- package/dist/core/db/db-postgres-adapter.d.ts +30 -0
- package/dist/core/db/db-postgres-adapter.js +245 -0
- package/dist/core/db/db-postgres-adapter.js.map +1 -0
- package/dist/core/db/db-query-files.d.ts +20 -0
- package/dist/core/db/db-query-files.js +106 -0
- package/dist/core/db/db-query-files.js.map +1 -0
- package/dist/core/db/db-query-history.d.ts +5 -0
- package/dist/core/db/db-query-history.js +49 -0
- package/dist/core/db/db-query-history.js.map +1 -0
- package/dist/core/db/db-row.d.ts +22 -0
- package/dist/core/db/db-row.js +70 -0
- package/dist/core/db/db-row.js.map +1 -0
- package/dist/core/db/db-studio-html.d.ts +4 -0
- package/dist/core/db/db-studio-html.js +437 -0
- package/dist/core/db/db-studio-html.js.map +1 -0
- package/dist/core/db/db-studio-server.d.ts +11 -0
- package/dist/core/db/db-studio-server.js +465 -0
- package/dist/core/db/db-studio-server.js.map +1 -0
- package/dist/core/db/db-types.d.ts +174 -0
- package/dist/core/db/db-types.js +3 -0
- package/dist/core/db/db-types.js.map +1 -0
- package/dist/core/db/db-vcap-parser.d.ts +7 -0
- package/dist/core/db/db-vcap-parser.js +137 -0
- package/dist/core/db/db-vcap-parser.js.map +1 -0
- package/dist/core/doctor.d.ts +1 -1
- package/dist/core/doctor.js +14 -8
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/guide.js +31 -26
- package/dist/core/guide.js.map +1 -1
- package/dist/core/install.d.ts +1 -1
- package/dist/core/install.js +17 -11
- package/dist/core/install.js.map +1 -1
- package/dist/core/navigator.d.ts +17 -0
- package/dist/core/navigator.js +140 -0
- package/dist/core/navigator.js.map +1 -0
- package/dist/core/npmrc.js +29 -16
- package/dist/core/npmrc.js.map +1 -1
- package/dist/core/process.js +11 -6
- package/dist/core/process.js.map +1 -1
- package/dist/core/prompts.js +16 -8
- package/dist/core/prompts.js.map +1 -1
- package/dist/core/repository.d.ts +1 -1
- package/dist/core/repository.js +16 -9
- package/dist/core/repository.js.map +1 -1
- package/dist/core/scanner.d.ts +1 -1
- package/dist/core/scanner.js +13 -7
- package/dist/core/scanner.js.map +1 -1
- package/dist/core/tooling.d.ts +28 -0
- package/dist/core/tooling.js +168 -0
- package/dist/core/tooling.js.map +1 -0
- package/dist/core/types.js +2 -1
- package/dist/core/version-conflict.d.ts +2 -2
- package/dist/core/version-conflict.js +11 -6
- package/dist/core/version-conflict.js.map +1 -1
- package/dist/index.js +65 -48
- package/dist/index.js.map +1 -1
- package/dist/types-local.js +2 -1
- package/package.json +12 -6
- package/src/commands/cds.command.ts +529 -0
- package/src/commands/cf-db.command.ts +636 -0
- package/src/commands/cf.command.ts +3345 -0
- package/src/commands/gitlab.command.ts +373 -0
- package/src/commands/npmrc.command.ts +581 -0
- package/src/core/cache.ts +332 -0
- package/src/core/cds.ts +278 -0
- package/src/core/cf-env-parser.ts +131 -0
- package/src/core/cf.ts +271 -0
- package/src/core/db/db-btp.ts +207 -0
- package/src/core/db/db-cache.ts +215 -0
- package/src/core/db/db-connection.ts +79 -0
- package/src/core/db/db-crypto.ts +53 -0
- package/src/core/db/db-hana-adapter.ts +294 -0
- package/src/core/db/db-metadata.ts +174 -0
- package/src/core/db/db-postgres-adapter.ts +275 -0
- package/src/core/db/db-query-files.ts +130 -0
- package/src/core/db/db-query-history.ts +53 -0
- package/src/core/db/db-row.ts +93 -0
- package/src/core/db/db-studio-html.ts +439 -0
- package/src/core/db/db-studio-server.ts +559 -0
- package/src/core/db/db-types.ts +195 -0
- package/src/core/db/db-vcap-parser.ts +182 -0
- package/src/core/doctor.ts +70 -0
- package/src/core/guide.ts +261 -0
- package/src/core/install.ts +91 -0
- package/src/core/navigator.ts +164 -0
- package/src/core/npmrc.ts +171 -0
- package/src/core/process.ts +75 -0
- package/src/core/prompts.ts +225 -0
- package/src/core/repository.ts +36 -0
- package/src/core/scanner.ts +41 -0
- package/src/core/tooling.ts +207 -0
- package/src/core/types.ts +152 -0
- package/src/core/version-conflict.ts +46 -0
- package/src/index.ts +460 -0
- package/src/types/external.d.ts +3 -0
- package/src/types-local.ts +11 -0
- package/tsconfig.json +17 -0
|
@@ -1,30 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerCloudFoundryCommands = registerCloudFoundryCommands;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const node_child_process_1 = require("node:child_process");
|
|
9
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
10
|
+
const node_net_1 = __importDefault(require("node:net"));
|
|
11
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
13
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
14
|
+
const cf_db_command_1 = require("./cf-db.command");
|
|
15
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
16
|
+
const cf_1 = require("../core/cf");
|
|
17
|
+
const cf_env_parser_1 = require("../core/cf-env-parser");
|
|
18
|
+
const cache_1 = require("../core/cache");
|
|
19
|
+
const process_1 = require("../core/process");
|
|
20
|
+
const repository_1 = require("../core/repository");
|
|
21
|
+
const prompts_2 = require("../core/prompts");
|
|
22
|
+
const tooling_1 = require("../core/tooling");
|
|
15
23
|
function validateRequired(value) {
|
|
16
24
|
return value.trim() ? true : "Value is required";
|
|
17
25
|
}
|
|
18
26
|
async function ensureCloudFoundrySessionFromCache() {
|
|
19
|
-
|
|
27
|
+
await (0, tooling_1.ensureExternalTool)("cf");
|
|
28
|
+
const target = await (0, cf_1.readCloudFoundryTarget)();
|
|
20
29
|
if (target.apiEndpoint && target.user) {
|
|
21
30
|
return target;
|
|
22
31
|
}
|
|
23
|
-
const cache = await readCache();
|
|
32
|
+
const cache = await (0, cache_1.readCache)();
|
|
24
33
|
const profilesWithPassword = cache.cloudFoundry.loginProfiles.filter((profile) => profile.password?.trim());
|
|
25
34
|
if (!profilesWithPassword.length) {
|
|
26
|
-
console.log(
|
|
27
|
-
console.log(
|
|
35
|
+
console.log(chalk_1.default.yellow("You are not logged in to Cloud Foundry yet and no cached password was found."));
|
|
36
|
+
console.log(chalk_1.default.gray("Run smdg cf login once and choose to save the password for automatic re-login."));
|
|
28
37
|
throw new Error("Cloud Foundry login is required");
|
|
29
38
|
}
|
|
30
39
|
const preferredProfiles = target.apiEndpoint
|
|
@@ -35,22 +44,22 @@ async function ensureCloudFoundrySessionFromCache() {
|
|
|
35
44
|
: profilesWithPassword;
|
|
36
45
|
const selectedProfileIndex = preferredProfiles.length === 1
|
|
37
46
|
? "0"
|
|
38
|
-
: await searchableSelectChoice({
|
|
47
|
+
: await (0, prompts_2.searchableSelectChoice)({
|
|
39
48
|
message: "Select cached CF login profile for automatic re-login",
|
|
40
49
|
choices: preferredProfiles.map((profile, index) => ({
|
|
41
|
-
title: `${profile.username} · ${profile.org}${profile.space ? `/${profile.space}` : ""} · ${inferCloudFoundryRegionFromApiEndpoint(profile.apiEndpoint)}`,
|
|
50
|
+
title: `${profile.username} · ${profile.org}${profile.space ? `/${profile.space}` : ""} · ${(0, cf_1.inferCloudFoundryRegionFromApiEndpoint)(profile.apiEndpoint)}`,
|
|
42
51
|
value: String(index),
|
|
43
52
|
})),
|
|
44
53
|
allowCustomValue: false,
|
|
45
54
|
});
|
|
46
55
|
const profile = preferredProfiles[Number(selectedProfileIndex)] ?? preferredProfiles[0];
|
|
47
|
-
console.log(
|
|
48
|
-
const apiExitCode = await setCloudFoundryApiEndpoint(profile.apiEndpoint);
|
|
56
|
+
console.log(chalk_1.default.gray(`Auto login CF: ${profile.username} · ${(0, cf_1.inferCloudFoundryRegionFromApiEndpoint)(profile.apiEndpoint)} · ${profile.org}${profile.space ? `/${profile.space}` : ""}`));
|
|
57
|
+
const apiExitCode = await (0, cf_1.setCloudFoundryApiEndpoint)(profile.apiEndpoint);
|
|
49
58
|
if (apiExitCode !== 0) {
|
|
50
59
|
process.exitCode = apiExitCode;
|
|
51
60
|
throw new Error("CF api target failed");
|
|
52
61
|
}
|
|
53
|
-
const authExitCode = await authenticateCloudFoundry({
|
|
62
|
+
const authExitCode = await (0, cf_1.authenticateCloudFoundry)({
|
|
54
63
|
username: profile.username,
|
|
55
64
|
password: profile.password,
|
|
56
65
|
});
|
|
@@ -58,23 +67,23 @@ async function ensureCloudFoundrySessionFromCache() {
|
|
|
58
67
|
process.exitCode = authExitCode;
|
|
59
68
|
throw new Error("CF automatic login failed. Run smdg cf login and update the cached password.");
|
|
60
69
|
}
|
|
61
|
-
const orgExitCode = await targetCloudFoundryOrg(profile.org);
|
|
70
|
+
const orgExitCode = await (0, cf_1.targetCloudFoundryOrg)(profile.org);
|
|
62
71
|
if (orgExitCode !== 0) {
|
|
63
72
|
process.exitCode = orgExitCode;
|
|
64
73
|
throw new Error("CF org target failed");
|
|
65
74
|
}
|
|
66
75
|
if (profile.space) {
|
|
67
|
-
const spaceExitCode = await targetCloudFoundrySpace(profile.space);
|
|
76
|
+
const spaceExitCode = await (0, cf_1.targetCloudFoundrySpace)(profile.space);
|
|
68
77
|
if (spaceExitCode !== 0) {
|
|
69
78
|
process.exitCode = spaceExitCode;
|
|
70
79
|
throw new Error("CF space target failed");
|
|
71
80
|
}
|
|
72
81
|
}
|
|
73
|
-
await rememberCloudFoundryLoginProfile({
|
|
82
|
+
await (0, cache_1.rememberCloudFoundryLoginProfile)({
|
|
74
83
|
...profile,
|
|
75
84
|
updatedAt: new Date().toISOString(),
|
|
76
85
|
});
|
|
77
|
-
return readCloudFoundryTarget();
|
|
86
|
+
return (0, cf_1.readCloudFoundryTarget)();
|
|
78
87
|
}
|
|
79
88
|
function sortCloudFoundryProfilesForEndpoint(options) {
|
|
80
89
|
const profilesWithPassword = options.profiles.filter((profile) => profile.password?.trim());
|
|
@@ -94,30 +103,30 @@ function sortCloudFoundryProfilesForEndpoint(options) {
|
|
|
94
103
|
});
|
|
95
104
|
}
|
|
96
105
|
async function ensureCloudFoundryAuthenticatedForApiEndpoint(options) {
|
|
97
|
-
const apiExitCode = await setCloudFoundryApiEndpoint(options.apiEndpoint);
|
|
106
|
+
const apiExitCode = await (0, cf_1.setCloudFoundryApiEndpoint)(options.apiEndpoint);
|
|
98
107
|
if (apiExitCode !== 0) {
|
|
99
108
|
process.exitCode = apiExitCode;
|
|
100
109
|
throw new Error(`Cannot set CF API endpoint: ${options.apiEndpoint}`);
|
|
101
110
|
}
|
|
102
|
-
const orgsCheck = await runCommand("cf", ["orgs"]);
|
|
111
|
+
const orgsCheck = await (0, process_1.runCommand)("cf", ["orgs"]);
|
|
103
112
|
if (orgsCheck.exitCode === 0) {
|
|
104
113
|
return undefined;
|
|
105
114
|
}
|
|
106
|
-
const cache = await readCache();
|
|
115
|
+
const cache = await (0, cache_1.readCache)();
|
|
107
116
|
const profiles = sortCloudFoundryProfilesForEndpoint({
|
|
108
117
|
profiles: cache.cloudFoundry.loginProfiles,
|
|
109
118
|
apiEndpoint: options.apiEndpoint,
|
|
110
119
|
preferredOrg: options.preferredOrg,
|
|
111
120
|
});
|
|
112
121
|
if (!profiles.length) {
|
|
113
|
-
console.log(
|
|
114
|
-
console.log(
|
|
122
|
+
console.log(chalk_1.default.yellow(`Not logged in to ${(0, cf_1.inferCloudFoundryRegionFromApiEndpoint)(options.apiEndpoint)} and no cached password was found for automatic login.`));
|
|
123
|
+
console.log(chalk_1.default.gray("Run smdg cf login once, choose to save password, then retry this command."));
|
|
115
124
|
throw new Error("Cloud Foundry automatic login is required");
|
|
116
125
|
}
|
|
117
126
|
let lastError = orgsCheck.stderr || orgsCheck.stdout || "cf orgs failed";
|
|
118
127
|
for (const profile of profiles) {
|
|
119
|
-
console.log(
|
|
120
|
-
const authExitCode = await authenticateCloudFoundry({
|
|
128
|
+
console.log(chalk_1.default.gray(`Auto auth CF ${(0, cf_1.inferCloudFoundryRegionFromApiEndpoint)(options.apiEndpoint)} as ${profile.username}...`));
|
|
129
|
+
const authExitCode = await (0, cf_1.authenticateCloudFoundry)({
|
|
121
130
|
username: profile.username,
|
|
122
131
|
password: profile.password,
|
|
123
132
|
});
|
|
@@ -125,7 +134,7 @@ async function ensureCloudFoundryAuthenticatedForApiEndpoint(options) {
|
|
|
125
134
|
lastError = `cf auth failed for ${profile.username}`;
|
|
126
135
|
continue;
|
|
127
136
|
}
|
|
128
|
-
const nextOrgsCheck = await runCommand("cf", ["orgs"]);
|
|
137
|
+
const nextOrgsCheck = await (0, process_1.runCommand)("cf", ["orgs"]);
|
|
129
138
|
if (nextOrgsCheck.exitCode === 0) {
|
|
130
139
|
const updatedProfile = {
|
|
131
140
|
...profile,
|
|
@@ -134,7 +143,7 @@ async function ensureCloudFoundryAuthenticatedForApiEndpoint(options) {
|
|
|
134
143
|
space: options.preferredSpace || profile.space,
|
|
135
144
|
updatedAt: new Date().toISOString(),
|
|
136
145
|
};
|
|
137
|
-
await rememberCloudFoundryLoginProfile(updatedProfile);
|
|
146
|
+
await (0, cache_1.rememberCloudFoundryLoginProfile)(updatedProfile);
|
|
138
147
|
return updatedProfile;
|
|
139
148
|
}
|
|
140
149
|
lastError = nextOrgsCheck.stderr || nextOrgsCheck.stdout || lastError;
|
|
@@ -205,7 +214,7 @@ function buildCloudFoundryDebugSshArgs(options) {
|
|
|
205
214
|
return args;
|
|
206
215
|
}
|
|
207
216
|
async function selectNodeInspectorPrepareMode(options) {
|
|
208
|
-
return searchableSelectChoice({
|
|
217
|
+
return (0, prompts_2.searchableSelectChoice)({
|
|
209
218
|
message: "Prepare Node.js inspector",
|
|
210
219
|
choices: [
|
|
211
220
|
{
|
|
@@ -228,14 +237,14 @@ async function selectNodeInspectorPrepareMode(options) {
|
|
|
228
237
|
});
|
|
229
238
|
}
|
|
230
239
|
async function ensureSshEnabledForDebug(appName) {
|
|
231
|
-
const sshEnabledResult = await runCommand("cf", ["ssh-enabled", appName]);
|
|
240
|
+
const sshEnabledResult = await (0, process_1.runCommand)("cf", ["ssh-enabled", appName]);
|
|
232
241
|
const combinedOutput = `${sshEnabledResult.stdout}
|
|
233
242
|
${sshEnabledResult.stderr}`;
|
|
234
243
|
if (sshEnabledResult.exitCode === 0 && /enabled/i.test(combinedOutput) && !/not enabled/i.test(combinedOutput)) {
|
|
235
244
|
return;
|
|
236
245
|
}
|
|
237
|
-
console.log(
|
|
238
|
-
const enableResult = await runCommand("cf", ["enable-ssh", appName]);
|
|
246
|
+
console.log(chalk_1.default.yellow("SSH is not enabled for this app. Enabling SSH..."));
|
|
247
|
+
const enableResult = await (0, process_1.runCommand)("cf", ["enable-ssh", appName]);
|
|
239
248
|
if (enableResult.stdout)
|
|
240
249
|
console.log(enableResult.stdout);
|
|
241
250
|
if (enableResult.stderr)
|
|
@@ -246,8 +255,8 @@ ${sshEnabledResult.stderr}`;
|
|
|
246
255
|
}
|
|
247
256
|
async function setNodeInspectorEnvironmentAndRestart(options) {
|
|
248
257
|
const nodeOptions = `--inspect=0.0.0.0:${options.remotePort} --enable-source-maps`;
|
|
249
|
-
console.log(
|
|
250
|
-
const setEnvResult = await runCommand("cf", ["set-env", options.appName, "NODE_OPTIONS", nodeOptions]);
|
|
258
|
+
console.log(chalk_1.default.gray(`Setting NODE_OPTIONS for ${options.appName}: ${nodeOptions}`));
|
|
259
|
+
const setEnvResult = await (0, process_1.runCommand)("cf", ["set-env", options.appName, "NODE_OPTIONS", nodeOptions]);
|
|
251
260
|
if (setEnvResult.stdout)
|
|
252
261
|
console.log(setEnvResult.stdout);
|
|
253
262
|
if (setEnvResult.stderr)
|
|
@@ -255,8 +264,8 @@ async function setNodeInspectorEnvironmentAndRestart(options) {
|
|
|
255
264
|
if (setEnvResult.exitCode !== 0) {
|
|
256
265
|
throw new Error("cf set-env NODE_OPTIONS failed");
|
|
257
266
|
}
|
|
258
|
-
console.log(
|
|
259
|
-
const restartExitCode = await runCommandInherit("cf", ["restart", options.appName]);
|
|
267
|
+
console.log(chalk_1.default.yellow("Restarting app so NODE_OPTIONS takes effect..."));
|
|
268
|
+
const restartExitCode = await (0, process_1.runCommandInherit)("cf", ["restart", options.appName]);
|
|
260
269
|
if (restartExitCode !== 0) {
|
|
261
270
|
throw new Error(`cf restart ${options.appName} failed`);
|
|
262
271
|
}
|
|
@@ -290,23 +299,23 @@ async function waitForNodeInspectorDebugUrl(localPort, timeoutMs = 10000) {
|
|
|
290
299
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
291
300
|
}
|
|
292
301
|
if (lastError instanceof Error) {
|
|
293
|
-
console.log(
|
|
302
|
+
console.log(chalk_1.default.gray(`Could not read inspector JSON yet: ${lastError.message}`));
|
|
294
303
|
}
|
|
295
304
|
return undefined;
|
|
296
305
|
}
|
|
297
306
|
function printNodeInspectorAttachInfo(options) {
|
|
298
307
|
console.log("");
|
|
299
|
-
console.log(
|
|
300
|
-
console.log(`Chrome inspect: ${
|
|
301
|
-
console.log(`Local inspector JSON: ${
|
|
308
|
+
console.log(chalk_1.default.green(`Debug tunnel is ready for ${options.appName} instance ${options.instanceIndex}.`));
|
|
309
|
+
console.log(`Chrome inspect: ${chalk_1.default.cyan("chrome://inspect")}`);
|
|
310
|
+
console.log(`Local inspector JSON: ${chalk_1.default.cyan(`http://127.0.0.1:${options.localPort}/json/list`)}`);
|
|
302
311
|
if (options.debugUrl) {
|
|
303
|
-
console.log(`Direct DevTools link: ${
|
|
312
|
+
console.log(`Direct DevTools link: ${chalk_1.default.cyan(options.debugUrl)}`);
|
|
304
313
|
}
|
|
305
314
|
else {
|
|
306
|
-
console.log(
|
|
315
|
+
console.log(chalk_1.default.yellow("Direct DevTools link was not detected yet. Open chrome://inspect and configure localhost target."));
|
|
307
316
|
}
|
|
308
317
|
console.log("");
|
|
309
|
-
console.log(
|
|
318
|
+
console.log(chalk_1.default.gray("VS Code attach config:"));
|
|
310
319
|
console.log(JSON.stringify({
|
|
311
320
|
type: "node",
|
|
312
321
|
request: "attach",
|
|
@@ -318,7 +327,7 @@ function printNodeInspectorAttachInfo(options) {
|
|
|
318
327
|
skipFiles: ["<node_internals>/**"],
|
|
319
328
|
}, null, 2));
|
|
320
329
|
console.log("");
|
|
321
|
-
console.log(
|
|
330
|
+
console.log(chalk_1.default.gray("Keep this terminal open. Press Ctrl+C to close the debug tunnel."));
|
|
322
331
|
}
|
|
323
332
|
function buildVscodeNodeAttachConfiguration(options) {
|
|
324
333
|
return {
|
|
@@ -340,21 +349,21 @@ function buildVscodeNodeAttachConfiguration(options) {
|
|
|
340
349
|
};
|
|
341
350
|
}
|
|
342
351
|
async function writeVscodeLaunchConfiguration(options) {
|
|
343
|
-
const vscodeDirectoryPath =
|
|
344
|
-
const launchJsonPath =
|
|
352
|
+
const vscodeDirectoryPath = node_path_1.default.resolve(options.cwd, ".vscode");
|
|
353
|
+
const launchJsonPath = node_path_1.default.join(vscodeDirectoryPath, "launch.json");
|
|
345
354
|
const configuration = buildVscodeNodeAttachConfiguration({
|
|
346
355
|
appName: options.appName,
|
|
347
356
|
localPort: options.localPort,
|
|
348
357
|
remoteRoot: options.remoteRoot ?? "/home/vcap/app",
|
|
349
358
|
});
|
|
350
|
-
await
|
|
359
|
+
await fs_extra_1.default.ensureDir(vscodeDirectoryPath);
|
|
351
360
|
let launchJson = {
|
|
352
361
|
version: "0.2.0",
|
|
353
362
|
configurations: [],
|
|
354
363
|
};
|
|
355
|
-
if (await
|
|
364
|
+
if (await fs_extra_1.default.pathExists(launchJsonPath)) {
|
|
356
365
|
try {
|
|
357
|
-
const currentContent = await
|
|
366
|
+
const currentContent = await fs_extra_1.default.readFile(launchJsonPath, "utf8");
|
|
358
367
|
const parsed = JSON.parse(currentContent);
|
|
359
368
|
launchJson = {
|
|
360
369
|
version: typeof parsed.version === "string" ? parsed.version : "0.2.0",
|
|
@@ -363,8 +372,8 @@ async function writeVscodeLaunchConfiguration(options) {
|
|
|
363
372
|
}
|
|
364
373
|
catch {
|
|
365
374
|
const backupPath = `${launchJsonPath}.backup-${Date.now()}`;
|
|
366
|
-
await
|
|
367
|
-
console.log(
|
|
375
|
+
await fs_extra_1.default.copyFile(launchJsonPath, backupPath);
|
|
376
|
+
console.log(chalk_1.default.yellow(`Existing launch.json is not valid JSON. Backup created: ${backupPath}`));
|
|
368
377
|
}
|
|
369
378
|
}
|
|
370
379
|
const configurationName = String(configuration.name);
|
|
@@ -375,41 +384,41 @@ async function writeVscodeLaunchConfiguration(options) {
|
|
|
375
384
|
else {
|
|
376
385
|
launchJson.configurations.unshift(configuration);
|
|
377
386
|
}
|
|
378
|
-
await
|
|
387
|
+
await fs_extra_1.default.writeFile(launchJsonPath, `${JSON.stringify(launchJson, null, 2)}\n`, "utf8");
|
|
379
388
|
return launchJsonPath;
|
|
380
389
|
}
|
|
381
390
|
function printVscodeAttachInstructions(options) {
|
|
382
391
|
console.log("");
|
|
383
392
|
if (options.inspectorReady === false) {
|
|
384
|
-
console.log(
|
|
385
|
-
console.log(
|
|
393
|
+
console.log(chalk_1.default.yellow(`VS Code config was created, but the Node inspector is not reachable yet for ${options.appName} instance ${options.instanceIndex}.`));
|
|
394
|
+
console.log(chalk_1.default.yellow("The VS Code debug toolbar appears only after the inspector is reachable and you start the attach config."));
|
|
386
395
|
}
|
|
387
396
|
else {
|
|
388
|
-
console.log(
|
|
397
|
+
console.log(chalk_1.default.green(`VS Code debug config is ready for ${options.appName} instance ${options.instanceIndex}.`));
|
|
389
398
|
}
|
|
390
|
-
console.log(`Launch file: ${
|
|
391
|
-
console.log(`Attach config: ${
|
|
392
|
-
console.log(`Inspector: ${
|
|
399
|
+
console.log(`Launch file: ${chalk_1.default.cyan(options.launchJsonPath)}`);
|
|
400
|
+
console.log(`Attach config: ${chalk_1.default.cyan(`Attach BTP ${options.appName}`)}`);
|
|
401
|
+
console.log(`Inspector: ${chalk_1.default.cyan(`127.0.0.1:${options.localPort}`)}`);
|
|
393
402
|
console.log("");
|
|
394
|
-
console.log(
|
|
403
|
+
console.log(chalk_1.default.cyan("How to start debugging in VS Code:"));
|
|
395
404
|
console.log("1. Keep this terminal open. It owns the CF SSH tunnel.");
|
|
396
405
|
console.log("2. Open VS Code Run and Debug panel with Ctrl+Shift+D.");
|
|
397
|
-
console.log(`3. Select ${
|
|
406
|
+
console.log(`3. Select ${chalk_1.default.cyan(`Attach BTP ${options.appName}`)}.`);
|
|
398
407
|
console.log("4. Press F5 or the green Start Debugging button.");
|
|
399
408
|
console.log("5. Debug buttons such as pause, step over, step into, restart, and stop appear only after attach succeeds.");
|
|
400
409
|
console.log("");
|
|
401
|
-
console.log(
|
|
410
|
+
console.log(chalk_1.default.gray("Press Ctrl+C in this terminal to close the tunnel."));
|
|
402
411
|
}
|
|
403
412
|
async function openVisualStudioCode(options) {
|
|
404
413
|
const args = options.debugPanel
|
|
405
414
|
? ["--reuse-window", options.cwd, "--command", "workbench.view.debug"]
|
|
406
415
|
: [options.cwd];
|
|
407
|
-
const result = await runCommand("code", args);
|
|
416
|
+
const result = await (0, process_1.runCommand)("code", args);
|
|
408
417
|
if (result.exitCode !== 0) {
|
|
409
|
-
console.log(
|
|
410
|
-
console.log(
|
|
418
|
+
console.log(chalk_1.default.yellow("Could not open VS Code automatically. Open this folder manually in VS Code."));
|
|
419
|
+
console.log(chalk_1.default.gray("Then open Run and Debug with Ctrl+Shift+D."));
|
|
411
420
|
if (result.stderr)
|
|
412
|
-
console.log(
|
|
421
|
+
console.log(chalk_1.default.gray(result.stderr));
|
|
413
422
|
}
|
|
414
423
|
}
|
|
415
424
|
async function selectDebugMode(options) {
|
|
@@ -425,7 +434,7 @@ async function selectDebugMode(options) {
|
|
|
425
434
|
return "chrome";
|
|
426
435
|
if (options.vscode)
|
|
427
436
|
return "vscode";
|
|
428
|
-
return searchableSelectChoice({
|
|
437
|
+
return (0, prompts_2.searchableSelectChoice)({
|
|
429
438
|
message: "Select debug mode",
|
|
430
439
|
choices: [
|
|
431
440
|
{
|
|
@@ -470,9 +479,9 @@ async function maybeSwitchCloudFoundryTargetForDebug(options) {
|
|
|
470
479
|
const currentTargetLabel = [
|
|
471
480
|
target.org ? `org: ${target.org}` : "org: N/A",
|
|
472
481
|
target.space ? `space: ${target.space}` : "space: N/A",
|
|
473
|
-
target.apiEndpoint ? inferCloudFoundryRegionFromApiEndpoint(target.apiEndpoint) : "current region",
|
|
482
|
+
target.apiEndpoint ? (0, cf_1.inferCloudFoundryRegionFromApiEndpoint)(target.apiEndpoint) : "current region",
|
|
474
483
|
].join(" · ");
|
|
475
|
-
const action = await searchableSelectChoice({
|
|
484
|
+
const action = await (0, prompts_2.searchableSelectChoice)({
|
|
476
485
|
message: "Select BTP target for debug",
|
|
477
486
|
choices: [
|
|
478
487
|
{ title: `Use current target (${currentTargetLabel})`, value: "current" },
|
|
@@ -488,7 +497,7 @@ async function selectDebugInstance(options) {
|
|
|
488
497
|
if (options.instance?.trim()) {
|
|
489
498
|
return options.instance.trim();
|
|
490
499
|
}
|
|
491
|
-
return searchableSelectChoice({
|
|
500
|
+
return (0, prompts_2.searchableSelectChoice)({
|
|
492
501
|
message: "Select app instance index",
|
|
493
502
|
choices: [
|
|
494
503
|
{ title: "0", value: "0" },
|
|
@@ -504,7 +513,7 @@ async function selectDebugPort(options) {
|
|
|
504
513
|
if (options.value?.trim()) {
|
|
505
514
|
return parsePositivePort(options.value, options.defaultPort);
|
|
506
515
|
}
|
|
507
|
-
const portValue = await searchableSelectChoice({
|
|
516
|
+
const portValue = await (0, prompts_2.searchableSelectChoice)({
|
|
508
517
|
message: options.message,
|
|
509
518
|
choices: [
|
|
510
519
|
{ title: `${options.defaultPort} recommended`, value: String(options.defaultPort) },
|
|
@@ -567,10 +576,10 @@ function writeFilteredLogChunk(chunk, options) {
|
|
|
567
576
|
options.outputStream?.write(outputText);
|
|
568
577
|
}
|
|
569
578
|
async function refreshAppsCacheForCurrentTarget() {
|
|
570
|
-
const target = await readCloudFoundryTarget();
|
|
571
|
-
const targetKey = buildCloudFoundryTargetKey(target);
|
|
572
|
-
const apps = await listCloudFoundryApps();
|
|
573
|
-
await rememberCloudFoundryApps(targetKey, apps);
|
|
579
|
+
const target = await (0, cf_1.readCloudFoundryTarget)();
|
|
580
|
+
const targetKey = (0, cf_1.buildCloudFoundryTargetKey)(target);
|
|
581
|
+
const apps = await (0, cf_1.listCloudFoundryApps)();
|
|
582
|
+
await (0, cache_1.rememberCloudFoundryApps)(targetKey, apps);
|
|
574
583
|
return apps;
|
|
575
584
|
}
|
|
576
585
|
function refreshAppsCacheInDetachedProcess() {
|
|
@@ -578,7 +587,7 @@ function refreshAppsCacheInDetachedProcess() {
|
|
|
578
587
|
if (!entryFilePath) {
|
|
579
588
|
return;
|
|
580
589
|
}
|
|
581
|
-
const childProcess = spawn(process.execPath, [entryFilePath, "cf", "apps-cache-refresh"], {
|
|
590
|
+
const childProcess = (0, node_child_process_1.spawn)(process.execPath, [entryFilePath, "cf", "apps-cache-refresh"], {
|
|
582
591
|
detached: true,
|
|
583
592
|
stdio: "ignore",
|
|
584
593
|
windowsHide: true,
|
|
@@ -590,9 +599,9 @@ async function getAppsWithCache(options) {
|
|
|
590
599
|
if (options.refresh) {
|
|
591
600
|
return refreshAppsCacheForCurrentTarget();
|
|
592
601
|
}
|
|
593
|
-
const target = await readCloudFoundryTarget();
|
|
594
|
-
const targetKey = buildCloudFoundryTargetKey(target);
|
|
595
|
-
const cache = await readCache();
|
|
602
|
+
const target = await (0, cf_1.readCloudFoundryTarget)();
|
|
603
|
+
const targetKey = (0, cf_1.buildCloudFoundryTargetKey)(target);
|
|
604
|
+
const cache = await (0, cache_1.readCache)();
|
|
596
605
|
const cachedEntry = cache.cloudFoundry.appListsByTarget[targetKey];
|
|
597
606
|
if (cachedEntry?.apps.length) {
|
|
598
607
|
if (options.startBackgroundRefresh) {
|
|
@@ -604,16 +613,16 @@ async function getAppsWithCache(options) {
|
|
|
604
613
|
}
|
|
605
614
|
async function resolveAppSelection(options) {
|
|
606
615
|
if (options.app?.trim()) {
|
|
607
|
-
await rememberSelectedApp(options.app.trim());
|
|
616
|
+
await (0, cache_1.rememberSelectedApp)(options.app.trim());
|
|
608
617
|
return options.app.trim();
|
|
609
618
|
}
|
|
610
619
|
const apps = await getAppsWithCache({ refresh: options.refresh, startBackgroundRefresh: !options.refresh });
|
|
611
|
-
const cache = await readCache();
|
|
620
|
+
const cache = await (0, cache_1.readCache)();
|
|
612
621
|
const cachedSelectedAppNames = cache.cloudFoundry.selectedApps;
|
|
613
622
|
const cachedSelectedApps = cachedSelectedAppNames
|
|
614
623
|
.filter((appName) => !apps.some((app) => app.name === appName))
|
|
615
|
-
.map((appName) => ({ title: `${appName} ${
|
|
616
|
-
const appName = await searchableSelectChoice({
|
|
624
|
+
.map((appName) => ({ title: `${appName} ${chalk_1.default.gray("cached selected")}`, value: appName }));
|
|
625
|
+
const appName = await (0, prompts_2.searchableSelectChoice)({
|
|
617
626
|
message: options.message,
|
|
618
627
|
choices: [
|
|
619
628
|
...apps.map((app) => ({
|
|
@@ -625,7 +634,7 @@ async function resolveAppSelection(options) {
|
|
|
625
634
|
validateCustomValue: validateRequired,
|
|
626
635
|
customValueTitle: (value) => `Use typed app name: ${value}`,
|
|
627
636
|
});
|
|
628
|
-
await rememberSelectedApp(appName);
|
|
637
|
+
await (0, cache_1.rememberSelectedApp)(appName);
|
|
629
638
|
return appName;
|
|
630
639
|
}
|
|
631
640
|
function printTarget(target) {
|
|
@@ -669,7 +678,7 @@ async function selectCloudFoundryApiEndpoint(options) {
|
|
|
669
678
|
}
|
|
670
679
|
const choices = [
|
|
671
680
|
...cachedApiEndpoints.map((apiEndpoint) => ({
|
|
672
|
-
title: `${apiEndpoint} ${
|
|
681
|
+
title: `${apiEndpoint} ${chalk_1.default.gray("cached")}`,
|
|
673
682
|
value: apiEndpoint,
|
|
674
683
|
})),
|
|
675
684
|
...DEFAULT_CLOUD_FOUNDRY_API_ENDPOINTS
|
|
@@ -677,7 +686,7 @@ async function selectCloudFoundryApiEndpoint(options) {
|
|
|
677
686
|
.map((apiEndpoint) => ({ title: apiEndpoint, value: apiEndpoint })),
|
|
678
687
|
{ title: "Enter CF API endpoint manually", value: "__ENTER_MANUAL__" },
|
|
679
688
|
];
|
|
680
|
-
return searchableSelectChoice({
|
|
689
|
+
return (0, prompts_2.searchableSelectChoice)({
|
|
681
690
|
message: "Select CF API endpoint",
|
|
682
691
|
choices: choices.filter((choice) => choice.value !== "__ENTER_MANUAL__"),
|
|
683
692
|
validateCustomValue: validateRequired,
|
|
@@ -688,16 +697,16 @@ async function selectCloudFoundryOrganization(options) {
|
|
|
688
697
|
if (options.org?.trim()) {
|
|
689
698
|
return options.org.trim();
|
|
690
699
|
}
|
|
691
|
-
const organizations = await listCloudFoundryOrganizations();
|
|
700
|
+
const organizations = await (0, cf_1.listCloudFoundryOrganizations)();
|
|
692
701
|
if (organizations.length === 0) {
|
|
693
|
-
return selectFromHistoryOrInput({
|
|
702
|
+
return (0, prompts_2.selectFromHistoryOrInput)({
|
|
694
703
|
message: "Select CF org",
|
|
695
704
|
values: options.cachedOrganizations,
|
|
696
705
|
initialValue: options.cachedOrganizations[0],
|
|
697
706
|
validate: validateRequired,
|
|
698
707
|
});
|
|
699
708
|
}
|
|
700
|
-
return searchableSelectChoice({
|
|
709
|
+
return (0, prompts_2.searchableSelectChoice)({
|
|
701
710
|
message: "Select CF org",
|
|
702
711
|
choices: organizations.map((organization) => ({ title: organization, value: organization })),
|
|
703
712
|
validateCustomValue: validateRequired,
|
|
@@ -708,16 +717,16 @@ async function selectCloudFoundrySpace(options) {
|
|
|
708
717
|
if (options.space?.trim()) {
|
|
709
718
|
return options.space.trim();
|
|
710
719
|
}
|
|
711
|
-
const spaces = await listCloudFoundrySpaces();
|
|
720
|
+
const spaces = await (0, cf_1.listCloudFoundrySpaces)();
|
|
712
721
|
if (spaces.length === 0) {
|
|
713
|
-
return selectFromHistoryOrInput({
|
|
722
|
+
return (0, prompts_2.selectFromHistoryOrInput)({
|
|
714
723
|
message: "Select CF space",
|
|
715
724
|
values: options.cachedSpaces,
|
|
716
725
|
initialValue: options.cachedSpaces[0] ?? "app",
|
|
717
726
|
});
|
|
718
727
|
}
|
|
719
728
|
const initialSpace = spaces.includes("app") ? "app" : spaces[0];
|
|
720
|
-
return searchableSelectChoice({
|
|
729
|
+
return (0, prompts_2.searchableSelectChoice)({
|
|
721
730
|
message: "Select CF space",
|
|
722
731
|
choices: [
|
|
723
732
|
...spaces
|
|
@@ -732,13 +741,14 @@ async function selectCloudFoundrySpace(options) {
|
|
|
732
741
|
});
|
|
733
742
|
}
|
|
734
743
|
async function runLoginCommand(options) {
|
|
735
|
-
|
|
744
|
+
await (0, tooling_1.ensureExternalTool)("cf");
|
|
745
|
+
const cache = await (0, cache_1.readCache)();
|
|
736
746
|
const lastProfile = cache.cloudFoundry.loginProfiles[0];
|
|
737
747
|
const apiEndpoint = await selectCloudFoundryApiEndpoint({
|
|
738
748
|
api: options.api,
|
|
739
749
|
cachedApiEndpoints: cache.cloudFoundry.loginProfiles.map((item) => item.apiEndpoint),
|
|
740
750
|
});
|
|
741
|
-
const username = options.username ?? await selectFromHistoryOrInput({
|
|
751
|
+
const username = options.username ?? await (0, prompts_2.selectFromHistoryOrInput)({
|
|
742
752
|
message: "Select CF username",
|
|
743
753
|
values: cache.cloudFoundry.loginProfiles.map((item) => item.username),
|
|
744
754
|
initialValue: lastProfile?.username,
|
|
@@ -747,7 +757,7 @@ async function runLoginCommand(options) {
|
|
|
747
757
|
let password = options.password ?? lastProfile?.password;
|
|
748
758
|
let shouldSavePassword = options.savePassword ?? false;
|
|
749
759
|
if (!password) {
|
|
750
|
-
const response = await
|
|
760
|
+
const response = await (0, prompts_1.default)({
|
|
751
761
|
type: "password",
|
|
752
762
|
name: "password",
|
|
753
763
|
message: "Enter CF password",
|
|
@@ -759,7 +769,7 @@ async function runLoginCommand(options) {
|
|
|
759
769
|
password = response.password;
|
|
760
770
|
}
|
|
761
771
|
else {
|
|
762
|
-
const response = await
|
|
772
|
+
const response = await (0, prompts_1.default)({
|
|
763
773
|
type: "select",
|
|
764
774
|
name: "useCachedPassword",
|
|
765
775
|
message: "Use cached password?",
|
|
@@ -770,7 +780,7 @@ async function runLoginCommand(options) {
|
|
|
770
780
|
initial: 0,
|
|
771
781
|
});
|
|
772
782
|
if (!response.useCachedPassword) {
|
|
773
|
-
const passwordResponse = await
|
|
783
|
+
const passwordResponse = await (0, prompts_1.default)({
|
|
774
784
|
type: "password",
|
|
775
785
|
name: "password",
|
|
776
786
|
message: "Enter CF password",
|
|
@@ -783,7 +793,7 @@ async function runLoginCommand(options) {
|
|
|
783
793
|
}
|
|
784
794
|
}
|
|
785
795
|
if (!shouldSavePassword) {
|
|
786
|
-
const savePasswordResponse = await
|
|
796
|
+
const savePasswordResponse = await (0, prompts_1.default)({
|
|
787
797
|
type: "select",
|
|
788
798
|
name: "savePassword",
|
|
789
799
|
message: "Save password for automatic re-login and region scan?",
|
|
@@ -795,12 +805,12 @@ async function runLoginCommand(options) {
|
|
|
795
805
|
});
|
|
796
806
|
shouldSavePassword = Boolean(savePasswordResponse.savePassword);
|
|
797
807
|
}
|
|
798
|
-
const apiExitCode = await setCloudFoundryApiEndpoint(apiEndpoint);
|
|
808
|
+
const apiExitCode = await (0, cf_1.setCloudFoundryApiEndpoint)(apiEndpoint);
|
|
799
809
|
if (apiExitCode !== 0) {
|
|
800
810
|
process.exitCode = apiExitCode;
|
|
801
811
|
return;
|
|
802
812
|
}
|
|
803
|
-
const authExitCode = await authenticateCloudFoundry({ username, password });
|
|
813
|
+
const authExitCode = await (0, cf_1.authenticateCloudFoundry)({ username, password });
|
|
804
814
|
if (authExitCode !== 0) {
|
|
805
815
|
process.exitCode = authExitCode;
|
|
806
816
|
return;
|
|
@@ -811,7 +821,7 @@ async function runLoginCommand(options) {
|
|
|
811
821
|
.filter((item) => item.apiEndpoint === apiEndpoint && item.username === username)
|
|
812
822
|
.map((item) => item.org),
|
|
813
823
|
});
|
|
814
|
-
const orgExitCode = await targetCloudFoundryOrg(org);
|
|
824
|
+
const orgExitCode = await (0, cf_1.targetCloudFoundryOrg)(org);
|
|
815
825
|
if (orgExitCode !== 0) {
|
|
816
826
|
process.exitCode = orgExitCode;
|
|
817
827
|
return;
|
|
@@ -824,13 +834,13 @@ async function runLoginCommand(options) {
|
|
|
824
834
|
.filter(Boolean),
|
|
825
835
|
});
|
|
826
836
|
if (space) {
|
|
827
|
-
const spaceExitCode = await targetCloudFoundrySpace(space);
|
|
837
|
+
const spaceExitCode = await (0, cf_1.targetCloudFoundrySpace)(space);
|
|
828
838
|
if (spaceExitCode !== 0) {
|
|
829
839
|
process.exitCode = spaceExitCode;
|
|
830
840
|
return;
|
|
831
841
|
}
|
|
832
842
|
}
|
|
833
|
-
await rememberCloudFoundryLoginProfile({
|
|
843
|
+
await (0, cache_1.rememberCloudFoundryLoginProfile)({
|
|
834
844
|
apiEndpoint,
|
|
835
845
|
username,
|
|
836
846
|
org,
|
|
@@ -839,16 +849,16 @@ async function runLoginCommand(options) {
|
|
|
839
849
|
updatedAt: new Date().toISOString(),
|
|
840
850
|
});
|
|
841
851
|
if (shouldSavePassword) {
|
|
842
|
-
console.log(
|
|
852
|
+
console.log(chalk_1.default.yellow("Password was cached in ~/.simplemdg/cache.json for automatic re-login. Do not use this on shared machines."));
|
|
843
853
|
}
|
|
844
|
-
console.log(
|
|
854
|
+
console.log(chalk_1.default.green("CF login completed."));
|
|
845
855
|
}
|
|
846
856
|
function formatCloudFoundryOrgEntry(entry, target) {
|
|
847
857
|
const isCurrent = entry.apiEndpoint === target.apiEndpoint && entry.org === target.org;
|
|
848
858
|
const spaceText = typeof entry.spaceCount === "number"
|
|
849
859
|
? `${entry.spaceCount} ${entry.spaceCount === 1 ? "space" : "spaces"}`
|
|
850
860
|
: "spaces unknown";
|
|
851
|
-
return `${entry.org} ${
|
|
861
|
+
return `${entry.org} ${chalk_1.default.gray(`${entry.region} · ${spaceText}${isCurrent ? " · current" : ""}`)}`;
|
|
852
862
|
}
|
|
853
863
|
function getCloudFoundryApiEndpointsForOrgSearch(options, target, cache) {
|
|
854
864
|
return uniqueValues([
|
|
@@ -860,8 +870,8 @@ function getCloudFoundryApiEndpointsForOrgSearch(options, target, cache) {
|
|
|
860
870
|
]);
|
|
861
871
|
}
|
|
862
872
|
async function getCloudFoundryOrganizationsAcrossRegions(options) {
|
|
863
|
-
const target = await readCloudFoundryTarget();
|
|
864
|
-
const cache = await readCache();
|
|
873
|
+
const target = await (0, cf_1.readCloudFoundryTarget)();
|
|
874
|
+
const cache = await (0, cache_1.readCache)();
|
|
865
875
|
const cachedEntries = cache.cloudFoundry.orgsAcrossRegions ?? [];
|
|
866
876
|
const cachedRegionCount = new Set(cachedEntries.map((entry) => entry.region)).size;
|
|
867
877
|
if (!options.refresh && cachedEntries.length && cachedRegionCount > 1) {
|
|
@@ -871,21 +881,21 @@ async function getCloudFoundryOrganizationsAcrossRegions(options) {
|
|
|
871
881
|
});
|
|
872
882
|
}
|
|
873
883
|
const apiEndpoints = getCloudFoundryApiEndpointsForOrgSearch(options, target, cache);
|
|
874
|
-
console.log(
|
|
884
|
+
console.log(chalk_1.default.gray(`Searching CF orgs across ${apiEndpoints.length} region endpoint(s)...`));
|
|
875
885
|
const credentials = cache.cloudFoundry.loginProfiles.map((profile) => ({
|
|
876
886
|
apiEndpoint: profile.apiEndpoint,
|
|
877
887
|
username: profile.username,
|
|
878
888
|
password: profile.password,
|
|
879
889
|
}));
|
|
880
|
-
const entries = await scanCloudFoundryOrganizationsAcrossRegions(apiEndpoints, credentials);
|
|
890
|
+
const entries = await (0, cf_1.scanCloudFoundryOrganizationsAcrossRegions)(apiEndpoints, credentials);
|
|
881
891
|
if (entries.length) {
|
|
882
892
|
const regionCount = new Set(entries.map((entry) => entry.region)).size;
|
|
883
|
-
console.log(
|
|
884
|
-
await rememberCloudFoundryOrgEntries(entries);
|
|
893
|
+
console.log(chalk_1.default.green(`Found ${entries.length} org(s) across ${regionCount} region(s).`));
|
|
894
|
+
await (0, cache_1.rememberCloudFoundryOrgEntries)(entries);
|
|
885
895
|
return entries;
|
|
886
896
|
}
|
|
887
|
-
const currentOrganizations = await listCloudFoundryOrganizations().catch(() => []);
|
|
888
|
-
const currentRegion = target.apiEndpoint ? inferCloudFoundryRegionFromApiEndpoint(target.apiEndpoint) : "current";
|
|
897
|
+
const currentOrganizations = await (0, cf_1.listCloudFoundryOrganizations)().catch(() => []);
|
|
898
|
+
const currentRegion = target.apiEndpoint ? (0, cf_1.inferCloudFoundryRegionFromApiEndpoint)(target.apiEndpoint) : "current";
|
|
889
899
|
const fallbackEntries = currentOrganizations.map((organization) => ({
|
|
890
900
|
apiEndpoint: target.apiEndpoint ?? "",
|
|
891
901
|
region: currentRegion,
|
|
@@ -893,7 +903,7 @@ async function getCloudFoundryOrganizationsAcrossRegions(options) {
|
|
|
893
903
|
updatedAt: new Date().toISOString(),
|
|
894
904
|
}));
|
|
895
905
|
if (fallbackEntries.length) {
|
|
896
|
-
await rememberCloudFoundryOrgEntries(fallbackEntries);
|
|
906
|
+
await (0, cache_1.rememberCloudFoundryOrgEntries)(fallbackEntries);
|
|
897
907
|
}
|
|
898
908
|
return fallbackEntries;
|
|
899
909
|
}
|
|
@@ -903,7 +913,7 @@ async function runOrgCommand(options) {
|
|
|
903
913
|
? "list"
|
|
904
914
|
: options.switch
|
|
905
915
|
? "switch"
|
|
906
|
-
: await searchableSelectChoice({
|
|
916
|
+
: await (0, prompts_2.searchableSelectChoice)({
|
|
907
917
|
message: "What do you want to do with CF org?",
|
|
908
918
|
choices: [
|
|
909
919
|
{ title: "List orgs across regions", value: "list" },
|
|
@@ -921,18 +931,18 @@ async function runOrgCommand(options) {
|
|
|
921
931
|
refresh: options.refresh,
|
|
922
932
|
});
|
|
923
933
|
const organizationRegionCount = new Set(organizationEntries.map((entry) => entry.region)).size;
|
|
924
|
-
const latestTarget = await readCloudFoundryTarget();
|
|
934
|
+
const latestTarget = await (0, cf_1.readCloudFoundryTarget)();
|
|
925
935
|
if (action === "list") {
|
|
926
936
|
printTarget(latestTarget);
|
|
927
937
|
console.log("");
|
|
928
938
|
if (!organizationEntries.length) {
|
|
929
|
-
console.log(
|
|
939
|
+
console.log(chalk_1.default.yellow("No orgs found for current CF user. Run smdg cf login, save the password, then run smdg cf org again."));
|
|
930
940
|
return;
|
|
931
941
|
}
|
|
932
|
-
console.log(
|
|
942
|
+
console.log(chalk_1.default.gray(`Showing ${organizationEntries.length} org(s) across ${organizationRegionCount} region(s).`));
|
|
933
943
|
console.log("");
|
|
934
944
|
for (const entry of organizationEntries) {
|
|
935
|
-
const marker = entry.apiEndpoint === latestTarget.apiEndpoint && entry.org === latestTarget.org ?
|
|
945
|
+
const marker = entry.apiEndpoint === latestTarget.apiEndpoint && entry.org === latestTarget.org ? chalk_1.default.green("*") : " ";
|
|
936
946
|
console.log(`${marker} ${formatCloudFoundryOrgEntry(entry, latestTarget)}`);
|
|
937
947
|
}
|
|
938
948
|
return;
|
|
@@ -944,19 +954,19 @@ async function runOrgCommand(options) {
|
|
|
944
954
|
}) ?? {
|
|
945
955
|
apiEndpoint: options.api?.trim() || latestTarget.apiEndpoint || "",
|
|
946
956
|
region: options.api?.trim()
|
|
947
|
-
? inferCloudFoundryRegionFromApiEndpoint(options.api.trim())
|
|
948
|
-
: inferCloudFoundryRegionFromApiEndpoint(latestTarget.apiEndpoint ?? "current"),
|
|
957
|
+
? (0, cf_1.inferCloudFoundryRegionFromApiEndpoint)(options.api.trim())
|
|
958
|
+
: (0, cf_1.inferCloudFoundryRegionFromApiEndpoint)(latestTarget.apiEndpoint ?? "current"),
|
|
949
959
|
org: options.org.trim(),
|
|
950
960
|
updatedAt: new Date().toISOString(),
|
|
951
961
|
};
|
|
952
962
|
}
|
|
953
963
|
else {
|
|
954
964
|
if (!organizationEntries.length) {
|
|
955
|
-
console.log(
|
|
956
|
-
console.log(
|
|
965
|
+
console.log(chalk_1.default.yellow("No orgs were found across regions."));
|
|
966
|
+
console.log(chalk_1.default.gray("Run smdg cf login and save the password, then run smdg cf org --list --refresh."));
|
|
957
967
|
return;
|
|
958
968
|
}
|
|
959
|
-
const selectedIndex = await searchableSelectChoice({
|
|
969
|
+
const selectedIndex = await (0, prompts_2.searchableSelectChoice)({
|
|
960
970
|
message: `Search CF org across ${organizationRegionCount} region(s)`,
|
|
961
971
|
choices: organizationEntries.map((entry, index) => ({
|
|
962
972
|
title: formatCloudFoundryOrgEntry(entry, latestTarget),
|
|
@@ -967,7 +977,7 @@ async function runOrgCommand(options) {
|
|
|
967
977
|
});
|
|
968
978
|
selectedEntry = organizationEntries[Number(selectedIndex)] ?? {
|
|
969
979
|
apiEndpoint: latestTarget.apiEndpoint ?? "",
|
|
970
|
-
region: inferCloudFoundryRegionFromApiEndpoint(latestTarget.apiEndpoint ?? "current"),
|
|
980
|
+
region: (0, cf_1.inferCloudFoundryRegionFromApiEndpoint)(latestTarget.apiEndpoint ?? "current"),
|
|
971
981
|
org: selectedIndex,
|
|
972
982
|
updatedAt: new Date().toISOString(),
|
|
973
983
|
};
|
|
@@ -981,22 +991,22 @@ async function runOrgCommand(options) {
|
|
|
981
991
|
preferredSpace: options.space,
|
|
982
992
|
reason: "switch-org",
|
|
983
993
|
});
|
|
984
|
-
const orgExitCode = await targetCloudFoundryOrg(selectedEntry.org);
|
|
994
|
+
const orgExitCode = await (0, cf_1.targetCloudFoundryOrg)(selectedEntry.org);
|
|
985
995
|
if (orgExitCode !== 0) {
|
|
986
|
-
console.log(
|
|
987
|
-
console.log(
|
|
996
|
+
console.log(chalk_1.default.yellow("Cannot switch to this org after automatic authentication."));
|
|
997
|
+
console.log(chalk_1.default.gray("Run smdg cf login, save the password, then try again."));
|
|
988
998
|
process.exitCode = orgExitCode;
|
|
989
999
|
return;
|
|
990
1000
|
}
|
|
991
|
-
const spaces = selectedEntry.spaces?.length ? selectedEntry.spaces : await listCloudFoundrySpaces();
|
|
992
|
-
const currentTargetAfterOrgSwitch = await readCloudFoundryTarget();
|
|
1001
|
+
const spaces = selectedEntry.spaces?.length ? selectedEntry.spaces : await (0, cf_1.listCloudFoundrySpaces)();
|
|
1002
|
+
const currentTargetAfterOrgSwitch = await (0, cf_1.readCloudFoundryTarget)();
|
|
993
1003
|
const preferredSpace = options.space?.trim() || currentTargetAfterOrgSwitch.space || (spaces.includes("app") ? "app" : spaces[0]);
|
|
994
|
-
const space = options.space?.trim() || await searchableSelectChoice({
|
|
1004
|
+
const space = options.space?.trim() || await (0, prompts_2.searchableSelectChoice)({
|
|
995
1005
|
message: "Select CF space",
|
|
996
1006
|
choices: [
|
|
997
1007
|
...spaces
|
|
998
1008
|
.filter((spaceName) => spaceName === preferredSpace)
|
|
999
|
-
.map((spaceName) => ({ title: `${spaceName} ${spaceName === currentTargetAfterOrgSwitch.space ?
|
|
1009
|
+
.map((spaceName) => ({ title: `${spaceName} ${spaceName === currentTargetAfterOrgSwitch.space ? chalk_1.default.gray("current") : chalk_1.default.gray("suggested")}`, value: spaceName })),
|
|
1000
1010
|
...spaces
|
|
1001
1011
|
.filter((spaceName) => spaceName !== preferredSpace)
|
|
1002
1012
|
.map((spaceName) => ({ title: spaceName, value: spaceName })),
|
|
@@ -1005,14 +1015,14 @@ async function runOrgCommand(options) {
|
|
|
1005
1015
|
customValueTitle: (value) => `Use typed CF space: ${value}`,
|
|
1006
1016
|
});
|
|
1007
1017
|
if (space) {
|
|
1008
|
-
const spaceExitCode = await targetCloudFoundrySpace(space);
|
|
1018
|
+
const spaceExitCode = await (0, cf_1.targetCloudFoundrySpace)(space);
|
|
1009
1019
|
if (spaceExitCode !== 0) {
|
|
1010
1020
|
process.exitCode = spaceExitCode;
|
|
1011
1021
|
return;
|
|
1012
1022
|
}
|
|
1013
1023
|
}
|
|
1014
1024
|
if (authenticatedProfile?.password) {
|
|
1015
|
-
await rememberCloudFoundryLoginProfile({
|
|
1025
|
+
await (0, cache_1.rememberCloudFoundryLoginProfile)({
|
|
1016
1026
|
...authenticatedProfile,
|
|
1017
1027
|
apiEndpoint: selectedEntry.apiEndpoint,
|
|
1018
1028
|
org: selectedEntry.org,
|
|
@@ -1020,14 +1030,14 @@ async function runOrgCommand(options) {
|
|
|
1020
1030
|
updatedAt: new Date().toISOString(),
|
|
1021
1031
|
});
|
|
1022
1032
|
}
|
|
1023
|
-
const switchedTarget = await readCloudFoundryTarget();
|
|
1024
|
-
console.log(
|
|
1033
|
+
const switchedTarget = await (0, cf_1.readCloudFoundryTarget)();
|
|
1034
|
+
console.log(chalk_1.default.green("CF org/space switched."));
|
|
1025
1035
|
printTarget(switchedTarget);
|
|
1026
1036
|
}
|
|
1027
1037
|
async function runAppsCommand(options) {
|
|
1028
|
-
const target = await readCloudFoundryTarget();
|
|
1029
|
-
const targetKey = buildCloudFoundryTargetKey(target);
|
|
1030
|
-
const cache = await readCache();
|
|
1038
|
+
const target = await (0, cf_1.readCloudFoundryTarget)();
|
|
1039
|
+
const targetKey = (0, cf_1.buildCloudFoundryTargetKey)(target);
|
|
1040
|
+
const cache = await (0, cache_1.readCache)();
|
|
1031
1041
|
const cachedEntry = cache.cloudFoundry.appListsByTarget[targetKey];
|
|
1032
1042
|
printTarget(target);
|
|
1033
1043
|
console.log("");
|
|
@@ -1037,12 +1047,12 @@ async function runAppsCommand(options) {
|
|
|
1037
1047
|
startBackgroundRefresh: shouldUseCache,
|
|
1038
1048
|
});
|
|
1039
1049
|
if (shouldUseCache && cachedEntry) {
|
|
1040
|
-
console.log(
|
|
1041
|
-
console.log(
|
|
1050
|
+
console.log(chalk_1.default.gray(`Using cached cf apps: ${cachedEntry.apps.length} apps, updated at ${cachedEntry.updatedAt}`));
|
|
1051
|
+
console.log(chalk_1.default.gray("Refreshing cf apps cache in background. Use --refresh when you want to wait for fresh data."));
|
|
1042
1052
|
console.log("");
|
|
1043
1053
|
}
|
|
1044
1054
|
else if (options.refresh) {
|
|
1045
|
-
console.log(
|
|
1055
|
+
console.log(chalk_1.default.green(`Refreshed cf apps cache for ${targetKey}.`));
|
|
1046
1056
|
console.log("");
|
|
1047
1057
|
}
|
|
1048
1058
|
if (options.select) {
|
|
@@ -1058,55 +1068,55 @@ async function runAppsCacheRefreshCommand() {
|
|
|
1058
1068
|
await refreshAppsCacheForCurrentTarget();
|
|
1059
1069
|
}
|
|
1060
1070
|
async function runBindCommand(options) {
|
|
1061
|
-
const repositoryPath = await resolveRepositoryPath(options.cwd ?? process.cwd());
|
|
1071
|
+
const repositoryPath = await (0, repository_1.resolveRepositoryPath)(options.cwd ?? process.cwd());
|
|
1062
1072
|
const appName = await resolveAppSelection({ app: options.app, refresh: options.refresh, message: "Select app to cds bind" });
|
|
1063
|
-
const exitCode = await runCommandInherit("cds", ["bind", "--to-app-services", appName], { cwd: repositoryPath });
|
|
1073
|
+
const exitCode = await (0, process_1.runCommandInherit)("cds", ["bind", "--to-app-services", appName], { cwd: repositoryPath });
|
|
1064
1074
|
process.exitCode = exitCode;
|
|
1065
1075
|
}
|
|
1066
1076
|
async function runEnvCommand(options) {
|
|
1067
|
-
const repositoryPath = await resolveRepositoryPath(options.cwd ?? process.cwd());
|
|
1077
|
+
const repositoryPath = await (0, repository_1.resolveRepositoryPath)(options.cwd ?? process.cwd());
|
|
1068
1078
|
const appName = await resolveAppSelection({ app: options.app, refresh: options.refresh, message: "Select app to export cf env" });
|
|
1069
|
-
const cache = await readCache();
|
|
1070
|
-
const outputFileName = options.out ?? await selectFromHistoryOrInput({
|
|
1079
|
+
const cache = await (0, cache_1.readCache)();
|
|
1080
|
+
const outputFileName = options.out ?? await (0, prompts_2.selectFromHistoryOrInput)({
|
|
1071
1081
|
message: "Select output env file name",
|
|
1072
1082
|
values: cache.cloudFoundry.envFileNames,
|
|
1073
1083
|
initialValue: cache.cloudFoundry.envFileNames[0] ?? "default-env.json",
|
|
1074
1084
|
validate: validateRequired,
|
|
1075
1085
|
});
|
|
1076
|
-
const result = await runCommand("cf", ["env", appName]);
|
|
1086
|
+
const result = await (0, process_1.runCommand)("cf", ["env", appName]);
|
|
1077
1087
|
if (result.exitCode !== 0) {
|
|
1078
1088
|
throw new Error(result.stderr || result.stdout || "cf env failed");
|
|
1079
1089
|
}
|
|
1080
|
-
const outputPath =
|
|
1090
|
+
const outputPath = node_path_1.default.resolve(repositoryPath, outputFileName);
|
|
1081
1091
|
if (options.raw) {
|
|
1082
|
-
await
|
|
1092
|
+
await fs_extra_1.default.writeFile(outputPath, result.stdout, "utf8");
|
|
1083
1093
|
}
|
|
1084
1094
|
else {
|
|
1085
|
-
const parsedEnvironment = parseCloudFoundryEnvironment(result.stdout);
|
|
1086
|
-
await
|
|
1095
|
+
const parsedEnvironment = (0, cf_env_parser_1.parseCloudFoundryEnvironment)(result.stdout);
|
|
1096
|
+
await fs_extra_1.default.writeJson(outputPath, parsedEnvironment, { spaces: 2 });
|
|
1087
1097
|
}
|
|
1088
|
-
await rememberSelectedApp(appName);
|
|
1089
|
-
await rememberEnvironmentFileName(outputFileName);
|
|
1090
|
-
console.log(
|
|
1098
|
+
await (0, cache_1.rememberSelectedApp)(appName);
|
|
1099
|
+
await (0, cache_1.rememberEnvironmentFileName)(outputFileName);
|
|
1100
|
+
console.log(chalk_1.default.green(`Exported ${options.raw ? "raw env" : "clean JSON env"} to ${outputPath}`));
|
|
1091
1101
|
}
|
|
1092
1102
|
async function runLogsCommand(options) {
|
|
1093
1103
|
const appName = await resolveAppSelection({ app: options.app, refresh: options.refresh, message: "Select app to view logs" });
|
|
1094
1104
|
const shouldFollow = options.follow || !options.recent;
|
|
1095
1105
|
const shouldReadRecent = options.recent || !shouldFollow;
|
|
1096
|
-
const outputPath = options.out ?
|
|
1106
|
+
const outputPath = options.out ? node_path_1.default.resolve(process.cwd(), options.out) : undefined;
|
|
1097
1107
|
if (outputPath) {
|
|
1098
|
-
await
|
|
1108
|
+
await fs_extra_1.default.ensureDir(node_path_1.default.dirname(outputPath));
|
|
1099
1109
|
}
|
|
1100
1110
|
if (shouldReadRecent && !shouldFollow) {
|
|
1101
|
-
const result = await runCommand("cf", buildCloudFoundryLogsArgs({ appName, recent: true }));
|
|
1111
|
+
const result = await (0, process_1.runCommand)("cf", buildCloudFoundryLogsArgs({ appName, recent: true }));
|
|
1102
1112
|
const combinedOutput = [result.stdout, result.stderr].filter(Boolean).join("\n");
|
|
1103
1113
|
const filteredOutput = filterCloudFoundryLogsOutput(combinedOutput, {
|
|
1104
1114
|
instance: options.instance,
|
|
1105
1115
|
process: options.process,
|
|
1106
1116
|
});
|
|
1107
1117
|
if (outputPath) {
|
|
1108
|
-
await
|
|
1109
|
-
console.log(
|
|
1118
|
+
await fs_extra_1.default.writeFile(outputPath, filteredOutput.endsWith("\n") ? filteredOutput : `${filteredOutput}\n`, "utf8");
|
|
1119
|
+
console.log(chalk_1.default.green(`Exported recent logs to ${outputPath}`));
|
|
1110
1120
|
}
|
|
1111
1121
|
else {
|
|
1112
1122
|
console.log(filteredOutput);
|
|
@@ -1114,12 +1124,12 @@ async function runLogsCommand(options) {
|
|
|
1114
1124
|
process.exitCode = result.exitCode;
|
|
1115
1125
|
return;
|
|
1116
1126
|
}
|
|
1117
|
-
const outputStream = outputPath ?
|
|
1127
|
+
const outputStream = outputPath ? node_fs_1.default.createWriteStream(outputPath, { flags: "a" }) : undefined;
|
|
1118
1128
|
if (outputPath) {
|
|
1119
|
-
console.log(
|
|
1129
|
+
console.log(chalk_1.default.gray(`Streaming logs and appending to ${outputPath}`));
|
|
1120
1130
|
}
|
|
1121
|
-
console.log(
|
|
1122
|
-
const childProcess = spawn("cf", buildCloudFoundryLogsArgs({ appName, recent: false }), {
|
|
1131
|
+
console.log(chalk_1.default.gray("Press Ctrl+C to stop realtime logs."));
|
|
1132
|
+
const childProcess = (0, node_child_process_1.spawn)("cf", buildCloudFoundryLogsArgs({ appName, recent: false }), {
|
|
1123
1133
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1124
1134
|
shell: false,
|
|
1125
1135
|
windowsHide: true,
|
|
@@ -1179,13 +1189,13 @@ async function waitForNodeInspectorWebSocketUrl(localPort, timeoutMs = 15000) {
|
|
|
1179
1189
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1180
1190
|
}
|
|
1181
1191
|
if (lastError instanceof Error) {
|
|
1182
|
-
console.log(
|
|
1192
|
+
console.log(chalk_1.default.gray(`Could not read inspector WebSocket yet: ${lastError.message}`));
|
|
1183
1193
|
}
|
|
1184
1194
|
return undefined;
|
|
1185
1195
|
}
|
|
1186
1196
|
function encodeWebSocketFrame(payload) {
|
|
1187
1197
|
const payloadBuffer = Buffer.from(payload, "utf8");
|
|
1188
|
-
const maskKey =
|
|
1198
|
+
const maskKey = node_crypto_1.default.randomBytes(4);
|
|
1189
1199
|
let header;
|
|
1190
1200
|
if (payloadBuffer.length < 126) {
|
|
1191
1201
|
header = Buffer.alloc(2);
|
|
@@ -1260,7 +1270,7 @@ function decodeWebSocketFrames(buffer) {
|
|
|
1260
1270
|
async function sendInspectorEvaluateCommand(options) {
|
|
1261
1271
|
const connection = parseWebSocketUrl(options.webSocketUrl);
|
|
1262
1272
|
const timeoutMs = options.timeoutMs ?? 10000;
|
|
1263
|
-
const key =
|
|
1273
|
+
const key = node_crypto_1.default.randomBytes(16).toString("base64");
|
|
1264
1274
|
const request = [
|
|
1265
1275
|
`GET ${connection.path} HTTP/1.1`,
|
|
1266
1276
|
`Host: ${connection.host}:${connection.port}`,
|
|
@@ -1272,7 +1282,7 @@ async function sendInspectorEvaluateCommand(options) {
|
|
|
1272
1282
|
"",
|
|
1273
1283
|
].join("\r\n");
|
|
1274
1284
|
await new Promise((resolve, reject) => {
|
|
1275
|
-
const socket =
|
|
1285
|
+
const socket = node_net_1.default.createConnection({ host: connection.host, port: connection.port });
|
|
1276
1286
|
const commandId = 1;
|
|
1277
1287
|
let isHandshakeComplete = false;
|
|
1278
1288
|
let handshakeBuffer = Buffer.alloc(0);
|
|
@@ -1411,16 +1421,16 @@ function parseHttpWatchLine(line) {
|
|
|
1411
1421
|
return parseHttpWatchAppLine(line) ?? parseHttpWatchRouterLine(line);
|
|
1412
1422
|
}
|
|
1413
1423
|
function formatHttpWatchEvent(appName, event) {
|
|
1414
|
-
const status = event.status ?
|
|
1415
|
-
const duration = event.durationMs !== undefined ?
|
|
1416
|
-
const source = event.source === "RTR" ?
|
|
1417
|
-
const requestId = event.requestId ?
|
|
1418
|
-
const instance = event.instance ?
|
|
1419
|
-
const user = event.user ?
|
|
1420
|
-
const tenant = event.tenant ?
|
|
1421
|
-
const size = event.contentLength || event.requestBytes ?
|
|
1422
|
-
const auth = event.authorization ?
|
|
1423
|
-
return `${source} ${
|
|
1424
|
+
const status = event.status ? chalk_1.default.green(String(event.status)) : chalk_1.default.gray("APP");
|
|
1425
|
+
const duration = event.durationMs !== undefined ? chalk_1.default.gray(`${event.durationMs}ms`) : "";
|
|
1426
|
+
const source = event.source === "RTR" ? chalk_1.default.magenta("RTR") : chalk_1.default.blue("APP");
|
|
1427
|
+
const requestId = event.requestId ? chalk_1.default.gray(` req=${event.requestId}`) : "";
|
|
1428
|
+
const instance = event.instance ? chalk_1.default.gray(` i=${event.instance}`) : "";
|
|
1429
|
+
const user = event.user ? chalk_1.default.gray(` user=${event.user}`) : "";
|
|
1430
|
+
const tenant = event.tenant ? chalk_1.default.gray(` tenant=${event.tenant}`) : "";
|
|
1431
|
+
const size = event.contentLength || event.requestBytes ? chalk_1.default.gray(` bytes=${event.contentLength || event.requestBytes}`) : "";
|
|
1432
|
+
const auth = event.authorization ? chalk_1.default.gray(` auth=${event.authorization}`) : "";
|
|
1433
|
+
return `${source} ${chalk_1.default.cyan(`[${appName}]`)} ${status} ${chalk_1.default.bold(event.method ?? "")} ${event.url ?? ""} ${duration}${instance}${user}${tenant}${size}${auth}${requestId}`.trim();
|
|
1424
1434
|
}
|
|
1425
1435
|
function printHttpWatchLine(appName, line, outputFile) {
|
|
1426
1436
|
const event = parseHttpWatchLine(line);
|
|
@@ -1430,7 +1440,7 @@ function printHttpWatchLine(appName, line, outputFile) {
|
|
|
1430
1440
|
console.log(formatted);
|
|
1431
1441
|
if (outputFile) {
|
|
1432
1442
|
const plain = formatted.replace(/\u001b\[[0-9;]*m/g, "");
|
|
1433
|
-
|
|
1443
|
+
fs_extra_1.default.appendFileSync(outputFile, `${plain}\n`, "utf8");
|
|
1434
1444
|
}
|
|
1435
1445
|
}
|
|
1436
1446
|
async function resolveHttpWatchApps(options) {
|
|
@@ -1443,12 +1453,12 @@ async function runHttpWatchForApps(options) {
|
|
|
1443
1453
|
if (!options.appNames.length)
|
|
1444
1454
|
throw new Error("No app selected for HTTP watch");
|
|
1445
1455
|
if (options.out) {
|
|
1446
|
-
await
|
|
1447
|
-
await
|
|
1456
|
+
await fs_extra_1.default.ensureDir(node_path_1.default.dirname(node_path_1.default.resolve(options.out)));
|
|
1457
|
+
await fs_extra_1.default.writeFile(options.out, "", "utf8");
|
|
1448
1458
|
}
|
|
1449
1459
|
if (options.recent) {
|
|
1450
1460
|
for (const appName of options.appNames) {
|
|
1451
|
-
const result = await runCommand("cf", ["logs", appName, "--recent"]);
|
|
1461
|
+
const result = await (0, process_1.runCommand)("cf", ["logs", appName, "--recent"]);
|
|
1452
1462
|
const text = `${result.stdout}\n${result.stderr}`;
|
|
1453
1463
|
for (const line of text.split(/\r?\n/)) {
|
|
1454
1464
|
printHttpWatchLine(appName, line, options.out);
|
|
@@ -1464,12 +1474,12 @@ async function runHttpWatchForApps(options) {
|
|
|
1464
1474
|
}
|
|
1465
1475
|
};
|
|
1466
1476
|
process.once("SIGINT", () => {
|
|
1467
|
-
console.log(
|
|
1477
|
+
console.log(chalk_1.default.gray("\nStopping HTTP watch..."));
|
|
1468
1478
|
stopAll();
|
|
1469
1479
|
process.exit(0);
|
|
1470
1480
|
});
|
|
1471
1481
|
for (const appName of options.appNames) {
|
|
1472
|
-
const child = spawn("cf", ["logs", appName], {
|
|
1482
|
+
const child = (0, node_child_process_1.spawn)("cf", ["logs", appName], {
|
|
1473
1483
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1474
1484
|
shell: false,
|
|
1475
1485
|
windowsHide: true,
|
|
@@ -1486,9 +1496,9 @@ async function runHttpWatchForApps(options) {
|
|
|
1486
1496
|
}
|
|
1487
1497
|
});
|
|
1488
1498
|
}
|
|
1489
|
-
console.log(
|
|
1490
|
-
console.log(
|
|
1491
|
-
console.log(
|
|
1499
|
+
console.log(chalk_1.default.green(`HTTP watch is watching ${options.appNames.length} app(s).`));
|
|
1500
|
+
console.log(chalk_1.default.gray("This uses existing CF/CDS/RTR logs. It shows method/path/status/user/tenant/size, but not full request body or full token."));
|
|
1501
|
+
console.log(chalk_1.default.gray("Press Ctrl+C to stop."));
|
|
1492
1502
|
await new Promise((resolve) => {
|
|
1493
1503
|
let closedCount = 0;
|
|
1494
1504
|
for (const child of children) {
|
|
@@ -1522,9 +1532,9 @@ async function runRequestTraceDoctorCommand(options) {
|
|
|
1522
1532
|
const appNames = await resolveRequestTraceApps({ app: options.app, refresh: options.refresh });
|
|
1523
1533
|
const instanceIndex = await selectDebugInstance({ instance: options.instance });
|
|
1524
1534
|
for (const appName of appNames) {
|
|
1525
|
-
console.log(
|
|
1526
|
-
console.log(
|
|
1527
|
-
const result = await runCommand("cf", ["logs", appName, "--recent"]);
|
|
1535
|
+
console.log(chalk_1.default.cyan(`\nRequest trace doctor for ${appName} instance ${instanceIndex}`));
|
|
1536
|
+
console.log(chalk_1.default.gray("Recent router/app HTTP traffic:"));
|
|
1537
|
+
const result = await (0, process_1.runCommand)("cf", ["logs", appName, "--recent"]);
|
|
1528
1538
|
const text = `${result.stdout}\n${result.stderr}`;
|
|
1529
1539
|
let count = 0;
|
|
1530
1540
|
for (const line of text.split(/\r?\n/)) {
|
|
@@ -1537,16 +1547,16 @@ async function runRequestTraceDoctorCommand(options) {
|
|
|
1537
1547
|
}
|
|
1538
1548
|
}
|
|
1539
1549
|
if (!count) {
|
|
1540
|
-
console.log(
|
|
1550
|
+
console.log(chalk_1.default.yellow("No recent HTTP traffic found in CF logs for this app."));
|
|
1541
1551
|
}
|
|
1542
|
-
console.log(
|
|
1543
|
-
const processList = await runCommand("cf", ["ssh", appName, "-i", instanceIndex, "-T", "-c", "ps -eo pid,args 2>/dev/null | head -n 40"]);
|
|
1552
|
+
console.log(chalk_1.default.gray("\nRemote process list:"));
|
|
1553
|
+
const processList = await (0, process_1.runCommand)("cf", ["ssh", appName, "-i", instanceIndex, "-T", "-c", "ps -eo pid,args 2>/dev/null | head -n 40"]);
|
|
1544
1554
|
if (processList.stdout)
|
|
1545
1555
|
console.log(processList.stdout);
|
|
1546
1556
|
if (processList.stderr)
|
|
1547
1557
|
console.error(processList.stderr);
|
|
1548
1558
|
}
|
|
1549
|
-
console.log(
|
|
1559
|
+
console.log(chalk_1.default.yellow("\nDoctor summary:"));
|
|
1550
1560
|
console.log("- If HTTP traffic appears above, the app is receiving requests.");
|
|
1551
1561
|
console.log("- Full body/token are not available from CF/CDS logs because they are intentionally omitted or masked.");
|
|
1552
1562
|
console.log("- Use smdg cf http-watch for stable live tracking.");
|
|
@@ -1888,7 +1898,7 @@ function buildRequestTraceInjectionExpression(options) {
|
|
|
1888
1898
|
return source;
|
|
1889
1899
|
}
|
|
1890
1900
|
async function selectRequestTraceMode() {
|
|
1891
|
-
return searchableSelectChoice({
|
|
1901
|
+
return (0, prompts_2.searchableSelectChoice)({
|
|
1892
1902
|
message: "Select request trace mode",
|
|
1893
1903
|
choices: [
|
|
1894
1904
|
{ title: "Path only", value: "path", description: "method, URL, status, duration" },
|
|
@@ -1900,7 +1910,7 @@ async function selectRequestTraceMode() {
|
|
|
1900
1910
|
});
|
|
1901
1911
|
}
|
|
1902
1912
|
async function selectRequestTraceAuthMode() {
|
|
1903
|
-
return searchableSelectChoice({
|
|
1913
|
+
return (0, prompts_2.searchableSelectChoice)({
|
|
1904
1914
|
message: "Authorization header handling",
|
|
1905
1915
|
choices: [
|
|
1906
1916
|
{ title: "Mask token (recommended)", value: "mask" },
|
|
@@ -1911,7 +1921,7 @@ async function selectRequestTraceAuthMode() {
|
|
|
1911
1921
|
});
|
|
1912
1922
|
}
|
|
1913
1923
|
async function selectRequestTraceDisplayOptions(options) {
|
|
1914
|
-
const headerPreset = await searchableSelectChoice({
|
|
1924
|
+
const headerPreset = await (0, prompts_2.searchableSelectChoice)({
|
|
1915
1925
|
message: "Headers to display in terminal",
|
|
1916
1926
|
choices: [
|
|
1917
1927
|
{ title: "Minimal headers", value: "minimal", description: "host, content-type, authorization, request/correlation ids" },
|
|
@@ -1927,7 +1937,7 @@ async function selectRequestTraceDisplayOptions(options) {
|
|
|
1927
1937
|
if (headerPreset === "common")
|
|
1928
1938
|
headerNames = getCommonTraceHeaderNames();
|
|
1929
1939
|
if (headerPreset === "custom") {
|
|
1930
|
-
const response = await
|
|
1940
|
+
const response = await (0, prompts_1.default)({
|
|
1931
1941
|
type: "text",
|
|
1932
1942
|
name: "headers",
|
|
1933
1943
|
message: "Header names to display",
|
|
@@ -1936,7 +1946,7 @@ async function selectRequestTraceDisplayOptions(options) {
|
|
|
1936
1946
|
});
|
|
1937
1947
|
headerNames = String(response.headers ?? "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
1938
1948
|
}
|
|
1939
|
-
const parseResponse = await
|
|
1949
|
+
const parseResponse = await (0, prompts_1.default)({
|
|
1940
1950
|
type: "select",
|
|
1941
1951
|
name: "parseBodyJson",
|
|
1942
1952
|
message: "Try parse request/response body as JSON when possible?",
|
|
@@ -1948,7 +1958,7 @@ async function selectRequestTraceDisplayOptions(options) {
|
|
|
1948
1958
|
});
|
|
1949
1959
|
let outputFile = options.out;
|
|
1950
1960
|
if (!outputFile) {
|
|
1951
|
-
const outResponse = await
|
|
1961
|
+
const outResponse = await (0, prompts_1.default)({
|
|
1952
1962
|
type: "select",
|
|
1953
1963
|
name: "export",
|
|
1954
1964
|
message: "Export captured trace events to JSONL file?",
|
|
@@ -1959,7 +1969,7 @@ async function selectRequestTraceDisplayOptions(options) {
|
|
|
1959
1969
|
initial: 0,
|
|
1960
1970
|
});
|
|
1961
1971
|
if (outResponse.export) {
|
|
1962
|
-
const fileResponse = await
|
|
1972
|
+
const fileResponse = await (0, prompts_1.default)({
|
|
1963
1973
|
type: "text",
|
|
1964
1974
|
name: "file",
|
|
1965
1975
|
message: "Trace output file",
|
|
@@ -1970,9 +1980,9 @@ async function selectRequestTraceDisplayOptions(options) {
|
|
|
1970
1980
|
}
|
|
1971
1981
|
}
|
|
1972
1982
|
if (outputFile) {
|
|
1973
|
-
await
|
|
1974
|
-
await
|
|
1975
|
-
console.log(
|
|
1983
|
+
await fs_extra_1.default.ensureDir(node_path_1.default.dirname(node_path_1.default.resolve(outputFile)));
|
|
1984
|
+
await fs_extra_1.default.writeFile(outputFile, "", "utf8");
|
|
1985
|
+
console.log(chalk_1.default.green(`Trace events will be exported to ${node_path_1.default.resolve(outputFile)}`));
|
|
1976
1986
|
}
|
|
1977
1987
|
return {
|
|
1978
1988
|
headerPreset,
|
|
@@ -1988,7 +1998,7 @@ async function resolveRequestTraceApps(options) {
|
|
|
1988
1998
|
const apps = await getAppsWithCache({ refresh: options.refresh, startBackgroundRefresh: !options.refresh });
|
|
1989
1999
|
const selectedApps = [];
|
|
1990
2000
|
while (true) {
|
|
1991
|
-
const appName = await searchableSelectChoice({
|
|
2001
|
+
const appName = await (0, prompts_2.searchableSelectChoice)({
|
|
1992
2002
|
message: selectedApps.length ? "Add another BTP app to trace, or finish" : "Search/select BTP app to trace",
|
|
1993
2003
|
choices: [
|
|
1994
2004
|
...apps
|
|
@@ -2006,8 +2016,8 @@ async function resolveRequestTraceApps(options) {
|
|
|
2006
2016
|
break;
|
|
2007
2017
|
}
|
|
2008
2018
|
selectedApps.push(appName);
|
|
2009
|
-
await rememberSelectedApp(appName);
|
|
2010
|
-
const moreResponse = await
|
|
2019
|
+
await (0, cache_1.rememberSelectedApp)(appName);
|
|
2020
|
+
const moreResponse = await (0, prompts_1.default)({
|
|
2011
2021
|
type: "select",
|
|
2012
2022
|
name: "more",
|
|
2013
2023
|
message: "Trace another app at the same time?",
|
|
@@ -2130,21 +2140,21 @@ function writeTraceEventToFile(outputFile, event) {
|
|
|
2130
2140
|
if (!outputFile)
|
|
2131
2141
|
return;
|
|
2132
2142
|
try {
|
|
2133
|
-
|
|
2143
|
+
fs_extra_1.default.appendFileSync(outputFile, `${JSON.stringify(event)}\n`, "utf8");
|
|
2134
2144
|
}
|
|
2135
2145
|
catch (error) {
|
|
2136
|
-
console.error(
|
|
2146
|
+
console.error(chalk_1.default.yellow(`Failed to write trace event to file: ${error instanceof Error ? error.message : String(error)}`));
|
|
2137
2147
|
}
|
|
2138
2148
|
}
|
|
2139
2149
|
function printRequestTraceEvent(appName, payload, runtime) {
|
|
2140
2150
|
const type = String(payload.type ?? "smdg-request-trace");
|
|
2141
2151
|
if (type === "smdg-request-trace-status") {
|
|
2142
|
-
console.log(
|
|
2143
|
-
console.log(
|
|
2152
|
+
console.log(chalk_1.default.green(`[${appName}] ${String(payload.status ?? "trace-status")}`));
|
|
2153
|
+
console.log(chalk_1.default.gray(`engine=${String(payload.engine ?? "unknown")} activeServers=${String(payload.activeServers ?? "?")} mode=${String(payload.mode ?? "")}`));
|
|
2144
2154
|
return;
|
|
2145
2155
|
}
|
|
2146
2156
|
if (type === "smdg-request-trace-error") {
|
|
2147
|
-
console.log(
|
|
2157
|
+
console.log(chalk_1.default.red(`[${appName}] trace error: ${String(payload.message ?? "unknown")}`));
|
|
2148
2158
|
return;
|
|
2149
2159
|
}
|
|
2150
2160
|
runtime.events.push(payload);
|
|
@@ -2156,7 +2166,7 @@ function printRequestTraceEvent(appName, payload, runtime) {
|
|
|
2156
2166
|
const url = String(payload.url ?? "");
|
|
2157
2167
|
const status = String(payload.status ?? "");
|
|
2158
2168
|
const duration = String(payload.durationMs ?? "");
|
|
2159
|
-
console.log(
|
|
2169
|
+
console.log(chalk_1.default.cyan(`\n[${time}] [${appName}] ${method} ${url} → ${status} ${duration}ms`));
|
|
2160
2170
|
console.log(JSON.stringify(buildPrintableTracePayload(payload, runtime.display), null, 2));
|
|
2161
2171
|
}
|
|
2162
2172
|
function printRequestTraceLine(appName, line, runtime) {
|
|
@@ -2174,19 +2184,19 @@ function printRequestTraceLine(appName, line, runtime) {
|
|
|
2174
2184
|
}
|
|
2175
2185
|
}
|
|
2176
2186
|
function printTraceRuntimeHelp() {
|
|
2177
|
-
console.log(
|
|
2178
|
-
console.log(
|
|
2179
|
-
console.log(
|
|
2180
|
-
console.log(
|
|
2181
|
-
console.log(
|
|
2182
|
-
console.log(
|
|
2183
|
-
console.log(
|
|
2184
|
-
console.log(
|
|
2185
|
-
console.log(
|
|
2186
|
-
console.log(
|
|
2187
|
-
console.log(
|
|
2188
|
-
console.log(
|
|
2189
|
-
console.log(
|
|
2187
|
+
console.log(chalk_1.default.gray("\nRuntime trace commands:"));
|
|
2188
|
+
console.log(chalk_1.default.gray(" /method POST show only one method"));
|
|
2189
|
+
console.log(chalk_1.default.gray(" /path text show only URLs containing text"));
|
|
2190
|
+
console.log(chalk_1.default.gray(" /body text show only request/response body containing text"));
|
|
2191
|
+
console.log(chalk_1.default.gray(" /status 500 show only status containing value"));
|
|
2192
|
+
console.log(chalk_1.default.gray(" /text value search anywhere in the event"));
|
|
2193
|
+
console.log(chalk_1.default.gray(" /headers a,b,c change displayed headers while running"));
|
|
2194
|
+
console.log(chalk_1.default.gray(" /headers all display all captured headers"));
|
|
2195
|
+
console.log(chalk_1.default.gray(" /clear clear active filters"));
|
|
2196
|
+
console.log(chalk_1.default.gray(" /show show active filters"));
|
|
2197
|
+
console.log(chalk_1.default.gray(" /replay print matching events already captured"));
|
|
2198
|
+
console.log(chalk_1.default.gray(" /pause or /resume pause/resume terminal display"));
|
|
2199
|
+
console.log(chalk_1.default.gray(" /help show this help"));
|
|
2190
2200
|
}
|
|
2191
2201
|
function applyTraceRuntimeCommand(input, runtime) {
|
|
2192
2202
|
const trimmed = input.trim();
|
|
@@ -2194,7 +2204,7 @@ function applyTraceRuntimeCommand(input, runtime) {
|
|
|
2194
2204
|
return;
|
|
2195
2205
|
if (!trimmed.startsWith("/")) {
|
|
2196
2206
|
runtime.filters.text = trimmed;
|
|
2197
|
-
console.log(
|
|
2207
|
+
console.log(chalk_1.default.yellow(`Search text filter: ${trimmed}`));
|
|
2198
2208
|
return;
|
|
2199
2209
|
}
|
|
2200
2210
|
const [commandRaw, ...restParts] = trimmed.slice(1).split(" ");
|
|
@@ -2241,16 +2251,16 @@ function applyTraceRuntimeCommand(input, runtime) {
|
|
|
2241
2251
|
}
|
|
2242
2252
|
}
|
|
2243
2253
|
else if (command === "show") {
|
|
2244
|
-
console.log(
|
|
2254
|
+
console.log(chalk_1.default.gray(JSON.stringify({ filters: runtime.filters, display: runtime.display, captured: runtime.events.length }, null, 2)));
|
|
2245
2255
|
return;
|
|
2246
2256
|
}
|
|
2247
2257
|
else if (command === "replay") {
|
|
2248
|
-
console.log(
|
|
2258
|
+
console.log(chalk_1.default.gray(`Replaying ${runtime.events.length} captured event(s) with current filters...`));
|
|
2249
2259
|
for (const event of runtime.events) {
|
|
2250
2260
|
if (traceEventMatchesFilters(event, runtime.filters)) {
|
|
2251
2261
|
const appName = String(event.app ?? "app");
|
|
2252
2262
|
const time = String(event.timestamp ?? "");
|
|
2253
|
-
console.log(
|
|
2263
|
+
console.log(chalk_1.default.cyan(`\n[${time}] [${appName}] ${String(event.method ?? "")} ${String(event.url ?? "")} → ${String(event.status ?? "")} ${String(event.durationMs ?? "")}ms`));
|
|
2254
2264
|
console.log(JSON.stringify(buildPrintableTracePayload(event, runtime.display), null, 2));
|
|
2255
2265
|
}
|
|
2256
2266
|
}
|
|
@@ -2261,11 +2271,11 @@ function applyTraceRuntimeCommand(input, runtime) {
|
|
|
2261
2271
|
return;
|
|
2262
2272
|
}
|
|
2263
2273
|
else {
|
|
2264
|
-
console.log(
|
|
2274
|
+
console.log(chalk_1.default.yellow(`Unknown runtime command: ${command}`));
|
|
2265
2275
|
printTraceRuntimeHelp();
|
|
2266
2276
|
return;
|
|
2267
2277
|
}
|
|
2268
|
-
console.log(
|
|
2278
|
+
console.log(chalk_1.default.yellow(`Trace runtime updated: ${trimmed}`));
|
|
2269
2279
|
}
|
|
2270
2280
|
function attachTraceRuntimeCommands(runtime) {
|
|
2271
2281
|
printTraceRuntimeHelp();
|
|
@@ -2278,7 +2288,7 @@ function attachTraceRuntimeCommands(runtime) {
|
|
|
2278
2288
|
});
|
|
2279
2289
|
}
|
|
2280
2290
|
function startRequestTraceLogStream(appName, runtime) {
|
|
2281
|
-
const childProcess = spawn("cf", ["logs", appName], {
|
|
2291
|
+
const childProcess = (0, node_child_process_1.spawn)("cf", ["logs", appName], {
|
|
2282
2292
|
stdio: ["ignore", "pipe", "pipe"],
|
|
2283
2293
|
shell: false,
|
|
2284
2294
|
windowsHide: true,
|
|
@@ -2312,7 +2322,7 @@ async function runRequestTraceCommand(options) {
|
|
|
2312
2322
|
if (!appNames.length) {
|
|
2313
2323
|
throw new Error("No app selected for request trace");
|
|
2314
2324
|
}
|
|
2315
|
-
const engine = await searchableSelectChoice({
|
|
2325
|
+
const engine = await (0, prompts_2.searchableSelectChoice)({
|
|
2316
2326
|
message: "Select request trace engine",
|
|
2317
2327
|
choices: [
|
|
2318
2328
|
{
|
|
@@ -2357,8 +2367,8 @@ async function runRequestTraceCommand(options) {
|
|
|
2357
2367
|
const remotePort = parsePositivePort(options.remotePort, 9229);
|
|
2358
2368
|
const maxBodyBytes = parsePositivePort(options.maxBodyBytes, 20000);
|
|
2359
2369
|
console.log("");
|
|
2360
|
-
console.log(
|
|
2361
|
-
console.log(
|
|
2370
|
+
console.log(chalk_1.default.yellow("Request trace attaches to the running Node.js app through Node Inspector."));
|
|
2371
|
+
console.log(chalk_1.default.gray("It does not modify your repository source code. It is temporary and disappears after app restart."));
|
|
2362
2372
|
const prepareMode = await selectNodeInspectorPrepareMode({ appName: appNames.join(", "), remotePort });
|
|
2363
2373
|
const tunnelProcesses = [];
|
|
2364
2374
|
const logProcesses = [];
|
|
@@ -2369,7 +2379,7 @@ async function runRequestTraceCommand(options) {
|
|
|
2369
2379
|
}
|
|
2370
2380
|
};
|
|
2371
2381
|
process.once("SIGINT", () => {
|
|
2372
|
-
console.log(
|
|
2382
|
+
console.log(chalk_1.default.gray("\nStopping request trace..."));
|
|
2373
2383
|
stopAll();
|
|
2374
2384
|
process.exit(0);
|
|
2375
2385
|
});
|
|
@@ -2379,8 +2389,8 @@ async function runRequestTraceCommand(options) {
|
|
|
2379
2389
|
if (prepareMode === "set-env-restart") {
|
|
2380
2390
|
await setNodeInspectorEnvironmentAndRestart({ appName, remotePort });
|
|
2381
2391
|
}
|
|
2382
|
-
console.log(
|
|
2383
|
-
const tunnelProcess = spawn("cf", buildCloudFoundryDebugSshArgs({
|
|
2392
|
+
console.log(chalk_1.default.gray(`Opening inspector tunnel for ${appName}: localhost:${localPort} -> 127.0.0.1:${remotePort}`));
|
|
2393
|
+
const tunnelProcess = (0, node_child_process_1.spawn)("cf", buildCloudFoundryDebugSshArgs({
|
|
2384
2394
|
appName,
|
|
2385
2395
|
instanceIndex,
|
|
2386
2396
|
processName: options.process,
|
|
@@ -2393,12 +2403,12 @@ async function runRequestTraceCommand(options) {
|
|
|
2393
2403
|
windowsHide: true,
|
|
2394
2404
|
});
|
|
2395
2405
|
tunnelProcesses.push(tunnelProcess);
|
|
2396
|
-
tunnelProcess.stdout.on("data", (chunk) => process.stdout.write(
|
|
2397
|
-
tunnelProcess.stderr.on("data", (chunk) => process.stderr.write(
|
|
2406
|
+
tunnelProcess.stdout.on("data", (chunk) => process.stdout.write(chalk_1.default.gray(`[${appName}:ssh] ${chunk.toString("utf8")}`)));
|
|
2407
|
+
tunnelProcess.stderr.on("data", (chunk) => process.stderr.write(chalk_1.default.yellow(`[${appName}:ssh] ${chunk.toString("utf8")}`)));
|
|
2398
2408
|
const webSocketUrl = await waitForNodeInspectorWebSocketUrl(localPort, 20000);
|
|
2399
2409
|
if (!webSocketUrl) {
|
|
2400
|
-
console.log(
|
|
2401
|
-
console.log(
|
|
2410
|
+
console.log(chalk_1.default.red(`Cannot reach Node Inspector for ${appName} on localhost:${localPort}.`));
|
|
2411
|
+
console.log(chalk_1.default.yellow("Try again and choose: Set NODE_OPTIONS and restart app."));
|
|
2402
2412
|
continue;
|
|
2403
2413
|
}
|
|
2404
2414
|
const expression = buildRequestTraceInjectionExpression({
|
|
@@ -2409,13 +2419,13 @@ async function runRequestTraceCommand(options) {
|
|
|
2409
2419
|
parseBodyJson: displayOptions.parseBodyJson,
|
|
2410
2420
|
});
|
|
2411
2421
|
await sendInspectorEvaluateCommand({ webSocketUrl, expression });
|
|
2412
|
-
console.log(
|
|
2422
|
+
console.log(chalk_1.default.green(`Request trace injected into ${appName}.`));
|
|
2413
2423
|
const logProcess = startRequestTraceLogStream(appName, runtime);
|
|
2414
2424
|
logProcesses.push(logProcess);
|
|
2415
2425
|
}
|
|
2416
2426
|
console.log("");
|
|
2417
|
-
console.log(
|
|
2418
|
-
console.log(
|
|
2427
|
+
console.log(chalk_1.default.green(`Request trace is watching ${appNames.length} app(s).`));
|
|
2428
|
+
console.log(chalk_1.default.gray("Send requests to your services. Type /help for runtime search commands. Press Ctrl+C to stop tunnels and log streams."));
|
|
2419
2429
|
attachTraceRuntimeCommands(runtime);
|
|
2420
2430
|
await new Promise((resolve) => {
|
|
2421
2431
|
const watchedProcesses = [...tunnelProcesses, ...logProcesses];
|
|
@@ -2445,9 +2455,9 @@ async function runDebugCommand(options) {
|
|
|
2445
2455
|
defaultPort: 9229,
|
|
2446
2456
|
});
|
|
2447
2457
|
const remotePort = parsePositivePort(options.remotePort, 9229);
|
|
2448
|
-
const repositoryPath = await resolveRepositoryPath(process.cwd()).catch(() => process.cwd());
|
|
2458
|
+
const repositoryPath = await (0, repository_1.resolveRepositoryPath)(process.cwd()).catch(() => process.cwd());
|
|
2449
2459
|
if (debugMode === "check-ssh") {
|
|
2450
|
-
const result = await runCommand("cf", ["ssh-enabled", appName]);
|
|
2460
|
+
const result = await (0, process_1.runCommand)("cf", ["ssh-enabled", appName]);
|
|
2451
2461
|
if (result.stdout)
|
|
2452
2462
|
console.log(result.stdout);
|
|
2453
2463
|
if (result.stderr)
|
|
@@ -2456,7 +2466,7 @@ async function runDebugCommand(options) {
|
|
|
2456
2466
|
return;
|
|
2457
2467
|
}
|
|
2458
2468
|
if (debugMode === "enable-ssh") {
|
|
2459
|
-
const result = await runCommand("cf", ["enable-ssh", appName]);
|
|
2469
|
+
const result = await (0, process_1.runCommand)("cf", ["enable-ssh", appName]);
|
|
2460
2470
|
if (result.stdout)
|
|
2461
2471
|
console.log(result.stdout);
|
|
2462
2472
|
if (result.stderr)
|
|
@@ -2465,7 +2475,7 @@ async function runDebugCommand(options) {
|
|
|
2465
2475
|
process.exitCode = result.exitCode;
|
|
2466
2476
|
return;
|
|
2467
2477
|
}
|
|
2468
|
-
const restartResponse = await
|
|
2478
|
+
const restartResponse = await (0, prompts_1.default)({
|
|
2469
2479
|
type: "select",
|
|
2470
2480
|
name: "restart",
|
|
2471
2481
|
message: "Restart app now so SSH setting takes effect?",
|
|
@@ -2476,11 +2486,11 @@ async function runDebugCommand(options) {
|
|
|
2476
2486
|
initial: 0,
|
|
2477
2487
|
});
|
|
2478
2488
|
if (restartResponse.restart) {
|
|
2479
|
-
const restartExitCode = await runCommandInherit("cf", ["restart", appName]);
|
|
2489
|
+
const restartExitCode = await (0, process_1.runCommandInherit)("cf", ["restart", appName]);
|
|
2480
2490
|
process.exitCode = restartExitCode;
|
|
2481
2491
|
return;
|
|
2482
2492
|
}
|
|
2483
|
-
console.log(
|
|
2493
|
+
console.log(chalk_1.default.yellow(`SSH was enabled. Restart the app before debugging: cf restart ${appName}`));
|
|
2484
2494
|
return;
|
|
2485
2495
|
}
|
|
2486
2496
|
let launchJsonPath;
|
|
@@ -2491,8 +2501,8 @@ async function runDebugCommand(options) {
|
|
|
2491
2501
|
localPort,
|
|
2492
2502
|
remoteRoot: "/home/vcap/app",
|
|
2493
2503
|
});
|
|
2494
|
-
console.log(
|
|
2495
|
-
const openResponse = await
|
|
2504
|
+
console.log(chalk_1.default.green(`Updated VS Code launch config: ${launchJsonPath}`));
|
|
2505
|
+
const openResponse = await (0, prompts_1.default)({
|
|
2496
2506
|
type: "select",
|
|
2497
2507
|
name: "open",
|
|
2498
2508
|
message: "Open current folder in VS Code?",
|
|
@@ -2511,7 +2521,7 @@ async function runDebugCommand(options) {
|
|
|
2511
2521
|
appName,
|
|
2512
2522
|
instanceIndex,
|
|
2513
2523
|
localPort,
|
|
2514
|
-
launchJsonPath: launchJsonPath ??
|
|
2524
|
+
launchJsonPath: launchJsonPath ?? node_path_1.default.resolve(repositoryPath, ".vscode", "launch.json"),
|
|
2515
2525
|
});
|
|
2516
2526
|
return;
|
|
2517
2527
|
}
|
|
@@ -2521,7 +2531,7 @@ async function runDebugCommand(options) {
|
|
|
2521
2531
|
return;
|
|
2522
2532
|
}
|
|
2523
2533
|
if (options.enableSsh) {
|
|
2524
|
-
const result = await runCommand("cf", ["enable-ssh", appName]);
|
|
2534
|
+
const result = await (0, process_1.runCommand)("cf", ["enable-ssh", appName]);
|
|
2525
2535
|
if (result.stdout)
|
|
2526
2536
|
console.log(result.stdout);
|
|
2527
2537
|
if (result.stderr)
|
|
@@ -2531,28 +2541,28 @@ async function runDebugCommand(options) {
|
|
|
2531
2541
|
return;
|
|
2532
2542
|
}
|
|
2533
2543
|
if (options.restart) {
|
|
2534
|
-
const restartExitCode = await runCommandInherit("cf", ["restart", appName]);
|
|
2544
|
+
const restartExitCode = await (0, process_1.runCommandInherit)("cf", ["restart", appName]);
|
|
2535
2545
|
if (restartExitCode !== 0) {
|
|
2536
2546
|
process.exitCode = restartExitCode;
|
|
2537
2547
|
return;
|
|
2538
2548
|
}
|
|
2539
2549
|
}
|
|
2540
2550
|
else {
|
|
2541
|
-
console.log(
|
|
2551
|
+
console.log(chalk_1.default.yellow("SSH was enabled. If cf ssh still fails, restart the app or run: cf restart " + appName));
|
|
2542
2552
|
}
|
|
2543
2553
|
}
|
|
2544
2554
|
console.log("");
|
|
2545
|
-
console.log(
|
|
2546
|
-
console.log(
|
|
2547
|
-
console.log(
|
|
2555
|
+
console.log(chalk_1.default.cyan("BTP debug works by opening a CF SSH tunnel to the Node.js inspector."));
|
|
2556
|
+
console.log(chalk_1.default.gray("If this is the first time debugging this app, choose: Set NODE_OPTIONS and restart app."));
|
|
2557
|
+
console.log(chalk_1.default.gray("If the app was already restarted with NODE_OPTIONS=--inspect, choose: Inspector is already enabled."));
|
|
2548
2558
|
const prepareMode = await selectNodeInspectorPrepareMode({ appName, remotePort });
|
|
2549
2559
|
await ensureSshEnabledForDebug(appName);
|
|
2550
2560
|
if (prepareMode === "set-env-restart") {
|
|
2551
2561
|
await setNodeInspectorEnvironmentAndRestart({ appName, remotePort });
|
|
2552
2562
|
}
|
|
2553
|
-
console.log(
|
|
2554
|
-
console.log(
|
|
2555
|
-
const childProcess = spawn("cf", buildCloudFoundryDebugSshArgs({
|
|
2563
|
+
console.log(chalk_1.default.gray(`Starting Node.js inspector tunnel for ${appName} instance ${instanceIndex}...`));
|
|
2564
|
+
console.log(chalk_1.default.gray(`Forwarding localhost:${localPort} -> app container 127.0.0.1:${remotePort}`));
|
|
2565
|
+
const childProcess = (0, node_child_process_1.spawn)("cf", buildCloudFoundryDebugSshArgs({
|
|
2556
2566
|
appName,
|
|
2557
2567
|
instanceIndex,
|
|
2558
2568
|
processName: options.process,
|
|
@@ -2573,13 +2583,13 @@ async function runDebugCommand(options) {
|
|
|
2573
2583
|
const debugUrl = await waitForNodeInspectorDebugUrl(localPort);
|
|
2574
2584
|
if (debugMode === "vscode") {
|
|
2575
2585
|
if (!debugUrl) {
|
|
2576
|
-
console.log(
|
|
2586
|
+
console.log(chalk_1.default.yellow("Inspector is not reachable yet on localhost. If you selected running-process mode and see a Node PID error, run debug again and choose 'Set NODE_OPTIONS and restart app'."));
|
|
2577
2587
|
}
|
|
2578
2588
|
printVscodeAttachInstructions({
|
|
2579
2589
|
appName,
|
|
2580
2590
|
instanceIndex,
|
|
2581
2591
|
localPort,
|
|
2582
|
-
launchJsonPath: launchJsonPath ??
|
|
2592
|
+
launchJsonPath: launchJsonPath ?? node_path_1.default.resolve(repositoryPath, ".vscode", "launch.json"),
|
|
2583
2593
|
inspectorReady: Boolean(debugUrl),
|
|
2584
2594
|
});
|
|
2585
2595
|
return;
|
|
@@ -2603,9 +2613,9 @@ async function runDebugCommand(options) {
|
|
|
2603
2613
|
clearTimeout(fallbackTimer);
|
|
2604
2614
|
if (!hasPrintedAttachInfo || (exitCode ?? 0) !== 0) {
|
|
2605
2615
|
console.log("");
|
|
2606
|
-
console.log(
|
|
2607
|
-
console.log(
|
|
2608
|
-
console.log(
|
|
2616
|
+
console.log(chalk_1.default.red("Debug tunnel stopped before a working inspector connection was confirmed."));
|
|
2617
|
+
console.log(chalk_1.default.yellow("Run smdg cf debug again and choose 'Set NODE_OPTIONS and restart app' when asked to prepare Node.js inspector."));
|
|
2618
|
+
console.log(chalk_1.default.gray("After the app restarts, choose VS Code guided debugging and start the attach config from VS Code Run and Debug."));
|
|
2609
2619
|
}
|
|
2610
2620
|
process.exitCode = exitCode ?? 0;
|
|
2611
2621
|
});
|
|
@@ -2614,14 +2624,14 @@ async function runDebugCommand(options) {
|
|
|
2614
2624
|
});
|
|
2615
2625
|
}
|
|
2616
2626
|
async function runTargetCommand() {
|
|
2617
|
-
const target = await readCloudFoundryTarget();
|
|
2627
|
+
const target = await (0, cf_1.readCloudFoundryTarget)();
|
|
2618
2628
|
printTarget(target);
|
|
2619
2629
|
}
|
|
2620
2630
|
async function runCacheCommand() {
|
|
2621
|
-
const cache = await readCache();
|
|
2631
|
+
const cache = await (0, cache_1.readCache)();
|
|
2622
2632
|
console.log(JSON.stringify(cache.cloudFoundry, null, 2));
|
|
2623
2633
|
}
|
|
2624
|
-
|
|
2634
|
+
function registerCloudFoundryCommands(program) {
|
|
2625
2635
|
const cfCommand = program.command("cf").description("Cloud Foundry helper commands for SimpleMDG");
|
|
2626
2636
|
cfCommand
|
|
2627
2637
|
.command("login")
|
|
@@ -2737,6 +2747,7 @@ export function registerCloudFoundryCommands(program) {
|
|
|
2737
2747
|
.command("apps-cache-refresh")
|
|
2738
2748
|
.description("Refresh cached cf apps for current target. Internal command used by smdg cf apps.")
|
|
2739
2749
|
.action(runAppsCacheRefreshCommand);
|
|
2750
|
+
(0, cf_db_command_1.registerCloudFoundryDbCommands)(cfCommand);
|
|
2740
2751
|
cfCommand.command("cache").description("Print cached Cloud Foundry values").action(runCacheCommand);
|
|
2741
2752
|
}
|
|
2742
2753
|
//# sourceMappingURL=cf.command.js.map
|