@saptools/cf-debugger 0.2.1 → 0.2.2
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 +66 -29
- package/dist/{chunk-AKQR4Y74.js → chunk-QBB4N2WN.js} +1509 -689
- package/dist/chunk-QBB4N2WN.js.map +1 -0
- package/dist/cli.js +101 -143
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +27 -9
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-AKQR4Y74.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getSession,
|
|
10
10
|
hasTunnelTerminationFailure,
|
|
11
11
|
listSessions,
|
|
12
|
+
parseRestartEnvironment,
|
|
12
13
|
readCurrentCfTarget,
|
|
13
14
|
remainingStartupMs,
|
|
14
15
|
requireCurrentCfRegion,
|
|
@@ -18,81 +19,16 @@ import {
|
|
|
18
19
|
startDebuggerWithinDeadline,
|
|
19
20
|
startupTimeoutError,
|
|
20
21
|
stopAllDebuggers,
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
stopAllExitCode,
|
|
23
|
+
stopDebugger,
|
|
24
|
+
validateApiEndpointOverride
|
|
25
|
+
} from "./chunk-QBB4N2WN.js";
|
|
23
26
|
|
|
24
27
|
// src/cli.ts
|
|
25
28
|
import nodeProcess from "process";
|
|
26
29
|
import { Command } from "commander";
|
|
27
30
|
|
|
28
|
-
//
|
|
29
|
-
var package_default = {
|
|
30
|
-
name: "@saptools/cf-debugger",
|
|
31
|
-
version: "0.2.1",
|
|
32
|
-
description: "Open an SSH debug tunnel to a SAP BTP Cloud Foundry app's Node.js inspector \u2014 from any terminal, no IDE required.",
|
|
33
|
-
type: "module",
|
|
34
|
-
publishConfig: {
|
|
35
|
-
access: "public",
|
|
36
|
-
registry: "https://registry.npmjs.org/"
|
|
37
|
-
},
|
|
38
|
-
bin: {
|
|
39
|
-
"cf-debugger": "dist/cli.js"
|
|
40
|
-
},
|
|
41
|
-
main: "./dist/index.js",
|
|
42
|
-
types: "./dist/index.d.ts",
|
|
43
|
-
exports: {
|
|
44
|
-
".": {
|
|
45
|
-
types: "./dist/index.d.ts",
|
|
46
|
-
import: "./dist/index.js"
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
files: [
|
|
50
|
-
"dist"
|
|
51
|
-
],
|
|
52
|
-
engines: {
|
|
53
|
-
node: ">=20.0.0"
|
|
54
|
-
},
|
|
55
|
-
scripts: {
|
|
56
|
-
build: "tsup",
|
|
57
|
-
typecheck: "tsc --noEmit",
|
|
58
|
-
lint: "eslint src tests",
|
|
59
|
-
"test:unit": "vitest run --coverage",
|
|
60
|
-
"test:e2e": "playwright test"
|
|
61
|
-
},
|
|
62
|
-
keywords: [
|
|
63
|
-
"sap",
|
|
64
|
-
"cloud-foundry",
|
|
65
|
-
"btp",
|
|
66
|
-
"debug",
|
|
67
|
-
"debugger",
|
|
68
|
-
"ssh-tunnel",
|
|
69
|
-
"node-inspector",
|
|
70
|
-
"cli",
|
|
71
|
-
"saptools"
|
|
72
|
-
],
|
|
73
|
-
author: "Dong Tran",
|
|
74
|
-
license: "MIT",
|
|
75
|
-
repository: {
|
|
76
|
-
type: "git",
|
|
77
|
-
url: "git+https://github.com/dongitran/saptools.git",
|
|
78
|
-
directory: "packages/cf-debugger"
|
|
79
|
-
},
|
|
80
|
-
homepage: "https://github.com/dongitran/saptools/tree/main/packages/cf-debugger#readme",
|
|
81
|
-
bugs: {
|
|
82
|
-
url: "https://github.com/dongitran/saptools/issues"
|
|
83
|
-
},
|
|
84
|
-
dependencies: {
|
|
85
|
-
commander: "^13.0.0"
|
|
86
|
-
},
|
|
87
|
-
devDependencies: {
|
|
88
|
-
"@playwright/test": "^1.50.0",
|
|
89
|
-
"@vitest/coverage-v8": "^3.0.0",
|
|
90
|
-
tsup: "^8.3.0",
|
|
91
|
-
vitest: "^3.0.0"
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
// src/cli.ts
|
|
31
|
+
// src/cli-input.ts
|
|
96
32
|
function readRequiredOption(value, flag) {
|
|
97
33
|
if (value === void 0 || value.trim().length === 0) {
|
|
98
34
|
throw new CfDebuggerError("UNSAFE_INPUT", `Missing required option ${flag}.`);
|
|
@@ -119,6 +55,24 @@ function parseSeconds(raw, label) {
|
|
|
119
55
|
const seconds = parseOptionalInteger(raw, label, 1);
|
|
120
56
|
return seconds === void 0 ? void 0 : seconds * 1e3;
|
|
121
57
|
}
|
|
58
|
+
|
|
59
|
+
// src/package-version.ts
|
|
60
|
+
import { readFileSync } from "fs";
|
|
61
|
+
function readPackageVersion() {
|
|
62
|
+
const parsed = JSON.parse(
|
|
63
|
+
readFileSync(new URL("../package.json", import.meta.url), "utf8")
|
|
64
|
+
);
|
|
65
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
66
|
+
throw new CfDebuggerError("PACKAGE_METADATA_INVALID", "Package metadata is not an object.");
|
|
67
|
+
}
|
|
68
|
+
const version = Reflect.get(parsed, "version");
|
|
69
|
+
if (typeof version !== "string" || version.length === 0) {
|
|
70
|
+
throw new CfDebuggerError("PACKAGE_METADATA_INVALID", "Package metadata has no version.");
|
|
71
|
+
}
|
|
72
|
+
return version;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/cli.ts
|
|
122
76
|
var STOP_SCOPE_OPTION_NAMES = [
|
|
123
77
|
"apiEndpoint",
|
|
124
78
|
"app",
|
|
@@ -244,28 +198,14 @@ ${tunnelError.stderr.trim()}
|
|
|
244
198
|
}
|
|
245
199
|
nodeProcess.exit(requestedExitCode ?? code ?? 0);
|
|
246
200
|
}
|
|
247
|
-
function parseRestartEnvironment() {
|
|
248
|
-
const value = nodeProcess.env["CF_DEBUGGER_ALLOW_RESTART"];
|
|
249
|
-
if (value === void 0) {
|
|
250
|
-
return "unset";
|
|
251
|
-
}
|
|
252
|
-
if (value === "0") {
|
|
253
|
-
return "forbid";
|
|
254
|
-
}
|
|
255
|
-
if (value === "1") {
|
|
256
|
-
return "allow";
|
|
257
|
-
}
|
|
258
|
-
throw new CfDebuggerError(
|
|
259
|
-
"UNSAFE_INPUT",
|
|
260
|
-
"CF_DEBUGGER_ALLOW_RESTART must be 0 or 1."
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
201
|
function buildStartOptions(options, key, app, signal, startupTimeoutMs) {
|
|
264
202
|
const preferredPort = parseOptionalInteger(options.port, "port", 1, 65535);
|
|
265
203
|
const remotePort = parseOptionalInteger(options.remotePort, "remotePort", 1, 65535);
|
|
266
204
|
const nodePid = parseOptionalInteger(options.nodePid, "nodePid", 1);
|
|
267
205
|
const tunnelReadyTimeoutMs = parseSeconds(options.timeout, "timeout");
|
|
268
|
-
const restartEnvironment = parseRestartEnvironment(
|
|
206
|
+
const restartEnvironment = parseRestartEnvironment(
|
|
207
|
+
nodeProcess.env["CF_DEBUGGER_ALLOW_RESTART"]
|
|
208
|
+
);
|
|
269
209
|
const allowSshEnableRestart = options.sshEnableRestart !== false && restartEnvironment !== "forbid" && (options.allowSshEnableRestart === true || restartEnvironment === "allow");
|
|
270
210
|
return {
|
|
271
211
|
region: key.region,
|
|
@@ -287,8 +227,14 @@ function buildStartOptions(options, key, app, signal, startupTimeoutMs) {
|
|
|
287
227
|
function deadlineTimedOut(deadline) {
|
|
288
228
|
return remainingStartupMs(deadline.expiresAt) === 0 || deadline.signal.reason instanceof CfDebuggerError && deadline.signal.reason.code === "STARTUP_TIMEOUT";
|
|
289
229
|
}
|
|
230
|
+
function validateStartEndpoint(options) {
|
|
231
|
+
return options.apiEndpoint === void 0 ? options : {
|
|
232
|
+
...options,
|
|
233
|
+
apiEndpoint: validateApiEndpointOverride(options.apiEndpoint)
|
|
234
|
+
};
|
|
235
|
+
}
|
|
290
236
|
async function handleStart(selector, rawOptions) {
|
|
291
|
-
const options = mergeSelector(selector, rawOptions);
|
|
237
|
+
const options = validateStartEndpoint(mergeSelector(selector, rawOptions));
|
|
292
238
|
const app = readRequiredOption(options.app, "--app or selector");
|
|
293
239
|
const verbose = options.verbose ?? false;
|
|
294
240
|
const startupTimeoutMs = resolveStartupTimeoutMs(
|
|
@@ -340,6 +286,38 @@ function currentCfOptions(signal) {
|
|
|
340
286
|
...signal === void 0 ? {} : { signal }
|
|
341
287
|
};
|
|
342
288
|
}
|
|
289
|
+
function currentTargetReadError(error) {
|
|
290
|
+
return new CfDebuggerError(
|
|
291
|
+
"CF_TARGET_FAILED",
|
|
292
|
+
"No current CF target. Run `cf target -o <org> -s <space>` or pass --region/--org/--space.",
|
|
293
|
+
error instanceof Error ? error.message : String(error)
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
function missingCurrentTargetError() {
|
|
297
|
+
return new CfDebuggerError(
|
|
298
|
+
"CF_TARGET_FAILED",
|
|
299
|
+
"No current CF target. Run `cf target -o <org> -s <space>` or pass --region/--org/--space."
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
async function resolveCfScope(region, org, space, signal) {
|
|
303
|
+
if (region !== void 0 && org !== void 0 && space !== void 0) {
|
|
304
|
+
return { region, org, space };
|
|
305
|
+
}
|
|
306
|
+
const current = await readCurrentCfTarget(currentCfOptions(signal)).catch((error) => {
|
|
307
|
+
if (error instanceof CfDebuggerError && (error.code === "ABORTED" || error.code === "STARTUP_TIMEOUT")) {
|
|
308
|
+
throw error;
|
|
309
|
+
}
|
|
310
|
+
throw currentTargetReadError(error);
|
|
311
|
+
});
|
|
312
|
+
if (current === void 0) {
|
|
313
|
+
throw missingCurrentTargetError();
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
region: region ?? requireCurrentCfRegion(current),
|
|
317
|
+
org: org ?? current.org,
|
|
318
|
+
space: space ?? current.space
|
|
319
|
+
};
|
|
320
|
+
}
|
|
343
321
|
async function resolveSessionKey(options, signal) {
|
|
344
322
|
const app = readRequiredOption(options.app, "--app or selector");
|
|
345
323
|
const region = optionalText(options.region);
|
|
@@ -352,38 +330,9 @@ async function resolveSessionKey(options, signal) {
|
|
|
352
330
|
...instance === void 0 ? {} : { instance },
|
|
353
331
|
...nodePid === void 0 ? {} : { nodePid }
|
|
354
332
|
});
|
|
355
|
-
|
|
356
|
-
return {
|
|
357
|
-
region,
|
|
358
|
-
org,
|
|
359
|
-
space,
|
|
360
|
-
app,
|
|
361
|
-
process: target.process,
|
|
362
|
-
instance: target.instance,
|
|
363
|
-
...options.apiEndpoint === void 0 ? {} : { apiEndpoint: options.apiEndpoint },
|
|
364
|
-
...target.nodePid === void 0 ? {} : { nodePid: target.nodePid }
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
const current = await readCurrentCfTarget(currentCfOptions(signal)).catch((error) => {
|
|
368
|
-
if (error instanceof CfDebuggerError && (error.code === "ABORTED" || error.code === "STARTUP_TIMEOUT")) {
|
|
369
|
-
throw error;
|
|
370
|
-
}
|
|
371
|
-
throw new CfDebuggerError(
|
|
372
|
-
"CF_TARGET_FAILED",
|
|
373
|
-
"No current CF target. Run `cf target -o <org> -s <space>` or pass --region/--org/--space.",
|
|
374
|
-
error instanceof Error ? error.message : String(error)
|
|
375
|
-
);
|
|
376
|
-
});
|
|
377
|
-
if (current === void 0) {
|
|
378
|
-
throw new CfDebuggerError(
|
|
379
|
-
"CF_TARGET_FAILED",
|
|
380
|
-
"No current CF target. Run `cf target -o <org> -s <space>` or pass --region/--org/--space."
|
|
381
|
-
);
|
|
382
|
-
}
|
|
333
|
+
const scope = await resolveCfScope(region, org, space, signal);
|
|
383
334
|
return {
|
|
384
|
-
|
|
385
|
-
org: org ?? current.org,
|
|
386
|
-
space: space ?? current.space,
|
|
335
|
+
...scope,
|
|
387
336
|
app,
|
|
388
337
|
process: target.process,
|
|
389
338
|
instance: target.instance,
|
|
@@ -394,7 +343,7 @@ async function resolveSessionKey(options, signal) {
|
|
|
394
343
|
async function resolveOptionalSessionKey(options) {
|
|
395
344
|
if (optionalText(options.app) === void 0) {
|
|
396
345
|
if (optionalText(options.region) !== void 0 || optionalText(options.org) !== void 0 || optionalText(options.space) !== void 0) {
|
|
397
|
-
readRequiredOption(options.app, "--app");
|
|
346
|
+
readRequiredOption(options.app, "--app or selector");
|
|
398
347
|
}
|
|
399
348
|
return void 0;
|
|
400
349
|
}
|
|
@@ -425,33 +374,42 @@ function writeStopResult(result) {
|
|
|
425
374
|
);
|
|
426
375
|
}
|
|
427
376
|
}
|
|
428
|
-
|
|
429
|
-
if (
|
|
377
|
+
function rejectScopedStopAll(selector, options, command) {
|
|
378
|
+
if (options.all === true && (selector !== void 0 || STOP_SCOPE_OPTION_NAMES.some((name) => command.getOptionValueSource(name) === "cli"))) {
|
|
430
379
|
throw new CfDebuggerError(
|
|
431
380
|
"UNSAFE_INPUT",
|
|
432
381
|
"--all cannot be combined with a selector, --session-id, or target selector options."
|
|
433
382
|
);
|
|
434
383
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
384
|
+
}
|
|
385
|
+
async function handleStopAll(force) {
|
|
386
|
+
const summary = await stopAllDebuggers(force);
|
|
387
|
+
for (const outcome of summary.outcomes) {
|
|
388
|
+
if (outcome.error === void 0) {
|
|
389
|
+
writeStopResult(outcome.result);
|
|
390
|
+
} else {
|
|
391
|
+
nodeProcess.stderr.write(
|
|
392
|
+
`Failed ${outcome.sessionId} (${outcome.app}): ${outcome.error.message}
|
|
444
393
|
`
|
|
445
|
-
|
|
446
|
-
}
|
|
394
|
+
);
|
|
447
395
|
}
|
|
448
|
-
|
|
449
|
-
|
|
396
|
+
}
|
|
397
|
+
nodeProcess.stdout.write(
|
|
398
|
+
`Stop summary: ${summary.stopped.toString()} stopped, ${summary.forced.toString()} forced, ${summary.stale.toString()} stale, ${summary.pending.toString()} pending, ${summary.failed.toString()} failed.
|
|
450
399
|
`
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
400
|
+
);
|
|
401
|
+
const exitCode = stopAllExitCode(
|
|
402
|
+
summary.outcomes.flatMap((outcome) => outcome.error === void 0 ? [] : [outcome.error])
|
|
403
|
+
);
|
|
404
|
+
if (exitCode !== void 0) {
|
|
405
|
+
nodeProcess.exitCode = exitCode;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async function handleStop(selector, rawOptions, command) {
|
|
409
|
+
rejectScopedStopAll(selector, rawOptions, command);
|
|
410
|
+
const options = mergeSelector(selector, rawOptions);
|
|
411
|
+
if (options.all === true) {
|
|
412
|
+
await handleStopAll(options.force === true);
|
|
455
413
|
return;
|
|
456
414
|
}
|
|
457
415
|
const key = await resolveOptionalSessionKey(options);
|
|
@@ -484,9 +442,9 @@ async function handleDoctor(cleanup) {
|
|
|
484
442
|
`);
|
|
485
443
|
}
|
|
486
444
|
function registerStartCommand(program) {
|
|
487
|
-
program.command("start").description("Open a debug tunnel for one app").argument("[selector]", "Optional <app> or <region>/<org>/<space>/<app>").option("--region <key>", "CF region key (default: current cf target)").option("--api-endpoint <url>", "
|
|
445
|
+
program.command("start").description("Open a debug tunnel for one app").argument("[selector]", "Optional <app> or <region>/<org>/<space>/<app>").option("--region <key>", "CF region key (default: current cf target)").option("--api-endpoint <url>", "Absolute HTTPS CF API endpoint override").option("--org <name>", "CF org (default: current cf target)").option("--space <name>", "CF space (default: current cf target)").option("--app <name>", "CF app name").option("--process <name>", "CF process name", "web").option("-i, --instance <index>", "CF process instance", "0").option("--node-pid <pid>", "Explicit remote Node.js PID").option("--port <number>", "Preferred local port").option(
|
|
488
446
|
"--remote-port <number>",
|
|
489
|
-
"
|
|
447
|
+
"Port where cf-debugger looks for an existing remote inspector",
|
|
490
448
|
DEFAULT_NODE_INSPECTOR_PORT.toString()
|
|
491
449
|
).option("--timeout <seconds>", "Tunnel/inspector readiness timeout", "180").option("--startup-timeout <seconds>", "Overall startup deadline", "300").option(
|
|
492
450
|
"--allow-ssh-enable-restart",
|
|
@@ -510,7 +468,7 @@ function registerStopCommand(program) {
|
|
|
510
468
|
false
|
|
511
469
|
).option(
|
|
512
470
|
"--force",
|
|
513
|
-
"Forget unverifiable state
|
|
471
|
+
"Forget unverifiable state first; best-effort remove its exact owned CF home; never signal an unverified process",
|
|
514
472
|
false
|
|
515
473
|
).action(
|
|
516
474
|
async (selector, options, command) => {
|
|
@@ -532,7 +490,7 @@ function registerReadCommands(program) {
|
|
|
532
490
|
});
|
|
533
491
|
}
|
|
534
492
|
async function main(argv) {
|
|
535
|
-
const program = new Command().name("cf-debugger").description("Open an SSH debug tunnel to a SAP BTP Cloud Foundry Node.js inspector").version(
|
|
493
|
+
const program = new Command().name("cf-debugger").description("Open an SSH debug tunnel to a SAP BTP Cloud Foundry Node.js inspector").version(readPackageVersion());
|
|
536
494
|
registerStartCommand(program);
|
|
537
495
|
registerStopCommand(program);
|
|
538
496
|
registerReadCommands(program);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../package.json"],"sourcesContent":["#!/usr/bin/env node\n\nimport nodeProcess from \"node:process\";\n\nimport { Command } from \"commander\";\n\nimport packageMetadata from \"../package.json\" with { type: \"json\" };\n\nimport { readCurrentCfTarget, requireCurrentCfRegion } from \"./cf.js\";\nimport type { CurrentCfTargetReadOptions } from \"./cf.js\";\nimport {\n CLEANUP_FAILURE_EXIT_CODE,\n cliErrorExitCode,\n hasTunnelTerminationFailure,\n} from \"./cli-errors.js\";\nimport {\n DEFAULT_NODE_INSPECTOR_PORT,\n resolveNodeTarget,\n} from \"./cloud-foundry/node-process.js\";\nimport { runDoctor } from \"./debug-session/doctor.js\";\nimport { getDebuggerHandleTunnelError } from \"./debug-session/lifecycle.js\";\nimport { startDebuggerWithinDeadline } from \"./debug-session/start.js\";\nimport {\n createStartupDeadline,\n remainingStartupMs,\n resolveStartupTimeoutMs,\n startupTimeoutError,\n} from \"./debug-session/startup-deadline.js\";\nimport {\n getSession,\n listSessions,\n stopAllDebuggers,\n stopDebugger,\n} from \"./debugger.js\";\nimport type {\n DebuggerHandle,\n ResolvedSessionKey,\n SessionStatus,\n StartDebuggerOptions,\n} from \"./types.js\";\nimport { CfDebuggerError } from \"./types.js\";\n\nfunction readRequiredOption(value: string | undefined, flag: string): string {\n if (value === undefined || value.trim().length === 0) {\n throw new CfDebuggerError(\"UNSAFE_INPUT\", `Missing required option ${flag}.`);\n }\n return value;\n}\n\nfunction parseOptionalInteger(\n raw: string | undefined,\n label: string,\n minimum: number,\n maximum = Number.MAX_SAFE_INTEGER,\n): number | undefined {\n if (raw === undefined) {\n return undefined;\n }\n if (!/^\\d+$/.test(raw)) {\n throw new CfDebuggerError(\"UNSAFE_INPUT\", `${label} must be an integer.`);\n }\n const value = Number(raw);\n if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {\n throw new CfDebuggerError(\n \"UNSAFE_INPUT\",\n `${label} must be from ${minimum.toString()} to ${maximum.toString()}.`,\n );\n }\n return value;\n}\n\nfunction parseSeconds(raw: string | undefined, label: string): number | undefined {\n const seconds = parseOptionalInteger(raw, label, 1);\n return seconds === undefined ? undefined : seconds * 1000;\n}\n\ninterface TargetCommandOptions {\n readonly process?: string;\n readonly instance?: string;\n readonly nodePid?: string;\n readonly apiEndpoint?: string;\n}\n\ninterface StartCommandOptions extends TargetCommandOptions {\n readonly region?: string;\n readonly org?: string;\n readonly space?: string;\n readonly app?: string;\n readonly port?: string;\n readonly remotePort?: string;\n readonly timeout?: string;\n readonly startupTimeout?: string;\n readonly allowSshEnableRestart?: boolean;\n readonly sshEnableRestart?: boolean;\n readonly verbose?: boolean;\n}\n\ninterface StopCommandOptions extends TargetCommandOptions {\n readonly region?: string;\n readonly org?: string;\n readonly space?: string;\n readonly app?: string;\n readonly sessionId?: string;\n readonly all?: boolean;\n readonly force?: boolean;\n}\n\ntype StatusCommandOptions = Omit<StopCommandOptions, \"all\" | \"force\">;\nconst STOP_SCOPE_OPTION_NAMES = [\n \"apiEndpoint\",\n \"app\",\n \"instance\",\n \"nodePid\",\n \"org\",\n \"process\",\n \"region\",\n \"sessionId\",\n \"space\",\n] as const;\n\nfunction logStatus(verbose: boolean, status: SessionStatus, message?: string): void {\n if (!verbose) {\n return;\n }\n const suffix = message === undefined ? \"\" : `: ${message}`;\n nodeProcess.stdout.write(`[cf-debugger] ${status}${suffix}\\n`);\n}\n\nfunction mergeSelector<\n T extends { region?: string; org?: string; space?: string; app?: string },\n>(selector: string | undefined, options: T): T {\n if (selector === undefined) {\n return options;\n }\n const parts = selector.split(\"/\");\n if (parts.length === 1 && parts[0]?.length !== 0) {\n return { ...options, app: options.app ?? parts[0] };\n }\n if (parts.length === 4 && parts.every((part) => part.length > 0)) {\n return {\n ...options,\n region: options.region ?? parts[0],\n org: options.org ?? parts[1],\n space: options.space ?? parts[2],\n app: options.app ?? parts[3],\n };\n }\n throw new CfDebuggerError(\n \"UNSAFE_INPUT\",\n \"Invalid app selector. Expected <app> or non-empty <region>/<org>/<space>/<app> segments.\",\n );\n}\n\ninterface StartupAbort {\n readonly signal: AbortSignal;\n requestedExitCode(): number | undefined;\n dispose(): void;\n}\n\nfunction startupAbort(app: string): StartupAbort {\n const controller = new AbortController();\n let requestedExitCode: number | undefined;\n const handler = (exitCode: number) => (): void => {\n requestedExitCode ??= exitCode;\n controller.abort();\n nodeProcess.stderr.write(`\\nAborting startup for ${app}...\\n`);\n };\n const onSigint = handler(130);\n const onSigterm = handler(143);\n nodeProcess.on(\"SIGINT\", onSigint);\n nodeProcess.on(\"SIGTERM\", onSigterm);\n return {\n signal: controller.signal,\n requestedExitCode: (): number | undefined => requestedExitCode,\n dispose: (): void => {\n nodeProcess.off(\"SIGINT\", onSigint);\n nodeProcess.off(\"SIGTERM\", onSigterm);\n },\n };\n}\n\nfunction writeReady(\n app: string,\n key: ResolvedSessionKey,\n handle: DebuggerHandle,\n): void {\n nodeProcess.stdout.write(\n `Debugger ready for ${app} (${key.region}/${key.org}/${key.space}).\\n` +\n ` Process: ${key.process}\\n` +\n ` Instance: ${key.instance.toString()}\\n` +\n ` Local port: ${handle.session.localPort.toString()}\\n` +\n ` Remote port: ${handle.session.remotePort.toString()}\\n` +\n ` Session id: ${handle.session.sessionId}\\n` +\n ` Tunnel PID: ${handle.session.pid.toString()}\\n` +\n ` Node PID: ${handle.session.remoteNodePid?.toString() ?? \"unknown\"}\\n` +\n \"Press Ctrl+C to stop.\\n\",\n );\n}\n\nfunction writeCleanupError(error: unknown): void {\n const message = error instanceof Error ? error.message : String(error);\n nodeProcess.stderr.write(`Error during stop: ${message}\\n`);\n}\n\nasync function waitForHandle(\n app: string,\n handle: DebuggerHandle,\n): Promise<void> {\n let pendingDispose: Promise<void> | undefined;\n let requestedExitCode: number | undefined;\n const dispose = (): Promise<void> => {\n pendingDispose ??= (async (): Promise<void> => {\n nodeProcess.stdout.write(`\\nStopping debugger for ${app}...\\n`);\n await handle.dispose();\n })();\n return pendingDispose;\n };\n const stop = (exitCode: number) => (): void => {\n requestedExitCode = exitCode;\n void dispose()\n .catch((error: unknown) => {\n writeCleanupError(error);\n nodeProcess.exit(CLEANUP_FAILURE_EXIT_CODE);\n });\n };\n nodeProcess.on(\"SIGINT\", stop(130));\n nodeProcess.on(\"SIGTERM\", stop(143));\n const code = await handle.waitForExit();\n const tunnelError = getDebuggerHandleTunnelError(handle);\n if (tunnelError !== undefined) {\n nodeProcess.stderr.write(\n `Error [${tunnelError.code}]: ${tunnelError.message}\\n`,\n );\n if (tunnelError.stderr !== undefined && tunnelError.stderr.trim().length > 0) {\n nodeProcess.stderr.write(`[cf-debugger transport]\\n${tunnelError.stderr.trim()}\\n`);\n }\n }\n try {\n await dispose();\n } catch (error: unknown) {\n writeCleanupError(error);\n nodeProcess.exit(CLEANUP_FAILURE_EXIT_CODE);\n }\n nodeProcess.exit(requestedExitCode ?? code ?? 0);\n}\n\ntype RestartEnvironment = \"allow\" | \"forbid\" | \"unset\";\n\nfunction parseRestartEnvironment(): RestartEnvironment {\n const value = nodeProcess.env[\"CF_DEBUGGER_ALLOW_RESTART\"];\n if (value === undefined) {\n return \"unset\";\n }\n if (value === \"0\") {\n return \"forbid\";\n }\n if (value === \"1\") {\n return \"allow\";\n }\n throw new CfDebuggerError(\n \"UNSAFE_INPUT\",\n \"CF_DEBUGGER_ALLOW_RESTART must be 0 or 1.\",\n );\n}\n\nfunction buildStartOptions(\n options: StartCommandOptions,\n key: ResolvedSessionKey,\n app: string,\n signal: AbortSignal,\n startupTimeoutMs: number,\n): StartDebuggerOptions {\n const preferredPort = parseOptionalInteger(options.port, \"port\", 1, 65_535);\n const remotePort = parseOptionalInteger(options.remotePort, \"remotePort\", 1, 65_535);\n const nodePid = parseOptionalInteger(options.nodePid, \"nodePid\", 1);\n const tunnelReadyTimeoutMs = parseSeconds(options.timeout, \"timeout\");\n const restartEnvironment = parseRestartEnvironment();\n const allowSshEnableRestart =\n options.sshEnableRestart !== false &&\n restartEnvironment !== \"forbid\" &&\n (options.allowSshEnableRestart === true || restartEnvironment === \"allow\");\n return {\n region: key.region,\n org: key.org,\n space: key.space,\n app,\n process: key.process,\n instance: key.instance,\n signal,\n allowSshEnableRestart,\n ...(options.apiEndpoint === undefined ? {} : { apiEndpoint: options.apiEndpoint }),\n ...(preferredPort === undefined ? {} : { preferredPort }),\n ...(remotePort === undefined ? {} : { remotePort }),\n ...(nodePid === undefined ? {} : { nodePid }),\n ...(tunnelReadyTimeoutMs === undefined ? {} : { tunnelReadyTimeoutMs }),\n startupTimeoutMs,\n };\n}\n\nfunction deadlineTimedOut(\n deadline: ReturnType<typeof createStartupDeadline>,\n): boolean {\n return remainingStartupMs(deadline.expiresAt) === 0 ||\n (\n deadline.signal.reason instanceof CfDebuggerError &&\n deadline.signal.reason.code === \"STARTUP_TIMEOUT\"\n );\n}\n\nasync function handleStart(\n selector: string | undefined,\n rawOptions: StartCommandOptions,\n): Promise<void> {\n const options = mergeSelector(selector, rawOptions);\n const app = readRequiredOption(options.app, \"--app or selector\");\n const verbose = options.verbose ?? false;\n const startupTimeoutMs = resolveStartupTimeoutMs(\n parseSeconds(options.startupTimeout, \"startupTimeout\"),\n );\n const abort = startupAbort(app);\n const deadline = createStartupDeadline(startupTimeoutMs, abort.signal);\n let handle;\n let phase = \"current CF target discovery\";\n let key: ResolvedSessionKey;\n try {\n key = await resolveSessionKey({ ...options, app }, deadline.signal);\n if (deadlineTimedOut(deadline)) {\n throw startupTimeoutError(startupTimeoutMs, phase);\n }\n phase = \"startup\";\n handle = await startDebuggerWithinDeadline({\n ...buildStartOptions(options, key, app, deadline.signal, startupTimeoutMs),\n verbose,\n onStatus: (status, message): void => {\n logStatus(verbose, status, message);\n },\n }, deadline);\n } catch (error: unknown) {\n const normalized = deadlineTimedOut(deadline) &&\n !(error instanceof CfDebuggerError && error.code === \"STARTUP_TIMEOUT\")\n ? startupTimeoutError(startupTimeoutMs, phase)\n : error;\n const exitCode = abort.requestedExitCode();\n if (exitCode !== undefined && typeof normalized === \"object\" && normalized !== null) {\n Reflect.set(normalized, \"cliExitCode\", exitCode);\n }\n throw normalized;\n } finally {\n deadline.dispose();\n abort.dispose();\n }\n writeReady(app, key, handle);\n await waitForHandle(app, handle);\n}\n\nfunction optionalText(value: string | undefined): string | undefined {\n const trimmed = value?.trim();\n return trimmed === undefined || trimmed.length === 0 ? undefined : trimmed;\n}\n\nfunction currentCfOptions(signal?: AbortSignal): CurrentCfTargetReadOptions | undefined {\n const command = nodeProcess.env[\"CF_DEBUGGER_CF_BIN\"];\n if (command === undefined && signal === undefined) {\n return undefined;\n }\n return {\n ...(command === undefined ? {} : { command }),\n ...(signal === undefined ? {} : { signal }),\n };\n}\n\nasync function resolveSessionKey(\n options: StopCommandOptions,\n signal?: AbortSignal,\n): Promise<ResolvedSessionKey> {\n const app = readRequiredOption(options.app, \"--app or selector\");\n const region = optionalText(options.region);\n const org = optionalText(options.org);\n const space = optionalText(options.space);\n const nodePid = parseOptionalInteger(options.nodePid, \"nodePid\", 1);\n const instance = parseOptionalInteger(options.instance, \"instance\", 0);\n const target = resolveNodeTarget({\n ...(options.process === undefined ? {} : { process: options.process }),\n ...(instance === undefined ? {} : { instance }),\n ...(nodePid === undefined ? {} : { nodePid }),\n });\n if (region !== undefined && org !== undefined && space !== undefined) {\n return {\n region,\n org,\n space,\n app,\n process: target.process,\n instance: target.instance,\n ...(options.apiEndpoint === undefined ? {} : { apiEndpoint: options.apiEndpoint }),\n ...(target.nodePid === undefined ? {} : { nodePid: target.nodePid }),\n };\n }\n const current = await readCurrentCfTarget(currentCfOptions(signal)).catch((error: unknown) => {\n if (\n error instanceof CfDebuggerError &&\n (error.code === \"ABORTED\" || error.code === \"STARTUP_TIMEOUT\")\n ) {\n throw error;\n }\n throw new CfDebuggerError(\n \"CF_TARGET_FAILED\",\n \"No current CF target. Run `cf target -o <org> -s <space>` or pass --region/--org/--space.\",\n error instanceof Error ? error.message : String(error),\n );\n });\n if (current === undefined) {\n throw new CfDebuggerError(\n \"CF_TARGET_FAILED\",\n \"No current CF target. Run `cf target -o <org> -s <space>` or pass --region/--org/--space.\",\n );\n }\n return {\n region: region ?? requireCurrentCfRegion(current),\n org: org ?? current.org,\n space: space ?? current.space,\n app,\n process: target.process,\n instance: target.instance,\n ...(options.apiEndpoint === undefined ? {} : { apiEndpoint: options.apiEndpoint }),\n ...(target.nodePid === undefined ? {} : { nodePid: target.nodePid }),\n };\n}\n\nasync function resolveOptionalSessionKey(\n options: StopCommandOptions,\n): Promise<ResolvedSessionKey | undefined> {\n if (optionalText(options.app) === undefined) {\n if (\n optionalText(options.region) !== undefined ||\n optionalText(options.org) !== undefined ||\n optionalText(options.space) !== undefined\n ) {\n readRequiredOption(options.app, \"--app\");\n }\n return undefined;\n }\n return await resolveSessionKey(options);\n}\n\nfunction writeStopResult(result: Awaited<ReturnType<typeof stopDebugger>>): void {\n if (result === undefined) {\n return;\n }\n if (result.warning !== undefined) {\n nodeProcess.stderr.write(`[cf-debugger] warning: ${result.warning}\\n`);\n }\n if (result.pending) {\n nodeProcess.stdout.write(\n `Stop requested for session ${result.sessionId} (${result.app}, phase ${result.status}).\\n`,\n );\n } else if (result.stale) {\n nodeProcess.stdout.write(\n `Removed ${result.forced ? \"forced \" : \"\"}stale session ${result.sessionId} ` +\n `(${result.app}, port ${result.localPort.toString()}).\\n`,\n );\n } else {\n nodeProcess.stdout.write(\n `Stopped session ${result.sessionId} (${result.app}, port ${result.localPort.toString()}).\\n`,\n );\n }\n}\n\nasync function handleStop(\n selector: string | undefined,\n rawOptions: StopCommandOptions,\n command: Command,\n): Promise<void> {\n if (\n rawOptions.all === true &&\n (\n selector !== undefined ||\n STOP_SCOPE_OPTION_NAMES.some((name) => command.getOptionValueSource(name) === \"cli\")\n )\n ) {\n throw new CfDebuggerError(\n \"UNSAFE_INPUT\",\n \"--all cannot be combined with a selector, --session-id, or target selector options.\",\n );\n }\n const options = mergeSelector(selector, rawOptions);\n if (options.all === true) {\n const summary = await stopAllDebuggers(options.force === true);\n for (const outcome of summary.outcomes) {\n if (outcome.error === undefined) {\n writeStopResult(outcome.result);\n } else {\n nodeProcess.stderr.write(\n `Failed ${outcome.sessionId} (${outcome.app}): ${outcome.error.message}\\n`,\n );\n }\n }\n nodeProcess.stdout.write(\n `Stop summary: ${summary.stopped.toString()} stopped, ${summary.stale.toString()} stale, ` +\n `${summary.pending.toString()} pending, ${summary.failed.toString()} failed.\\n`,\n );\n if (summary.failed > 0) {\n nodeProcess.exitCode = 1;\n }\n return;\n }\n const key = await resolveOptionalSessionKey(options);\n const result = await stopDebugger({\n ...(options.sessionId === undefined ? {} : { sessionId: options.sessionId }),\n ...(key === undefined ? {} : { key }),\n force: options.force === true,\n });\n if (result === undefined) {\n throw new CfDebuggerError(\n \"SESSION_NOT_FOUND\",\n \"No matching session. Use `cf-debugger list` and pass --session-id or an exact key.\",\n );\n }\n writeStopResult(result);\n}\n\nasync function handleList(): Promise<void> {\n nodeProcess.stdout.write(`${JSON.stringify(await listSessions(), null, 2)}\\n`);\n}\n\nasync function handleStatus(\n selector: string | undefined,\n rawOptions: StatusCommandOptions,\n): Promise<void> {\n const options = mergeSelector(selector, rawOptions);\n const session = options.sessionId === undefined\n ? await getSession(await resolveSessionKey(options))\n : (await listSessions()).find((candidate) => candidate.sessionId === options.sessionId);\n nodeProcess.stdout.write(`${JSON.stringify(session ?? null, null, 2)}\\n`);\n}\n\nasync function handleDoctor(cleanup: boolean): Promise<void> {\n const report = await runDoctor({ cleanup });\n nodeProcess.stdout.write(`${JSON.stringify(report, null, 2)}\\n`);\n}\n\nfunction registerStartCommand(program: Command): void {\n program\n .command(\"start\")\n .description(\"Open a debug tunnel for one app\")\n .argument(\"[selector]\", \"Optional <app> or <region>/<org>/<space>/<app>\")\n .option(\"--region <key>\", \"CF region key (default: current cf target)\")\n .option(\"--api-endpoint <url>\", \"Explicit CF API endpoint override\")\n .option(\"--org <name>\", \"CF org (default: current cf target)\")\n .option(\"--space <name>\", \"CF space (default: current cf target)\")\n .option(\"--app <name>\", \"CF app name\")\n .option(\"--process <name>\", \"CF process name\", \"web\")\n .option(\"-i, --instance <index>\", \"CF process instance\", \"0\")\n .option(\"--node-pid <pid>\", \"Explicit remote Node.js PID\")\n .option(\"--port <number>\", \"Preferred local port\")\n .option(\n \"--remote-port <number>\",\n \"Remote inspector port\",\n DEFAULT_NODE_INSPECTOR_PORT.toString(),\n )\n .option(\"--timeout <seconds>\", \"Tunnel/inspector readiness timeout\", \"180\")\n .option(\"--startup-timeout <seconds>\", \"Overall startup deadline\", \"300\")\n .option(\n \"--allow-ssh-enable-restart\",\n \"Allow app-level SSH enablement and an app restart (opt-in)\",\n )\n .option(\n \"--no-ssh-enable-restart\",\n \"Forbid app-level SSH enablement and restart, overriding flag/environment\",\n )\n .option(\"--verbose\", \"Print status transitions and tunnel diagnostics\", false)\n .action(async (selector: string | undefined, options: StartCommandOptions): Promise<void> => {\n await handleStart(selector, options);\n });\n}\n\nfunction addKeyOptions(command: Command): Command {\n return command\n .option(\"--region <key>\")\n .option(\"--api-endpoint <url>\", \"Exact API endpoint selector\")\n .option(\"--org <name>\")\n .option(\"--space <name>\")\n .option(\"--app <name>\")\n .option(\"--process <name>\", \"CF process name\", \"web\")\n .option(\"-i, --instance <index>\", \"CF process instance\", \"0\")\n .option(\"--node-pid <pid>\", \"Exact remote Node PID selector\");\n}\n\nfunction registerStopCommand(program: Command): void {\n addKeyOptions(\n program\n .command(\"stop\")\n .description(\"Stop one session by exact key/id, or every session with --all\")\n .argument(\"[selector]\", \"Optional <app> or <region>/<org>/<space>/<app>\"),\n )\n .option(\"--session-id <id>\")\n .option(\n \"--all\",\n \"Attempt every active session; cannot be combined with session selectors\",\n false,\n )\n .option(\n \"--force\",\n \"Forget unverifiable state and its owned CF home; never signal an unverified process\",\n false,\n )\n .action(\n async (\n selector: string | undefined,\n options: StopCommandOptions,\n command: Command,\n ): Promise<void> => {\n await handleStop(selector, options, command);\n },\n );\n}\n\nfunction registerReadCommands(program: Command): void {\n program\n .command(\"list\")\n .description(\"Print every active debugger session as JSON\")\n .action(async (): Promise<void> => {\n await handleList();\n });\n addKeyOptions(\n program\n .command(\"status\")\n .description(\"Print one exact session as JSON; null only when no session matches\")\n .argument(\"[selector]\", \"Optional <app> or <region>/<org>/<space>/<app>\"),\n )\n .option(\"--session-id <id>\", \"Exact session ID selector\")\n .action(async (selector: string | undefined, options: StatusCommandOptions): Promise<void> => {\n await handleStatus(selector, options);\n });\n program\n .command(\"doctor\")\n .description(\"Report session health, orphan homes/ports, temp files, and legacy credentials\")\n .option(\"--cleanup\", \"Remove only safe orphan v2 homes and stale v2 temp/lock files\", false)\n .action(async (options: { readonly cleanup?: boolean }): Promise<void> => {\n await handleDoctor(options.cleanup === true);\n });\n}\n\nexport async function main(argv: readonly string[]): Promise<void> {\n const program = new Command()\n .name(\"cf-debugger\")\n .description(\"Open an SSH debug tunnel to a SAP BTP Cloud Foundry Node.js inspector\")\n .version(packageMetadata.version);\n registerStartCommand(program);\n registerStopCommand(program);\n registerReadCommands(program);\n await program.parseAsync([...argv]);\n}\n\nfunction writeTopLevelError(error: unknown): void {\n if (error instanceof CfDebuggerError) {\n if (error.code === \"ABORTED\") {\n nodeProcess.stderr.write(`Aborted: ${error.message}\\n`);\n nodeProcess.exit(cliErrorExitCode(error));\n }\n nodeProcess.stderr.write(`Error [${error.code}]: ${error.message}\\n`);\n if (error.stderr !== undefined && error.stderr.trim().length > 0) {\n nodeProcess.stderr.write(`[cf-debugger transport]\\n${error.stderr.trim()}\\n`);\n }\n nodeProcess.exit(cliErrorExitCode(error));\n }\n if (hasTunnelTerminationFailure(error)) {\n const message = error instanceof Error ? error.message : String(error);\n nodeProcess.stderr.write(`Error during cleanup: ${message}\\n`);\n nodeProcess.exit(CLEANUP_FAILURE_EXIT_CODE);\n }\n const message = error instanceof Error ? error.message : String(error);\n nodeProcess.stderr.write(`Error: ${message}\\n`);\n nodeProcess.exit(cliErrorExitCode(error));\n}\n\ntry {\n await main(nodeProcess.argv);\n} catch (error: unknown) {\n writeTopLevelError(error);\n}\n","{\n \"name\": \"@saptools/cf-debugger\",\n \"version\": \"0.2.1\",\n \"description\": \"Open an SSH debug tunnel to a SAP BTP Cloud Foundry app's Node.js inspector — from any terminal, no IDE required.\",\n \"type\": \"module\",\n \"publishConfig\": {\n \"access\": \"public\",\n \"registry\": \"https://registry.npmjs.org/\"\n },\n \"bin\": {\n \"cf-debugger\": \"dist/cli.js\"\n },\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n }\n },\n \"files\": [\n \"dist\"\n ],\n \"engines\": {\n \"node\": \">=20.0.0\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"typecheck\": \"tsc --noEmit\",\n \"lint\": \"eslint src tests\",\n \"test:unit\": \"vitest run --coverage\",\n \"test:e2e\": \"playwright test\"\n },\n \"keywords\": [\n \"sap\",\n \"cloud-foundry\",\n \"btp\",\n \"debug\",\n \"debugger\",\n \"ssh-tunnel\",\n \"node-inspector\",\n \"cli\",\n \"saptools\"\n ],\n \"author\": \"Dong Tran\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/dongitran/saptools.git\",\n \"directory\": \"packages/cf-debugger\"\n },\n \"homepage\": \"https://github.com/dongitran/saptools/tree/main/packages/cf-debugger#readme\",\n \"bugs\": {\n \"url\": \"https://github.com/dongitran/saptools/issues\"\n },\n \"dependencies\": {\n \"commander\": \"^13.0.0\"\n },\n \"devDependencies\": {\n \"@playwright/test\": \"^1.50.0\",\n \"@vitest/coverage-v8\": \"^3.0.0\",\n \"tsup\": \"^8.3.0\",\n \"vitest\": \"^3.0.0\"\n }\n}\n"],"mappings":"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","names":["message"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/cli-input.ts","../src/package-version.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport nodeProcess from \"node:process\";\n\nimport { Command } from \"commander\";\n\nimport { readCurrentCfTarget, requireCurrentCfRegion } from \"./cf.js\";\nimport type { CurrentCfTargetReadOptions } from \"./cf.js\";\nimport {\n CLEANUP_FAILURE_EXIT_CODE,\n cliErrorExitCode,\n hasTunnelTerminationFailure,\n stopAllExitCode,\n} from \"./cli-errors.js\";\nimport {\n parseOptionalInteger,\n parseSeconds,\n readRequiredOption,\n} from \"./cli-input.js\";\nimport {\n DEFAULT_NODE_INSPECTOR_PORT,\n resolveNodeTarget,\n} from \"./cloud-foundry/node-process.js\";\nimport { runDoctor } from \"./debug-session/doctor.js\";\nimport { getDebuggerHandleTunnelError } from \"./debug-session/lifecycle.js\";\nimport { startDebuggerWithinDeadline } from \"./debug-session/start.js\";\nimport {\n createStartupDeadline,\n remainingStartupMs,\n resolveStartupTimeoutMs,\n startupTimeoutError,\n} from \"./debug-session/startup-deadline.js\";\nimport {\n getSession,\n listSessions,\n stopAllDebuggers,\n stopDebugger,\n} from \"./debugger.js\";\nimport { readPackageVersion } from \"./package-version.js\";\nimport { validateApiEndpointOverride } from \"./regions.js\";\nimport { parseRestartEnvironment } from \"./restart-policy.js\";\nimport type {\n DebuggerHandle,\n ResolvedSessionKey,\n SessionStatus,\n StartDebuggerOptions,\n} from \"./types.js\";\nimport { CfDebuggerError } from \"./types.js\";\n\ninterface TargetCommandOptions {\n readonly process?: string;\n readonly instance?: string;\n readonly nodePid?: string;\n readonly apiEndpoint?: string;\n}\n\ninterface StartCommandOptions extends TargetCommandOptions {\n readonly region?: string;\n readonly org?: string;\n readonly space?: string;\n readonly app?: string;\n readonly port?: string;\n readonly remotePort?: string;\n readonly timeout?: string;\n readonly startupTimeout?: string;\n readonly allowSshEnableRestart?: boolean;\n readonly sshEnableRestart?: boolean;\n readonly verbose?: boolean;\n}\n\ninterface StopCommandOptions extends TargetCommandOptions {\n readonly region?: string;\n readonly org?: string;\n readonly space?: string;\n readonly app?: string;\n readonly sessionId?: string;\n readonly all?: boolean;\n readonly force?: boolean;\n}\n\ntype StatusCommandOptions = Omit<StopCommandOptions, \"all\" | \"force\">;\nconst STOP_SCOPE_OPTION_NAMES = [\n \"apiEndpoint\",\n \"app\",\n \"instance\",\n \"nodePid\",\n \"org\",\n \"process\",\n \"region\",\n \"sessionId\",\n \"space\",\n] as const;\n\nfunction logStatus(verbose: boolean, status: SessionStatus, message?: string): void {\n if (!verbose) {\n return;\n }\n const suffix = message === undefined ? \"\" : `: ${message}`;\n nodeProcess.stdout.write(`[cf-debugger] ${status}${suffix}\\n`);\n}\n\nfunction mergeSelector<\n T extends { region?: string; org?: string; space?: string; app?: string },\n>(selector: string | undefined, options: T): T {\n if (selector === undefined) {\n return options;\n }\n const parts = selector.split(\"/\");\n if (parts.length === 1 && parts[0]?.length !== 0) {\n return { ...options, app: options.app ?? parts[0] };\n }\n if (parts.length === 4 && parts.every((part) => part.length > 0)) {\n return {\n ...options,\n region: options.region ?? parts[0],\n org: options.org ?? parts[1],\n space: options.space ?? parts[2],\n app: options.app ?? parts[3],\n };\n }\n throw new CfDebuggerError(\n \"UNSAFE_INPUT\",\n \"Invalid app selector. Expected <app> or non-empty <region>/<org>/<space>/<app> segments.\",\n );\n}\n\ninterface StartupAbort {\n readonly signal: AbortSignal;\n requestedExitCode(): number | undefined;\n dispose(): void;\n}\n\nfunction startupAbort(app: string): StartupAbort {\n const controller = new AbortController();\n let requestedExitCode: number | undefined;\n const handler = (exitCode: number) => (): void => {\n requestedExitCode ??= exitCode;\n controller.abort();\n nodeProcess.stderr.write(`\\nAborting startup for ${app}...\\n`);\n };\n const onSigint = handler(130);\n const onSigterm = handler(143);\n nodeProcess.on(\"SIGINT\", onSigint);\n nodeProcess.on(\"SIGTERM\", onSigterm);\n return {\n signal: controller.signal,\n requestedExitCode: (): number | undefined => requestedExitCode,\n dispose: (): void => {\n nodeProcess.off(\"SIGINT\", onSigint);\n nodeProcess.off(\"SIGTERM\", onSigterm);\n },\n };\n}\n\nfunction writeReady(\n app: string,\n key: ResolvedSessionKey,\n handle: DebuggerHandle,\n): void {\n nodeProcess.stdout.write(\n `Debugger ready for ${app} (${key.region}/${key.org}/${key.space}).\\n` +\n ` Process: ${key.process}\\n` +\n ` Instance: ${key.instance.toString()}\\n` +\n ` Local port: ${handle.session.localPort.toString()}\\n` +\n ` Remote port: ${handle.session.remotePort.toString()}\\n` +\n ` Session id: ${handle.session.sessionId}\\n` +\n ` Tunnel PID: ${handle.session.pid.toString()}\\n` +\n ` Node PID: ${handle.session.remoteNodePid?.toString() ?? \"unknown\"}\\n` +\n \"Press Ctrl+C to stop.\\n\",\n );\n}\n\nfunction writeCleanupError(error: unknown): void {\n const message = error instanceof Error ? error.message : String(error);\n nodeProcess.stderr.write(`Error during stop: ${message}\\n`);\n}\n\nasync function waitForHandle(\n app: string,\n handle: DebuggerHandle,\n): Promise<void> {\n let pendingDispose: Promise<void> | undefined;\n let requestedExitCode: number | undefined;\n const dispose = (): Promise<void> => {\n pendingDispose ??= (async (): Promise<void> => {\n nodeProcess.stdout.write(`\\nStopping debugger for ${app}...\\n`);\n await handle.dispose();\n })();\n return pendingDispose;\n };\n const stop = (exitCode: number) => (): void => {\n requestedExitCode = exitCode;\n void dispose()\n .catch((error: unknown) => {\n writeCleanupError(error);\n nodeProcess.exit(CLEANUP_FAILURE_EXIT_CODE);\n });\n };\n nodeProcess.on(\"SIGINT\", stop(130));\n nodeProcess.on(\"SIGTERM\", stop(143));\n const code = await handle.waitForExit();\n const tunnelError = getDebuggerHandleTunnelError(handle);\n if (tunnelError !== undefined) {\n nodeProcess.stderr.write(\n `Error [${tunnelError.code}]: ${tunnelError.message}\\n`,\n );\n if (tunnelError.stderr !== undefined && tunnelError.stderr.trim().length > 0) {\n nodeProcess.stderr.write(`[cf-debugger transport]\\n${tunnelError.stderr.trim()}\\n`);\n }\n }\n try {\n await dispose();\n } catch (error: unknown) {\n writeCleanupError(error);\n nodeProcess.exit(CLEANUP_FAILURE_EXIT_CODE);\n }\n nodeProcess.exit(requestedExitCode ?? code ?? 0);\n}\n\nfunction buildStartOptions(\n options: StartCommandOptions,\n key: ResolvedSessionKey,\n app: string,\n signal: AbortSignal,\n startupTimeoutMs: number,\n): StartDebuggerOptions {\n const preferredPort = parseOptionalInteger(options.port, \"port\", 1, 65_535);\n const remotePort = parseOptionalInteger(options.remotePort, \"remotePort\", 1, 65_535);\n const nodePid = parseOptionalInteger(options.nodePid, \"nodePid\", 1);\n const tunnelReadyTimeoutMs = parseSeconds(options.timeout, \"timeout\");\n const restartEnvironment = parseRestartEnvironment(\n nodeProcess.env[\"CF_DEBUGGER_ALLOW_RESTART\"],\n );\n const allowSshEnableRestart =\n options.sshEnableRestart !== false &&\n restartEnvironment !== \"forbid\" &&\n (options.allowSshEnableRestart === true || restartEnvironment === \"allow\");\n return {\n region: key.region,\n org: key.org,\n space: key.space,\n app,\n process: key.process,\n instance: key.instance,\n signal,\n allowSshEnableRestart,\n ...(options.apiEndpoint === undefined ? {} : { apiEndpoint: options.apiEndpoint }),\n ...(preferredPort === undefined ? {} : { preferredPort }),\n ...(remotePort === undefined ? {} : { remotePort }),\n ...(nodePid === undefined ? {} : { nodePid }),\n ...(tunnelReadyTimeoutMs === undefined ? {} : { tunnelReadyTimeoutMs }),\n startupTimeoutMs,\n };\n}\n\nfunction deadlineTimedOut(\n deadline: ReturnType<typeof createStartupDeadline>,\n): boolean {\n return remainingStartupMs(deadline.expiresAt) === 0 ||\n (\n deadline.signal.reason instanceof CfDebuggerError &&\n deadline.signal.reason.code === \"STARTUP_TIMEOUT\"\n );\n}\n\nfunction validateStartEndpoint(options: StartCommandOptions): StartCommandOptions {\n return options.apiEndpoint === undefined\n ? options\n : {\n ...options,\n apiEndpoint: validateApiEndpointOverride(options.apiEndpoint),\n };\n}\n\nasync function handleStart(\n selector: string | undefined,\n rawOptions: StartCommandOptions,\n): Promise<void> {\n const options = validateStartEndpoint(mergeSelector(selector, rawOptions));\n const app = readRequiredOption(options.app, \"--app or selector\");\n const verbose = options.verbose ?? false;\n const startupTimeoutMs = resolveStartupTimeoutMs(\n parseSeconds(options.startupTimeout, \"startupTimeout\"),\n );\n const abort = startupAbort(app);\n const deadline = createStartupDeadline(startupTimeoutMs, abort.signal);\n let handle;\n let phase = \"current CF target discovery\";\n let key: ResolvedSessionKey;\n try {\n key = await resolveSessionKey({ ...options, app }, deadline.signal);\n if (deadlineTimedOut(deadline)) {\n throw startupTimeoutError(startupTimeoutMs, phase);\n }\n phase = \"startup\";\n handle = await startDebuggerWithinDeadline({\n ...buildStartOptions(options, key, app, deadline.signal, startupTimeoutMs),\n verbose,\n onStatus: (status, message): void => {\n logStatus(verbose, status, message);\n },\n }, deadline);\n } catch (error: unknown) {\n const normalized = deadlineTimedOut(deadline) &&\n !(error instanceof CfDebuggerError && error.code === \"STARTUP_TIMEOUT\")\n ? startupTimeoutError(startupTimeoutMs, phase)\n : error;\n const exitCode = abort.requestedExitCode();\n if (exitCode !== undefined && typeof normalized === \"object\" && normalized !== null) {\n Reflect.set(normalized, \"cliExitCode\", exitCode);\n }\n throw normalized;\n } finally {\n deadline.dispose();\n abort.dispose();\n }\n writeReady(app, key, handle);\n await waitForHandle(app, handle);\n}\n\nfunction optionalText(value: string | undefined): string | undefined {\n const trimmed = value?.trim();\n return trimmed === undefined || trimmed.length === 0 ? undefined : trimmed;\n}\n\nfunction currentCfOptions(signal?: AbortSignal): CurrentCfTargetReadOptions | undefined {\n const command = nodeProcess.env[\"CF_DEBUGGER_CF_BIN\"];\n if (command === undefined && signal === undefined) {\n return undefined;\n }\n return {\n ...(command === undefined ? {} : { command }),\n ...(signal === undefined ? {} : { signal }),\n };\n}\n\ninterface ResolvedCfScope {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n}\n\nfunction currentTargetReadError(error: unknown): CfDebuggerError {\n return new CfDebuggerError(\n \"CF_TARGET_FAILED\",\n \"No current CF target. Run `cf target -o <org> -s <space>` or pass --region/--org/--space.\",\n error instanceof Error ? error.message : String(error),\n );\n}\n\nfunction missingCurrentTargetError(): CfDebuggerError {\n return new CfDebuggerError(\n \"CF_TARGET_FAILED\",\n \"No current CF target. Run `cf target -o <org> -s <space>` or pass --region/--org/--space.\",\n );\n}\n\nasync function resolveCfScope(\n region: string | undefined,\n org: string | undefined,\n space: string | undefined,\n signal?: AbortSignal,\n): Promise<ResolvedCfScope> {\n if (region !== undefined && org !== undefined && space !== undefined) {\n return { region, org, space };\n }\n const current = await readCurrentCfTarget(currentCfOptions(signal)).catch((error: unknown) => {\n if (\n error instanceof CfDebuggerError &&\n (error.code === \"ABORTED\" || error.code === \"STARTUP_TIMEOUT\")\n ) {\n throw error;\n }\n throw currentTargetReadError(error);\n });\n if (current === undefined) {\n throw missingCurrentTargetError();\n }\n return {\n region: region ?? requireCurrentCfRegion(current),\n org: org ?? current.org,\n space: space ?? current.space,\n };\n}\n\nasync function resolveSessionKey(\n options: StopCommandOptions,\n signal?: AbortSignal,\n): Promise<ResolvedSessionKey> {\n const app = readRequiredOption(options.app, \"--app or selector\");\n const region = optionalText(options.region);\n const org = optionalText(options.org);\n const space = optionalText(options.space);\n const nodePid = parseOptionalInteger(options.nodePid, \"nodePid\", 1);\n const instance = parseOptionalInteger(options.instance, \"instance\", 0);\n const target = resolveNodeTarget({\n ...(options.process === undefined ? {} : { process: options.process }),\n ...(instance === undefined ? {} : { instance }),\n ...(nodePid === undefined ? {} : { nodePid }),\n });\n const scope = await resolveCfScope(region, org, space, signal);\n return {\n ...scope,\n app,\n process: target.process,\n instance: target.instance,\n ...(options.apiEndpoint === undefined ? {} : { apiEndpoint: options.apiEndpoint }),\n ...(target.nodePid === undefined ? {} : { nodePid: target.nodePid }),\n };\n}\n\nasync function resolveOptionalSessionKey(\n options: StopCommandOptions,\n): Promise<ResolvedSessionKey | undefined> {\n if (optionalText(options.app) === undefined) {\n if (\n optionalText(options.region) !== undefined ||\n optionalText(options.org) !== undefined ||\n optionalText(options.space) !== undefined\n ) {\n readRequiredOption(options.app, \"--app or selector\");\n }\n return undefined;\n }\n return await resolveSessionKey(options);\n}\n\nfunction writeStopResult(result: Awaited<ReturnType<typeof stopDebugger>>): void {\n if (result === undefined) {\n return;\n }\n if (result.warning !== undefined) {\n nodeProcess.stderr.write(`[cf-debugger] warning: ${result.warning}\\n`);\n }\n if (result.pending) {\n nodeProcess.stdout.write(\n `Stop requested for session ${result.sessionId} (${result.app}, phase ${result.status}).\\n`,\n );\n } else if (result.stale) {\n nodeProcess.stdout.write(\n `Removed ${result.forced ? \"forced \" : \"\"}stale session ${result.sessionId} ` +\n `(${result.app}, port ${result.localPort.toString()}).\\n`,\n );\n } else {\n nodeProcess.stdout.write(\n `Stopped session ${result.sessionId} (${result.app}, port ${result.localPort.toString()}).\\n`,\n );\n }\n}\n\nfunction rejectScopedStopAll(\n selector: string | undefined,\n options: StopCommandOptions,\n command: Command,\n): void {\n if (\n options.all === true &&\n (\n selector !== undefined ||\n STOP_SCOPE_OPTION_NAMES.some((name) => command.getOptionValueSource(name) === \"cli\")\n )\n ) {\n throw new CfDebuggerError(\n \"UNSAFE_INPUT\",\n \"--all cannot be combined with a selector, --session-id, or target selector options.\",\n );\n }\n}\n\nasync function handleStopAll(force: boolean): Promise<void> {\n const summary = await stopAllDebuggers(force);\n for (const outcome of summary.outcomes) {\n if (outcome.error === undefined) {\n writeStopResult(outcome.result);\n } else {\n nodeProcess.stderr.write(\n `Failed ${outcome.sessionId} (${outcome.app}): ${outcome.error.message}\\n`,\n );\n }\n }\n nodeProcess.stdout.write(\n `Stop summary: ${summary.stopped.toString()} stopped, ${summary.forced.toString()} forced, ` +\n `${summary.stale.toString()} stale, ` +\n `${summary.pending.toString()} pending, ${summary.failed.toString()} failed.\\n`,\n );\n const exitCode = stopAllExitCode(\n summary.outcomes.flatMap((outcome) =>\n outcome.error === undefined ? [] : [outcome.error]),\n );\n if (exitCode !== undefined) {\n nodeProcess.exitCode = exitCode;\n }\n}\n\nasync function handleStop(\n selector: string | undefined,\n rawOptions: StopCommandOptions,\n command: Command,\n): Promise<void> {\n rejectScopedStopAll(selector, rawOptions, command);\n const options = mergeSelector(selector, rawOptions);\n if (options.all === true) {\n await handleStopAll(options.force === true);\n return;\n }\n const key = await resolveOptionalSessionKey(options);\n const result = await stopDebugger({\n ...(options.sessionId === undefined ? {} : { sessionId: options.sessionId }),\n ...(key === undefined ? {} : { key }),\n force: options.force === true,\n });\n if (result === undefined) {\n throw new CfDebuggerError(\n \"SESSION_NOT_FOUND\",\n \"No matching session. Use `cf-debugger list` and pass --session-id or an exact key.\",\n );\n }\n writeStopResult(result);\n}\n\nasync function handleList(): Promise<void> {\n nodeProcess.stdout.write(`${JSON.stringify(await listSessions(), null, 2)}\\n`);\n}\n\nasync function handleStatus(\n selector: string | undefined,\n rawOptions: StatusCommandOptions,\n): Promise<void> {\n const options = mergeSelector(selector, rawOptions);\n const session = options.sessionId === undefined\n ? await getSession(await resolveSessionKey(options))\n : (await listSessions()).find((candidate) => candidate.sessionId === options.sessionId);\n nodeProcess.stdout.write(`${JSON.stringify(session ?? null, null, 2)}\\n`);\n}\n\nasync function handleDoctor(cleanup: boolean): Promise<void> {\n const report = await runDoctor({ cleanup });\n nodeProcess.stdout.write(`${JSON.stringify(report, null, 2)}\\n`);\n}\n\nfunction registerStartCommand(program: Command): void {\n program\n .command(\"start\")\n .description(\"Open a debug tunnel for one app\")\n .argument(\"[selector]\", \"Optional <app> or <region>/<org>/<space>/<app>\")\n .option(\"--region <key>\", \"CF region key (default: current cf target)\")\n .option(\"--api-endpoint <url>\", \"Absolute HTTPS CF API endpoint override\")\n .option(\"--org <name>\", \"CF org (default: current cf target)\")\n .option(\"--space <name>\", \"CF space (default: current cf target)\")\n .option(\"--app <name>\", \"CF app name\")\n .option(\"--process <name>\", \"CF process name\", \"web\")\n .option(\"-i, --instance <index>\", \"CF process instance\", \"0\")\n .option(\"--node-pid <pid>\", \"Explicit remote Node.js PID\")\n .option(\"--port <number>\", \"Preferred local port\")\n .option(\n \"--remote-port <number>\",\n \"Port where cf-debugger looks for an existing remote inspector\",\n DEFAULT_NODE_INSPECTOR_PORT.toString(),\n )\n .option(\"--timeout <seconds>\", \"Tunnel/inspector readiness timeout\", \"180\")\n .option(\"--startup-timeout <seconds>\", \"Overall startup deadline\", \"300\")\n .option(\n \"--allow-ssh-enable-restart\",\n \"Allow app-level SSH enablement and an app restart (opt-in)\",\n )\n .option(\n \"--no-ssh-enable-restart\",\n \"Forbid app-level SSH enablement and restart, overriding flag/environment\",\n )\n .option(\"--verbose\", \"Print status transitions and tunnel diagnostics\", false)\n .action(async (selector: string | undefined, options: StartCommandOptions): Promise<void> => {\n await handleStart(selector, options);\n });\n}\n\nfunction addKeyOptions(command: Command): Command {\n return command\n .option(\"--region <key>\")\n .option(\"--api-endpoint <url>\", \"Exact API endpoint selector\")\n .option(\"--org <name>\")\n .option(\"--space <name>\")\n .option(\"--app <name>\")\n .option(\"--process <name>\", \"CF process name\", \"web\")\n .option(\"-i, --instance <index>\", \"CF process instance\", \"0\")\n .option(\"--node-pid <pid>\", \"Exact remote Node PID selector\");\n}\n\nfunction registerStopCommand(program: Command): void {\n addKeyOptions(\n program\n .command(\"stop\")\n .description(\"Stop one session by exact key/id, or every session with --all\")\n .argument(\"[selector]\", \"Optional <app> or <region>/<org>/<space>/<app>\"),\n )\n .option(\"--session-id <id>\")\n .option(\n \"--all\",\n \"Attempt every active session; cannot be combined with session selectors\",\n false,\n )\n .option(\n \"--force\",\n \"Forget unverifiable state first; best-effort remove its exact owned CF home; \" +\n \"never signal an unverified process\",\n false,\n )\n .action(\n async (\n selector: string | undefined,\n options: StopCommandOptions,\n command: Command,\n ): Promise<void> => {\n await handleStop(selector, options, command);\n },\n );\n}\n\nfunction registerReadCommands(program: Command): void {\n program\n .command(\"list\")\n .description(\"Print every active debugger session as JSON\")\n .action(async (): Promise<void> => {\n await handleList();\n });\n addKeyOptions(\n program\n .command(\"status\")\n .description(\"Print one exact session as JSON; null only when no session matches\")\n .argument(\"[selector]\", \"Optional <app> or <region>/<org>/<space>/<app>\"),\n )\n .option(\"--session-id <id>\", \"Exact session ID selector\")\n .action(async (selector: string | undefined, options: StatusCommandOptions): Promise<void> => {\n await handleStatus(selector, options);\n });\n program\n .command(\"doctor\")\n .description(\"Report session health, orphan homes/ports, temp files, and legacy credentials\")\n .option(\"--cleanup\", \"Remove only safe orphan v2 homes and stale v2 temp/lock files\", false)\n .action(async (options: { readonly cleanup?: boolean }): Promise<void> => {\n await handleDoctor(options.cleanup === true);\n });\n}\n\nexport async function main(argv: readonly string[]): Promise<void> {\n const program = new Command()\n .name(\"cf-debugger\")\n .description(\"Open an SSH debug tunnel to a SAP BTP Cloud Foundry Node.js inspector\")\n .version(readPackageVersion());\n registerStartCommand(program);\n registerStopCommand(program);\n registerReadCommands(program);\n await program.parseAsync([...argv]);\n}\n\nfunction writeTopLevelError(error: unknown): void {\n if (error instanceof CfDebuggerError) {\n if (error.code === \"ABORTED\") {\n nodeProcess.stderr.write(`Aborted: ${error.message}\\n`);\n nodeProcess.exit(cliErrorExitCode(error));\n }\n nodeProcess.stderr.write(`Error [${error.code}]: ${error.message}\\n`);\n if (error.stderr !== undefined && error.stderr.trim().length > 0) {\n nodeProcess.stderr.write(`[cf-debugger transport]\\n${error.stderr.trim()}\\n`);\n }\n nodeProcess.exit(cliErrorExitCode(error));\n }\n if (hasTunnelTerminationFailure(error)) {\n const message = error instanceof Error ? error.message : String(error);\n nodeProcess.stderr.write(`Error during cleanup: ${message}\\n`);\n nodeProcess.exit(CLEANUP_FAILURE_EXIT_CODE);\n }\n const message = error instanceof Error ? error.message : String(error);\n nodeProcess.stderr.write(`Error: ${message}\\n`);\n nodeProcess.exit(cliErrorExitCode(error));\n}\n\ntry {\n await main(nodeProcess.argv);\n} catch (error: unknown) {\n writeTopLevelError(error);\n}\n","import { CfDebuggerError } from \"./types.js\";\n\nexport function readRequiredOption(\n value: string | undefined,\n flag: string,\n): string {\n if (value === undefined || value.trim().length === 0) {\n throw new CfDebuggerError(\"UNSAFE_INPUT\", `Missing required option ${flag}.`);\n }\n return value;\n}\n\nexport function parseOptionalInteger(\n raw: string | undefined,\n label: string,\n minimum: number,\n maximum = Number.MAX_SAFE_INTEGER,\n): number | undefined {\n if (raw === undefined) {\n return undefined;\n }\n if (!/^\\d+$/.test(raw)) {\n throw new CfDebuggerError(\"UNSAFE_INPUT\", `${label} must be an integer.`);\n }\n const value = Number(raw);\n if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {\n throw new CfDebuggerError(\n \"UNSAFE_INPUT\",\n `${label} must be from ${minimum.toString()} to ${maximum.toString()}.`,\n );\n }\n return value;\n}\n\nexport function parseSeconds(\n raw: string | undefined,\n label: string,\n): number | undefined {\n const seconds = parseOptionalInteger(raw, label, 1);\n return seconds === undefined ? undefined : seconds * 1000;\n}\n","import { readFileSync } from \"node:fs\";\n\nimport { CfDebuggerError } from \"./types.js\";\n\nexport function readPackageVersion(): string {\n const parsed: unknown = JSON.parse(\n readFileSync(new URL(\"../package.json\", import.meta.url), \"utf8\"),\n );\n if (typeof parsed !== \"object\" || parsed === null) {\n throw new CfDebuggerError(\"PACKAGE_METADATA_INVALID\", \"Package metadata is not an object.\");\n }\n const version: unknown = Reflect.get(parsed, \"version\");\n if (typeof version !== \"string\" || version.length === 0) {\n throw new CfDebuggerError(\"PACKAGE_METADATA_INVALID\", \"Package metadata has no version.\");\n }\n return version;\n}\n"],"mappings":"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","names":["message"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -71,6 +71,28 @@ interface SessionHealthVerdict {
|
|
|
71
71
|
|
|
72
72
|
declare function sessionKeyString(key: SessionKey): string;
|
|
73
73
|
|
|
74
|
+
type DoctorLegacyLiveness = "alive" | "not-running" | "unverified";
|
|
75
|
+
interface DoctorLegacySessionFinding {
|
|
76
|
+
readonly index: number;
|
|
77
|
+
readonly sessionId?: string;
|
|
78
|
+
readonly pid?: number;
|
|
79
|
+
readonly localPort?: number;
|
|
80
|
+
readonly hostname?: string;
|
|
81
|
+
readonly liveness: DoctorLegacyLiveness;
|
|
82
|
+
readonly reason?: string;
|
|
83
|
+
}
|
|
84
|
+
interface DoctorLegacyFinding {
|
|
85
|
+
readonly statePath: string;
|
|
86
|
+
readonly statePresent: boolean;
|
|
87
|
+
readonly homesPath: string;
|
|
88
|
+
readonly homesPresent: boolean;
|
|
89
|
+
readonly homeCount?: number;
|
|
90
|
+
readonly sessions?: readonly DoctorLegacySessionFinding[];
|
|
91
|
+
readonly inspectionWarnings?: readonly string[];
|
|
92
|
+
readonly warning?: string;
|
|
93
|
+
readonly manualRemovalCommand?: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
74
96
|
type DoctorCleanupStatus = "not-requested" | "not-eligible" | "removed" | "skipped" | "failed";
|
|
75
97
|
interface DoctorSessionFinding {
|
|
76
98
|
readonly session: ActiveSession;
|
|
@@ -81,6 +103,7 @@ interface DoctorHomeFinding {
|
|
|
81
103
|
readonly path: string;
|
|
82
104
|
readonly cleanupEligible: boolean;
|
|
83
105
|
readonly cleanupStatus: DoctorCleanupStatus;
|
|
106
|
+
readonly reason?: string;
|
|
84
107
|
}
|
|
85
108
|
interface DoctorPortFinding {
|
|
86
109
|
readonly port: number;
|
|
@@ -96,13 +119,7 @@ interface DoctorArtifactFinding {
|
|
|
96
119
|
readonly cleanupEligible: boolean;
|
|
97
120
|
readonly cleanupStatus: DoctorCleanupStatus;
|
|
98
121
|
readonly cleanupError?: string;
|
|
99
|
-
|
|
100
|
-
interface DoctorLegacyFinding {
|
|
101
|
-
readonly statePath: string;
|
|
102
|
-
readonly statePresent: boolean;
|
|
103
|
-
readonly homesPath: string;
|
|
104
|
-
readonly homesPresent: boolean;
|
|
105
|
-
readonly warning?: string;
|
|
122
|
+
readonly note?: string;
|
|
106
123
|
readonly manualRemovalCommand?: string;
|
|
107
124
|
}
|
|
108
125
|
interface DoctorReport {
|
|
@@ -133,13 +150,14 @@ interface StopDebuggerResult extends ActiveSession {
|
|
|
133
150
|
interface StopAllOutcome {
|
|
134
151
|
readonly sessionId: string;
|
|
135
152
|
readonly app: string;
|
|
136
|
-
readonly status: "failed" | "pending" | "stale" | "stopped";
|
|
153
|
+
readonly status: "failed" | "forced" | "pending" | "stale" | "stopped";
|
|
137
154
|
readonly result?: StopDebuggerResult;
|
|
138
155
|
readonly error?: CfDebuggerError;
|
|
139
156
|
}
|
|
140
157
|
interface StopAllResult {
|
|
141
158
|
readonly outcomes: readonly StopAllOutcome[];
|
|
142
159
|
readonly failed: number;
|
|
160
|
+
readonly forced: number;
|
|
143
161
|
readonly pending: number;
|
|
144
162
|
readonly stale: number;
|
|
145
163
|
readonly stopped: number;
|
|
@@ -188,4 +206,4 @@ declare function resolveNodeTarget(input: NodeTargetSelectors): ResolvedNodeTarg
|
|
|
188
206
|
declare function buildNodeInspectorCommand(nodePid?: number, remotePort?: number): string;
|
|
189
207
|
declare function parseNodeInspectorMarkers(stdout: string, remotePort?: number): NodeProcessSelection;
|
|
190
208
|
|
|
191
|
-
export { type ActiveSession, CfDebuggerError, type CurrentCfTarget, type CurrentCfTargetReadOptions, DEFAULT_NODE_INSPECTOR_PORT, type DebuggerHandle, type DoctorArtifactFinding, type DoctorArtifactKind, type DoctorCleanupStatus, type DoctorHomeFinding, type DoctorLegacyFinding, type DoctorOptions, type DoctorPortFinding, type DoctorReport, type DoctorSessionFinding, type NodeProcessSelection, type NodeTargetSelectors, type ResolvedNodeTarget, type ResolvedSessionKey, type SessionKey, type SessionStatus, type StartDebuggerOptions, type StopAllOutcome, type StopAllResult, type StopDebuggerResult, type StopOptions, buildNodeInspectorCommand, getSession, listKnownRegionKeys, listSessions, parseCurrentCfTarget, parseNodeInspectorMarkers, readCurrentCfTarget, regionKeyForApiEndpoint, regionKeyFromSapApiEndpoint, requireCurrentCfRegion, resolveApiEndpoint, resolveNodeTarget, runDoctor, sessionKeyString, startDebugger, stopAllDebuggers, stopDebugger };
|
|
209
|
+
export { type ActiveSession, CfDebuggerError, type CurrentCfTarget, type CurrentCfTargetReadOptions, DEFAULT_NODE_INSPECTOR_PORT, type DebuggerHandle, type DoctorArtifactFinding, type DoctorArtifactKind, type DoctorCleanupStatus, type DoctorHomeFinding, type DoctorLegacyFinding, type DoctorLegacyLiveness, type DoctorLegacySessionFinding, type DoctorOptions, type DoctorPortFinding, type DoctorReport, type DoctorSessionFinding, type NodeProcessSelection, type NodeTargetSelectors, type ResolvedNodeTarget, type ResolvedSessionKey, type SessionKey, type SessionStatus, type StartDebuggerOptions, type StopAllOutcome, type StopAllResult, type StopDebuggerResult, type StopOptions, buildNodeInspectorCommand, getSession, listKnownRegionKeys, listSessions, parseCurrentCfTarget, parseNodeInspectorMarkers, readCurrentCfTarget, regionKeyForApiEndpoint, regionKeyFromSapApiEndpoint, requireCurrentCfRegion, resolveApiEndpoint, resolveNodeTarget, runDoctor, sessionKeyString, startDebugger, stopAllDebuggers, stopDebugger };
|
package/dist/index.js
CHANGED
package/package.json
CHANGED