@saptools/cf-debugger 0.1.6 → 0.1.8
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 +7 -3
- package/dist/cli.js +35 -18
- package/dist/cli.js.map +1 -1
- package/dist/index.js +34 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ Open a tunnel for one app and keep running until interrupted.
|
|
|
85
85
|
```bash
|
|
86
86
|
cf-debugger start --region eu10 --org my-org --space dev --app my-app
|
|
87
87
|
cf-debugger start --region eu10 --org my-org --space dev --app my-app --port 9230
|
|
88
|
-
cf-debugger start --region eu10 --org my-org --space dev --app my-app --timeout
|
|
88
|
+
cf-debugger start --region eu10 --org my-org --space dev --app my-app --timeout 180 --verbose
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
| Flag | Description |
|
|
@@ -95,9 +95,13 @@ cf-debugger start --region eu10 --org my-org --space dev --app my-app --timeout
|
|
|
95
95
|
| `--space <name>` | **Required.** CF space name |
|
|
96
96
|
| `--app <name>` | **Required.** CF app name |
|
|
97
97
|
| `--port <number>` | Preferred local port (auto-assigned in `20000–20999` if omitted) |
|
|
98
|
-
| `--timeout <seconds>` | Tunnel-ready timeout (default: `
|
|
98
|
+
| `--timeout <seconds>` | Tunnel-ready timeout (default: `180`) |
|
|
99
99
|
| `--verbose` | Print every status transition |
|
|
100
100
|
|
|
101
|
+
Cloud Foundry startup commands (`api`, `auth`, `target`, SSH checks, app
|
|
102
|
+
restart, and the one-shot SIGUSR1 SSH command) each allow up to 180 seconds.
|
|
103
|
+
`--timeout` controls the subsequent local tunnel-readiness probe separately.
|
|
104
|
+
|
|
101
105
|
### ⏹️ `cf-debugger stop`
|
|
102
106
|
|
|
103
107
|
Stop a specific session or everything at once.
|
|
@@ -188,7 +192,7 @@ await handle.dispose();
|
|
|
188
192
|
| `CF_LOGIN_FAILED` | `cf api` / `cf auth` rejected the credentials |
|
|
189
193
|
| `CF_TARGET_FAILED` | Org or space not reachable |
|
|
190
194
|
| `SSH_NOT_ENABLED` | SSH disabled at space or app level and could not be enabled |
|
|
191
|
-
| `USR1_SIGNAL_FAILED` | Remote `kill -s USR1`
|
|
195
|
+
| `USR1_SIGNAL_FAILED` | Remote `kill -s USR1` failed, timed out, or was terminated by a signal |
|
|
192
196
|
| `TUNNEL_NOT_READY` | Inspector didn't respond on port 9229 before timeout |
|
|
193
197
|
| `PORT_UNAVAILABLE` | Preferred local port is taken and could not be freed |
|
|
194
198
|
|
package/dist/cli.js
CHANGED
|
@@ -27,7 +27,7 @@ import { execFile } from "child_process";
|
|
|
27
27
|
import { promisify } from "util";
|
|
28
28
|
var execFileAsync = promisify(execFile);
|
|
29
29
|
var MAX_BUFFER = 16 * 1024 * 1024;
|
|
30
|
-
var
|
|
30
|
+
var DEFAULT_CF_COMMAND_TIMEOUT_MS = 18e4;
|
|
31
31
|
var REDACTED_ARG = "<redacted>";
|
|
32
32
|
function buildEnv(cfHome) {
|
|
33
33
|
return { ...process.env, CF_HOME: cfHome };
|
|
@@ -50,7 +50,7 @@ function formatArgsForError(args) {
|
|
|
50
50
|
}
|
|
51
51
|
return args.map((arg, index) => index === 0 ? arg : REDACTED_ARG).join(" ");
|
|
52
52
|
}
|
|
53
|
-
async function runCf(args, context, timeoutMs =
|
|
53
|
+
async function runCf(args, context, timeoutMs = DEFAULT_CF_COMMAND_TIMEOUT_MS) {
|
|
54
54
|
try {
|
|
55
55
|
const { stdout } = await execFileAsync(resolveBin(context), [...args], {
|
|
56
56
|
env: buildEnv(context.cfHome),
|
|
@@ -72,7 +72,6 @@ async function runCf(args, context, timeoutMs = CF_CLI_TIMEOUT_MS) {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// src/cloud-foundry/commands.ts
|
|
75
|
-
var CF_RESTART_TIMEOUT_MS = 12e4;
|
|
76
75
|
var CF_AUTH_MAX_ATTEMPTS = 3;
|
|
77
76
|
async function cfApi(apiEndpoint, context) {
|
|
78
77
|
await runCf(["api", apiEndpoint], context);
|
|
@@ -137,13 +136,12 @@ async function cfEnableSsh(appName, context) {
|
|
|
137
136
|
}
|
|
138
137
|
}
|
|
139
138
|
async function cfRestartApp(appName, context) {
|
|
140
|
-
await runCf(["restart", appName], context
|
|
139
|
+
await runCf(["restart", appName], context);
|
|
141
140
|
}
|
|
142
141
|
|
|
143
142
|
// src/cloud-foundry/ssh.ts
|
|
144
143
|
import { spawn } from "child_process";
|
|
145
|
-
|
|
146
|
-
async function cfSshOneShot(appName, command, context) {
|
|
144
|
+
async function cfSshOneShot(appName, command, context, timeoutMs = DEFAULT_CF_COMMAND_TIMEOUT_MS) {
|
|
147
145
|
return await new Promise((resolve) => {
|
|
148
146
|
const child = spawn(resolveBin(context), ["ssh", appName, "-c", command], {
|
|
149
147
|
env: buildEnv(context.cfHome),
|
|
@@ -160,18 +158,20 @@ async function cfSshOneShot(appName, command, context) {
|
|
|
160
158
|
child.kill();
|
|
161
159
|
} catch {
|
|
162
160
|
}
|
|
163
|
-
resolve({ exitCode: null, stderr: stderrBuf });
|
|
164
|
-
},
|
|
161
|
+
resolve({ exitCode: null, stderr: stderrBuf, timedOutAfterMs: timeoutMs });
|
|
162
|
+
}, timeoutMs);
|
|
165
163
|
child.stderr.on("data", (data) => {
|
|
166
164
|
stderrBuf += data.toString();
|
|
167
165
|
});
|
|
168
|
-
child.on("close", (code) => {
|
|
166
|
+
child.on("close", (code, signal) => {
|
|
169
167
|
if (settled) {
|
|
170
168
|
return;
|
|
171
169
|
}
|
|
172
170
|
settled = true;
|
|
173
171
|
clearTimeout(timeout);
|
|
174
|
-
resolve(
|
|
172
|
+
resolve(
|
|
173
|
+
signal === null ? { exitCode: code, stderr: stderrBuf } : { exitCode: code, stderr: stderrBuf, signal }
|
|
174
|
+
);
|
|
175
175
|
});
|
|
176
176
|
child.on("error", (err) => {
|
|
177
177
|
if (settled) {
|
|
@@ -522,6 +522,12 @@ async function readAndPruneLocked() {
|
|
|
522
522
|
}
|
|
523
523
|
return { sessions: pruned, removed };
|
|
524
524
|
}
|
|
525
|
+
async function readSessionSnapshot() {
|
|
526
|
+
return await withFileLock(stateLockPath(), async () => {
|
|
527
|
+
const raw = await readStateRaw();
|
|
528
|
+
return raw.sessions;
|
|
529
|
+
});
|
|
530
|
+
}
|
|
525
531
|
async function readAndPruneActiveSessions() {
|
|
526
532
|
return await withFileLock(stateLockPath(), readAndPruneLocked);
|
|
527
533
|
}
|
|
@@ -674,7 +680,7 @@ async function removeSession(sessionId) {
|
|
|
674
680
|
}
|
|
675
681
|
|
|
676
682
|
// src/debug-session/constants.ts
|
|
677
|
-
var DEFAULT_TUNNEL_READY_TIMEOUT_MS =
|
|
683
|
+
var DEFAULT_TUNNEL_READY_TIMEOUT_MS = 18e4;
|
|
678
684
|
var POST_USR1_DELAY_MS = 300;
|
|
679
685
|
var PORT_CLEANUP_DELAY_MS = 600;
|
|
680
686
|
var CHILD_SIGTERM_GRACE_MS = 2e3;
|
|
@@ -737,6 +743,19 @@ async function killProcessGroupOrProc(child, timeoutMs = CHILD_SIGTERM_GRACE_MS)
|
|
|
737
743
|
}
|
|
738
744
|
|
|
739
745
|
// src/debug-session/start.ts
|
|
746
|
+
function signalFailureDetail(result) {
|
|
747
|
+
if (result.timedOutAfterMs !== void 0) {
|
|
748
|
+
return `timed out after ${(result.timedOutAfterMs / 1e3).toString()}s`;
|
|
749
|
+
}
|
|
750
|
+
const stderr = result.stderr.trim();
|
|
751
|
+
if (stderr.length > 0) {
|
|
752
|
+
return stderr;
|
|
753
|
+
}
|
|
754
|
+
if (result.signal !== void 0) {
|
|
755
|
+
return `terminated by signal ${result.signal}`;
|
|
756
|
+
}
|
|
757
|
+
return `exit code ${String(result.exitCode)}`;
|
|
758
|
+
}
|
|
740
759
|
function checkAbort(signal) {
|
|
741
760
|
if (signal?.aborted) {
|
|
742
761
|
throw new CfDebuggerError("ABORTED", "Operation aborted by caller");
|
|
@@ -796,10 +815,9 @@ async function signalRemoteNode(options, context, sessionId, emit) {
|
|
|
796
815
|
if (signalResult.exitCode === 0) {
|
|
797
816
|
return;
|
|
798
817
|
}
|
|
799
|
-
const detail = signalResult.stderr.trim().length > 0 ? signalResult.stderr.trim() : `exit code ${String(signalResult.exitCode)}`;
|
|
800
818
|
throw new CfDebuggerError(
|
|
801
819
|
"USR1_SIGNAL_FAILED",
|
|
802
|
-
`Failed to send SIGUSR1 to the Node.js process on ${options.app}: ${
|
|
820
|
+
`Failed to send SIGUSR1 to the Node.js process on ${options.app}: ${signalFailureDetail(signalResult)}`,
|
|
803
821
|
signalResult.stderr
|
|
804
822
|
);
|
|
805
823
|
}
|
|
@@ -826,10 +844,9 @@ async function retryRemoteSignal(options, context, sessionId, emit) {
|
|
|
826
844
|
if (retrySignalResult.exitCode === 0) {
|
|
827
845
|
return;
|
|
828
846
|
}
|
|
829
|
-
const detail = retrySignalResult.stderr.trim().length > 0 ? retrySignalResult.stderr.trim() : `exit code ${String(retrySignalResult.exitCode)}`;
|
|
830
847
|
throw new CfDebuggerError(
|
|
831
848
|
"USR1_SIGNAL_FAILED",
|
|
832
|
-
`Failed to send SIGUSR1 to the Node.js process on ${options.app} after enabling SSH: ${
|
|
849
|
+
`Failed to send SIGUSR1 to the Node.js process on ${options.app} after enabling SSH: ${signalFailureDetail(retrySignalResult)}`,
|
|
833
850
|
retrySignalResult.stderr
|
|
834
851
|
);
|
|
835
852
|
}
|
|
@@ -1020,10 +1037,10 @@ async function stopAllDebuggers() {
|
|
|
1020
1037
|
return stopped;
|
|
1021
1038
|
}
|
|
1022
1039
|
async function listSessions() {
|
|
1023
|
-
return await
|
|
1040
|
+
return await readSessionSnapshot();
|
|
1024
1041
|
}
|
|
1025
1042
|
async function getSession(key) {
|
|
1026
|
-
const sessions = await
|
|
1043
|
+
const sessions = await readSessionSnapshot();
|
|
1027
1044
|
return sessions.find((s) => matchesKey(s, key));
|
|
1028
1045
|
}
|
|
1029
1046
|
|
|
@@ -1197,7 +1214,7 @@ async function handleStatus(opts) {
|
|
|
1197
1214
|
async function main(argv) {
|
|
1198
1215
|
const program = new Command();
|
|
1199
1216
|
program.name("cf-debugger").description("Open an SSH debug tunnel to a SAP BTP Cloud Foundry app's Node.js inspector");
|
|
1200
|
-
program.command("start").description("Open a debug tunnel for one app").requiredOption("--region <key>", "CF region key (e.g. eu10)").requiredOption("--org <name>", "CF org name").requiredOption("--space <name>", "CF space name").requiredOption("--app <name>", "CF app name").option("--port <number>", "Preferred local port (auto-assigned if omitted)").option("--timeout <seconds>", "Tunnel-ready timeout in seconds (default:
|
|
1217
|
+
program.command("start").description("Open a debug tunnel for one app").requiredOption("--region <key>", "CF region key (e.g. eu10)").requiredOption("--org <name>", "CF org name").requiredOption("--space <name>", "CF space name").requiredOption("--app <name>", "CF app name").option("--port <number>", "Preferred local port (auto-assigned if omitted)").option("--timeout <seconds>", "Tunnel-ready timeout in seconds (default: 180)").option("--verbose", "Print status transitions", false).action(async (opts) => {
|
|
1201
1218
|
await handleStart(opts);
|
|
1202
1219
|
});
|
|
1203
1220
|
program.command("stop").description("Stop one session (by key or id) or all sessions with --all").option("--region <key>").option("--org <name>").option("--space <name>").option("--app <name>").option("--session-id <id>").option("--all", "Stop every active session", false).action(async (opts) => {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/debug-session/start.ts","../src/types.ts","../src/cloud-foundry/execute.ts","../src/cloud-foundry/commands.ts","../src/cloud-foundry/ssh.ts","../src/paths.ts","../src/network/ports.ts","../src/regions.ts","../src/session-state/store.ts","../src/lock.ts","../src/debug-session/constants.ts","../src/debug-session/orphans.ts","../src/debug-session/processes.ts","../src/debug-session/sessions.ts"],"sourcesContent":["import process from \"node:process\";\n\nimport { Command } from \"commander\";\n\nimport {\n getSession,\n listSessions,\n startDebugger,\n stopAllDebuggers,\n stopDebugger,\n} from \"./debugger.js\";\nimport type { SessionKey, SessionStatus } from \"./types.js\";\nimport { CfDebuggerError } from \"./types.js\";\n\nfunction readRequiredOption(value: string | undefined, flag: string): string {\n if (value === undefined || value === \"\") {\n process.stderr.write(`Missing required option ${flag}\\n`);\n process.exit(1);\n }\n return value;\n}\n\nfunction parseOptionalPort(raw: string | undefined): number | undefined {\n if (raw === undefined) {\n return undefined;\n }\n const port = Number.parseInt(raw, 10);\n if (Number.isNaN(port) || port <= 0 || port > 65_535) {\n process.stderr.write(`Invalid port: ${raw}\\n`);\n process.exit(1);\n }\n return port;\n}\n\nfunction parseOptionalTimeout(raw: string | undefined): number | undefined {\n if (raw === undefined) {\n return undefined;\n }\n const seconds = Number.parseInt(raw, 10);\n if (Number.isNaN(seconds) || seconds <= 0) {\n process.stderr.write(`Invalid timeout: ${raw}\\n`);\n process.exit(1);\n }\n return seconds * 1000;\n}\n\ninterface StartCommandOptions {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n readonly port?: string;\n readonly timeout?: string;\n readonly verbose?: boolean;\n}\n\ninterface StopCommandOptions {\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}\n\ninterface StatusCommandOptions {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n}\n\nfunction logStatus(verbose: boolean, status: SessionStatus, message?: string): void {\n if (verbose) {\n const suffix = message === undefined ? \"\" : `: ${message}`;\n process.stdout.write(`[cf-debugger] ${status}${suffix}\\n`);\n }\n}\n\nasync function handleStart(opts: StartCommandOptions): Promise<void> {\n const region = readRequiredOption(opts.region, \"--region\");\n const org = readRequiredOption(opts.org, \"--org\");\n const space = readRequiredOption(opts.space, \"--space\");\n const app = readRequiredOption(opts.app, \"--app\");\n const verbose = opts.verbose ?? false;\n\n const preferredPort = parseOptionalPort(opts.port);\n const tunnelReadyTimeoutMs = parseOptionalTimeout(opts.timeout);\n\n const abortController = new AbortController();\n const onStartupSignal = (exitCode: number) => (): void => {\n abortController.abort();\n process.stderr.write(`\\nAborting startup for ${app}...\\n`);\n setTimeout(() => {\n process.exit(exitCode);\n }, 5_000).unref();\n };\n const startupSigint = onStartupSignal(130);\n const startupSigterm = onStartupSignal(143);\n process.on(\"SIGINT\", startupSigint);\n process.on(\"SIGTERM\", startupSigterm);\n\n let handle;\n try {\n handle = await startDebugger({\n region,\n org,\n space,\n app,\n verbose,\n signal: abortController.signal,\n ...(preferredPort === undefined ? {} : { preferredPort }),\n ...(tunnelReadyTimeoutMs === undefined ? {} : { tunnelReadyTimeoutMs }),\n onStatus: (status, message) => {\n logStatus(verbose, status, message);\n },\n });\n } finally {\n process.off(\"SIGINT\", startupSigint);\n process.off(\"SIGTERM\", startupSigterm);\n }\n\n process.stdout.write(\n `Debugger ready for ${app} (${region}/${org}/${space}).\\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 ` PID: ${handle.session.pid.toString()}\\n` +\n `Press Ctrl+C to stop.\\n`,\n );\n\n let disposePromise: Promise<void> | undefined;\n const dispose = async (): Promise<void> => {\n disposePromise ??= (async (): Promise<void> => {\n process.stdout.write(`\\nStopping debugger for ${app}...\\n`);\n try {\n await handle.dispose();\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`Error during stop: ${msg}\\n`);\n }\n })();\n await disposePromise;\n };\n\n process.on(\"SIGINT\", () => {\n void dispose().then(() => {\n process.exit(130);\n });\n });\n process.on(\"SIGTERM\", () => {\n void dispose().then(() => {\n process.exit(143);\n });\n });\n\n const code = await handle.waitForExit();\n await dispose();\n process.exit(code ?? 0);\n}\n\nfunction resolveKeyFromOpts(opts: StopCommandOptions): SessionKey | undefined {\n if (\n opts.region !== undefined &&\n opts.org !== undefined &&\n opts.space !== undefined &&\n opts.app !== undefined\n ) {\n return {\n region: opts.region,\n org: opts.org,\n space: opts.space,\n app: opts.app,\n };\n }\n return undefined;\n}\n\nasync function handleStop(opts: StopCommandOptions): Promise<void> {\n if (opts.all === true) {\n const count = await stopAllDebuggers();\n process.stdout.write(`Stopped ${count.toString()} session(s).\\n`);\n return;\n }\n const key = resolveKeyFromOpts(opts);\n const result = await stopDebugger({\n ...(opts.sessionId === undefined ? {} : { sessionId: opts.sessionId }),\n ...(key === undefined ? {} : { key }),\n });\n if (result === undefined) {\n process.stderr.write(\"No matching session found.\\n\");\n process.exit(1);\n }\n process.stdout.write(\n `Stopped session ${result.sessionId} (${result.app}, port ${result.localPort.toString()}).\\n`,\n );\n}\n\nasync function handleList(): Promise<void> {\n const sessions = await listSessions();\n process.stdout.write(`${JSON.stringify(sessions, null, 2)}\\n`);\n}\n\nasync function handleStatus(opts: StatusCommandOptions): Promise<void> {\n const session = await getSession({\n region: opts.region,\n org: opts.org,\n space: opts.space,\n app: opts.app,\n });\n process.stdout.write(`${JSON.stringify(session ?? null, null, 2)}\\n`);\n}\n\nexport async function main(argv: readonly string[]): Promise<void> {\n const program = new Command();\n\n program\n .name(\"cf-debugger\")\n .description(\"Open an SSH debug tunnel to a SAP BTP Cloud Foundry app's Node.js inspector\");\n\n program\n .command(\"start\")\n .description(\"Open a debug tunnel for one app\")\n .requiredOption(\"--region <key>\", \"CF region key (e.g. eu10)\")\n .requiredOption(\"--org <name>\", \"CF org name\")\n .requiredOption(\"--space <name>\", \"CF space name\")\n .requiredOption(\"--app <name>\", \"CF app name\")\n .option(\"--port <number>\", \"Preferred local port (auto-assigned if omitted)\")\n .option(\"--timeout <seconds>\", \"Tunnel-ready timeout in seconds (default: 30)\")\n .option(\"--verbose\", \"Print status transitions\", false)\n .action(async (opts: StartCommandOptions): Promise<void> => {\n await handleStart(opts);\n });\n\n program\n .command(\"stop\")\n .description(\"Stop one session (by key or id) or all sessions with --all\")\n .option(\"--region <key>\")\n .option(\"--org <name>\")\n .option(\"--space <name>\")\n .option(\"--app <name>\")\n .option(\"--session-id <id>\")\n .option(\"--all\", \"Stop every active session\", false)\n .action(async (opts: StopCommandOptions): Promise<void> => {\n await handleStop(opts);\n });\n\n program\n .command(\"list\")\n .description(\"Print every active debugger session as JSON\")\n .action(async (): Promise<void> => {\n await handleList();\n });\n\n program\n .command(\"status\")\n .description(\"Print one session by key as JSON (null if not active)\")\n .requiredOption(\"--region <key>\")\n .requiredOption(\"--org <name>\")\n .requiredOption(\"--space <name>\")\n .requiredOption(\"--app <name>\")\n .action(async (opts: StatusCommandOptions): Promise<void> => {\n await handleStatus(opts);\n });\n\n await program.parseAsync([...argv]);\n}\n\ntry {\n await main(process.argv);\n} catch (err: unknown) {\n if (err instanceof CfDebuggerError) {\n if (err.code === \"ABORTED\") {\n process.stderr.write(`Aborted: ${err.message}\\n`);\n process.exit(130);\n }\n process.stderr.write(`Error [${err.code}]: ${err.message}\\n`);\n } else {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`Error: ${msg}\\n`);\n }\n process.exit(1);\n}\n","import type { ChildProcess } from \"node:child_process\";\nimport { mkdir, rm } from \"node:fs/promises\";\nimport process from \"node:process\";\n\nimport type { CfExecContext } from \"../cf.js\";\nimport {\n cfEnableSsh,\n cfLogin,\n cfRestartApp,\n cfSshEnabled,\n cfSshOneShot,\n cfTarget,\n isSshDisabledError,\n spawnSshTunnel,\n} from \"../cf.js\";\nimport { sessionCfHomeDir } from \"../paths.js\";\nimport { findListeningProcessId, isPortFree, killProcessOnPort, probeTunnelReady } from \"../port.js\";\nimport { resolveApiEndpoint } from \"../regions.js\";\nimport {\n registerNewSession,\n removeSession,\n sessionKeyString,\n updateSessionPid,\n updateSessionStatus,\n} from \"../state.js\";\nimport type { ActiveSession, DebuggerHandle, SessionStatus, StartDebuggerOptions } from \"../types.js\";\nimport { CfDebuggerError } from \"../types.js\";\n\nimport {\n DEFAULT_TUNNEL_READY_TIMEOUT_MS,\n PORT_CLEANUP_DELAY_MS,\n PORT_RECLAIM_DELAY_MS,\n POST_USR1_DELAY_MS,\n} from \"./constants.js\";\nimport { pruneAndCleanupOrphans } from \"./orphans.js\";\nimport { killProcessGroupOrProc } from \"./processes.js\";\n\ntype StatusEmitter = (status: SessionStatus, message?: string) => void;\n\ninterface TunnelResult {\n readonly child: ChildProcess;\n readonly activePid: number;\n}\n\nfunction checkAbort(signal: AbortSignal | undefined): void {\n if (signal?.aborted) {\n throw new CfDebuggerError(\"ABORTED\", \"Operation aborted by caller\");\n }\n}\n\nfunction requireCredentials(options: StartDebuggerOptions): {\n readonly email: string;\n readonly password: string;\n} {\n const email = options.email ?? process.env[\"SAP_EMAIL\"];\n const password = options.password ?? process.env[\"SAP_PASSWORD\"];\n if (email === undefined || email === \"\") {\n throw new CfDebuggerError(\n \"MISSING_CREDENTIALS\",\n \"SAP email is required. Pass `email` or set SAP_EMAIL env var.\",\n );\n }\n if (password === undefined || password === \"\") {\n throw new CfDebuggerError(\n \"MISSING_CREDENTIALS\",\n \"SAP password is required. Pass `password` or set SAP_PASSWORD env var.\",\n );\n }\n return { email, password };\n}\n\nasync function registerSession(\n options: StartDebuggerOptions,\n apiEndpoint: string,\n): Promise<ActiveSession> {\n const registration = await registerNewSession({\n region: options.region,\n org: options.org,\n space: options.space,\n app: options.app,\n apiEndpoint,\n ...(options.preferredPort === undefined ? {} : { preferredPort: options.preferredPort }),\n portProbe: isPortFree,\n cfHomeForSession: sessionCfHomeDir,\n });\n\n if (registration.existing) {\n throw new CfDebuggerError(\n \"SESSION_ALREADY_RUNNING\",\n `A debugger session is already running for ${sessionKeyString(options)} ` +\n `on port ${registration.existing.localPort.toString()} ` +\n `(pid ${registration.existing.pid.toString()}, sessionId ${registration.existing.sessionId}). ` +\n `Stop it first with \\`cf-debugger stop\\`.`,\n );\n }\n return registration.session;\n}\n\nasync function loginAndTarget(\n options: StartDebuggerOptions,\n apiEndpoint: string,\n email: string,\n password: string,\n context: CfExecContext,\n sessionId: string,\n emit: StatusEmitter,\n): Promise<void> {\n emit(\"logging-in\");\n await updateSessionStatus(sessionId, \"logging-in\");\n await cfLogin(apiEndpoint, email, password, context);\n checkAbort(options.signal);\n\n emit(\"targeting\");\n await updateSessionStatus(sessionId, \"targeting\");\n await cfTarget(options.org, options.space, context);\n checkAbort(options.signal);\n}\n\nasync function signalRemoteNode(\n options: StartDebuggerOptions,\n context: CfExecContext,\n sessionId: string,\n emit: StatusEmitter,\n): Promise<void> {\n emit(\"signaling\");\n await updateSessionStatus(sessionId, \"signaling\");\n const signalResult = await cfSshOneShot(options.app, `kill -s USR1 $(pidof node)`, context);\n\n if (!isSshDisabledError(signalResult.stderr)) {\n if (signalResult.exitCode === 0) {\n return;\n }\n const detail = signalResult.stderr.trim().length > 0\n ? signalResult.stderr.trim()\n : `exit code ${String(signalResult.exitCode)}`;\n throw new CfDebuggerError(\n \"USR1_SIGNAL_FAILED\",\n `Failed to send SIGUSR1 to the Node.js process on ${options.app}: ${detail}`,\n signalResult.stderr,\n );\n }\n\n const alreadyEnabled = await cfSshEnabled(options.app, context);\n if (!alreadyEnabled) {\n emit(\"ssh-enabling\", \"Enabling SSH on the app\");\n await updateSessionStatus(sessionId, \"ssh-enabling\");\n await cfEnableSsh(options.app, context);\n }\n emit(\"ssh-restarting\", \"Restarting app so SSH becomes active\");\n await updateSessionStatus(sessionId, \"ssh-restarting\");\n await cfRestartApp(options.app, context);\n checkAbort(options.signal);\n\n await retryRemoteSignal(options, context, sessionId, emit);\n}\n\nasync function retryRemoteSignal(\n options: StartDebuggerOptions,\n context: CfExecContext,\n sessionId: string,\n emit: StatusEmitter,\n): Promise<void> {\n emit(\"signaling\");\n await updateSessionStatus(sessionId, \"signaling\");\n const retrySignalResult = await cfSshOneShot(\n options.app,\n `kill -s USR1 $(pidof node)`,\n context,\n );\n if (retrySignalResult.exitCode === 0) {\n return;\n }\n const detail = retrySignalResult.stderr.trim().length > 0\n ? retrySignalResult.stderr.trim()\n : `exit code ${String(retrySignalResult.exitCode)}`;\n throw new CfDebuggerError(\n \"USR1_SIGNAL_FAILED\",\n `Failed to send SIGUSR1 to the Node.js process on ${options.app} after enabling SSH: ${detail}`,\n retrySignalResult.stderr,\n );\n}\n\nasync function waitAfterSignal(signal: AbortSignal | undefined): Promise<void> {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, POST_USR1_DELAY_MS);\n });\n checkAbort(signal);\n}\n\nasync function ensurePortAvailable(localPort: number): Promise<void> {\n if (await isPortFree(localPort)) {\n return;\n }\n await killProcessOnPort(localPort);\n await new Promise<void>((resolve) => {\n setTimeout(resolve, PORT_RECLAIM_DELAY_MS);\n });\n if (!(await isPortFree(localPort))) {\n throw new CfDebuggerError(\n \"PORT_UNAVAILABLE\",\n `Local port ${localPort.toString()} is in use and could not be reclaimed for the tunnel.`,\n );\n }\n}\n\nasync function openReadyTunnel(\n options: StartDebuggerOptions,\n session: ActiveSession,\n context: CfExecContext,\n tunnelReadyTimeoutMs: number,\n onChild: (child: ChildProcess) => void,\n): Promise<TunnelResult> {\n await ensurePortAvailable(session.localPort);\n const child = spawnSshTunnel(options.app, session.localPort, session.remotePort, context);\n onChild(child);\n if (child.pid !== undefined) {\n await updateSessionPid(session.sessionId, child.pid);\n }\n\n const ready = await probeTunnelReady(session.localPort, tunnelReadyTimeoutMs);\n checkAbort(options.signal);\n if (!ready) {\n throw new CfDebuggerError(\n \"TUNNEL_NOT_READY\",\n `SSH tunnel on port ${session.localPort.toString()} did not become ready within ` +\n `${Math.round(tunnelReadyTimeoutMs / 1000).toString()}s.`,\n );\n }\n\n const listeningPid = await findListeningProcessId(session.localPort);\n const activePid = listeningPid ?? child.pid ?? session.pid;\n if (activePid !== session.pid) {\n await updateSessionPid(session.sessionId, activePid);\n }\n return { child, activePid };\n}\n\nfunction attachTunnelEvents(\n child: ChildProcess,\n markClosed: () => void,\n resolveExit: (code: number | null) => void,\n emit: StatusEmitter,\n): void {\n child.on(\"close\", (code) => {\n markClosed();\n resolveExit(code);\n });\n\n child.on(\"error\", (err: Error) => {\n emit(\"error\", err.message);\n });\n}\n\nfunction createHandle(\n session: ActiveSession,\n emit: StatusEmitter,\n finalize: () => Promise<void>,\n exitPromise: Promise<number | null>,\n): DebuggerHandle {\n let disposePromise: Promise<void> | undefined;\n return {\n session,\n dispose: async (): Promise<void> => {\n disposePromise ??= (async (): Promise<void> => {\n emit(\"stopping\");\n await updateSessionStatus(session.sessionId, \"stopping\");\n await finalize();\n })();\n await disposePromise;\n },\n waitForExit: async (): Promise<number | null> => {\n return await exitPromise;\n },\n };\n}\n\nexport async function startDebugger(options: StartDebuggerOptions): Promise<DebuggerHandle> {\n const { email, password } = requireCredentials(options);\n const apiEndpoint = resolveApiEndpoint(options.region, options.apiEndpoint);\n const tunnelReadyTimeoutMs = options.tunnelReadyTimeoutMs ?? DEFAULT_TUNNEL_READY_TIMEOUT_MS;\n const emit = (status: SessionStatus, message?: string): void => {\n options.onStatus?.(status, message);\n };\n\n checkAbort(options.signal);\n await pruneAndCleanupOrphans();\n\n const session = await registerSession(options, apiEndpoint);\n const context: CfExecContext = { cfHome: session.cfHomeDir };\n let child: ChildProcess | undefined;\n let tunnelClosed = false;\n let exitResolve: (code: number | null) => void = (_code) => {\n throw new Error(\"Exit resolver was used before initialization\");\n };\n const exitPromise = new Promise<number | null>((resolve) => {\n exitResolve = resolve;\n });\n\n const finalize = async (): Promise<void> => {\n if (!tunnelClosed) {\n tunnelClosed = true;\n if (child) {\n await killProcessGroupOrProc(child);\n }\n setTimeout(() => {\n void killProcessOnPort(session.localPort);\n }, PORT_CLEANUP_DELAY_MS);\n }\n await removeSession(session.sessionId);\n await cleanupFilesystem(session.cfHomeDir);\n emit(\"stopped\");\n };\n\n try {\n await mkdir(session.cfHomeDir, { recursive: true });\n await loginAndTarget(options, apiEndpoint, email, password, context, session.sessionId, emit);\n await killProcessOnPort(session.localPort);\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 200);\n });\n await signalRemoteNode(options, context, session.sessionId, emit);\n await waitAfterSignal(options.signal);\n\n emit(\"tunneling\");\n await updateSessionStatus(session.sessionId, \"tunneling\");\n const tunnel = await openReadyTunnel(\n options,\n session,\n context,\n tunnelReadyTimeoutMs,\n (tunnelChild) => {\n child = tunnelChild;\n attachTunnelEvents(tunnelChild, () => {\n tunnelClosed = true;\n }, exitResolve, emit);\n },\n );\n child = tunnel.child;\n\n emit(\"ready\");\n const readySession = await updateSessionStatus(session.sessionId, \"ready\");\n const activeSession = readySession ?? { ...session, pid: tunnel.activePid, status: \"ready\" };\n return createHandle(activeSession, emit, finalize, exitPromise);\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n emit(\"error\", message);\n await finalize();\n throw err;\n }\n}\n\nasync function cleanupFilesystem(cfHomeDir: string): Promise<void> {\n try {\n await rm(cfHomeDir, { recursive: true, force: true });\n } catch {\n // best-effort\n }\n}\n","export interface SessionKey {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n}\n\nexport type SessionStatus =\n | \"starting\"\n | \"logging-in\"\n | \"targeting\"\n | \"ssh-enabling\"\n | \"ssh-restarting\"\n | \"signaling\"\n | \"tunneling\"\n | \"ready\"\n | \"stopping\"\n | \"stopped\"\n | \"error\";\n\nexport interface ActiveSession extends SessionKey {\n readonly sessionId: string;\n readonly pid: number;\n readonly hostname: string;\n readonly localPort: number;\n readonly remotePort: number;\n readonly apiEndpoint: string;\n readonly cfHomeDir: string;\n readonly startedAt: string;\n readonly status: SessionStatus;\n readonly message?: string;\n}\n\nexport interface StartDebuggerOptions extends SessionKey {\n readonly email?: string;\n readonly password?: string;\n readonly apiEndpoint?: string;\n readonly preferredPort?: number;\n readonly tunnelReadyTimeoutMs?: number;\n readonly verbose?: boolean;\n readonly onStatus?: (status: SessionStatus, message?: string) => void;\n readonly signal?: AbortSignal;\n}\n\nexport interface DebuggerHandle {\n readonly session: ActiveSession;\n dispose(): Promise<void>;\n waitForExit(): Promise<number | null>;\n}\n\nexport interface StateFile {\n readonly version: \"1\";\n readonly sessions: readonly ActiveSession[];\n}\n\nexport class CfDebuggerError extends Error {\n public readonly code: string;\n public readonly stderr?: string;\n\n public constructor(code: string, message: string, stderr?: string) {\n super(message);\n this.name = \"CfDebuggerError\";\n this.code = code;\n if (stderr !== undefined) {\n this.stderr = stderr;\n }\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport { CfDebuggerError } from \"../types.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst MAX_BUFFER = 16 * 1024 * 1024;\nconst CF_CLI_TIMEOUT_MS = 30_000;\nconst REDACTED_ARG = \"<redacted>\";\n\nexport interface CfExecContext {\n readonly cfHome: string;\n readonly command?: string;\n}\n\nexport function buildEnv(cfHome: string): NodeJS.ProcessEnv {\n return { ...process.env, CF_HOME: cfHome };\n}\n\nexport function resolveBin(context: CfExecContext): string {\n return context.command ?? process.env[\"CF_DEBUGGER_CF_BIN\"] ?? \"cf\";\n}\n\nfunction sensitiveArgs(args: readonly string[]): readonly string[] {\n if (args[0] !== \"auth\") {\n return [];\n }\n return args.slice(1).filter((arg) => arg.length > 0);\n}\n\nfunction redactText(text: string, values: readonly string[]): string {\n return values.reduce((current, value) => current.split(value).join(REDACTED_ARG), text);\n}\n\nfunction formatArgsForError(args: readonly string[]): string {\n if (args[0] !== \"auth\") {\n return args.join(\" \");\n }\n return args.map((arg, index) => (index === 0 ? arg : REDACTED_ARG)).join(\" \");\n}\n\nexport async function runCf(\n args: readonly string[],\n context: CfExecContext,\n timeoutMs: number = CF_CLI_TIMEOUT_MS,\n): Promise<string> {\n try {\n const { stdout } = await execFileAsync(resolveBin(context), [...args], {\n env: buildEnv(context.cfHome),\n maxBuffer: MAX_BUFFER,\n timeout: timeoutMs,\n });\n return stdout;\n } catch (err: unknown) {\n const e = err as NodeJS.ErrnoException & { stderr?: string; stdout?: string };\n const redactionValues = sensitiveArgs(args);\n const stderr = redactText(e.stderr?.trim() ?? \"\", redactionValues);\n const fallbackMessage = redactText(e.message, redactionValues);\n throw new CfDebuggerError(\n \"CF_CLI_FAILED\",\n `cf ${formatArgsForError(args)} failed: ${stderr.length > 0 ? stderr : fallbackMessage}`,\n stderr,\n );\n }\n}\n","import { CfDebuggerError } from \"../types.js\";\n\nimport { type CfExecContext, runCf } from \"./execute.js\";\nimport { parseAppNames, parseNameTable } from \"./parsers.js\";\n\nconst CF_RESTART_TIMEOUT_MS = 120_000;\nconst CF_AUTH_MAX_ATTEMPTS = 3;\n\nexport async function cfApi(apiEndpoint: string, context: CfExecContext): Promise<void> {\n await runCf([\"api\", apiEndpoint], context);\n}\n\nexport async function cfAuth(\n email: string,\n password: string,\n context: CfExecContext,\n): Promise<void> {\n let lastError: unknown;\n for (let attempt = 0; attempt < CF_AUTH_MAX_ATTEMPTS; attempt++) {\n try {\n await runCf([\"auth\", email, password], context);\n return;\n } catch (err: unknown) {\n lastError = err;\n if (attempt < CF_AUTH_MAX_ATTEMPTS - 1) {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 1000 * (attempt + 1));\n });\n }\n }\n }\n if (lastError instanceof Error) {\n throw lastError;\n }\n throw new CfDebuggerError(\"CF_AUTH_FAILED\", `cf auth failed: ${String(lastError)}`);\n}\n\nexport async function cfLogin(\n apiEndpoint: string,\n email: string,\n password: string,\n context: CfExecContext,\n): Promise<void> {\n try {\n await cfApi(apiEndpoint, context);\n await cfAuth(email, password, context);\n } catch (err: unknown) {\n if (err instanceof CfDebuggerError) {\n throw new CfDebuggerError(\"CF_LOGIN_FAILED\", err.message, err.stderr);\n }\n throw err;\n }\n}\n\nexport async function cfTarget(\n org: string,\n space: string,\n context: CfExecContext,\n): Promise<void> {\n try {\n await runCf([\"target\", \"-o\", org, \"-s\", space], context);\n } catch (err: unknown) {\n if (err instanceof CfDebuggerError) {\n throw new CfDebuggerError(\"CF_TARGET_FAILED\", err.message, err.stderr);\n }\n throw err;\n }\n}\n\nexport async function cfAppExists(appName: string, context: CfExecContext): Promise<boolean> {\n try {\n await runCf([\"app\", appName], context);\n return true;\n } catch (err: unknown) {\n const stderr = (err as CfDebuggerError).stderr ?? \"\";\n if (stderr.toLowerCase().includes(\"not found\")) {\n return false;\n }\n throw err;\n }\n}\n\nexport async function cfSshEnabled(appName: string, context: CfExecContext): Promise<boolean> {\n try {\n const stdout = await runCf([\"ssh-enabled\", appName], context);\n return stdout.toLowerCase().includes(\"ssh support is enabled\");\n } catch {\n return false;\n }\n}\n\nexport async function cfEnableSsh(appName: string, context: CfExecContext): Promise<void> {\n try {\n await runCf([\"enable-ssh\", appName], context);\n } catch (err: unknown) {\n if (err instanceof CfDebuggerError) {\n throw new CfDebuggerError(\"SSH_NOT_ENABLED\", err.message, err.stderr);\n }\n throw err;\n }\n}\n\nexport async function cfRestartApp(appName: string, context: CfExecContext): Promise<void> {\n await runCf([\"restart\", appName], context, CF_RESTART_TIMEOUT_MS);\n}\n\nexport async function cfApps(context: CfExecContext): Promise<readonly string[]> {\n const stdout = await runCf([\"apps\"], context);\n return parseAppNames(stdout);\n}\n\nexport async function cfOrgs(context: CfExecContext): Promise<readonly string[]> {\n const stdout = await runCf([\"orgs\"], context);\n return parseNameTable(stdout);\n}\n\nexport async function cfSpaces(context: CfExecContext): Promise<readonly string[]> {\n const stdout = await runCf([\"spaces\"], context);\n return parseNameTable(stdout);\n}\n","import { spawn } from \"node:child_process\";\n\nimport { buildEnv, resolveBin, type CfExecContext } from \"./execute.js\";\n\nconst CF_SSH_SIGNAL_TIMEOUT_MS = 15_000;\n\nexport interface CfSshSignalResult {\n readonly exitCode: number | null;\n readonly stderr: string;\n}\n\nexport async function cfSshOneShot(\n appName: string,\n command: string,\n context: CfExecContext,\n): Promise<CfSshSignalResult> {\n return await new Promise<CfSshSignalResult>((resolve) => {\n const child = spawn(resolveBin(context), [\"ssh\", appName, \"-c\", command], {\n env: buildEnv(context.cfHome),\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n let stderrBuf = \"\";\n let settled = false;\n\n const timeout = setTimeout(() => {\n if (settled) {\n return;\n }\n settled = true;\n try {\n child.kill();\n } catch {\n // already gone\n }\n resolve({ exitCode: null, stderr: stderrBuf });\n }, CF_SSH_SIGNAL_TIMEOUT_MS);\n\n child.stderr.on(\"data\", (data: Buffer | string) => {\n stderrBuf += data.toString();\n });\n\n child.on(\"close\", (code) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timeout);\n resolve({ exitCode: code, stderr: stderrBuf });\n });\n\n child.on(\"error\", (err: Error) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timeout);\n resolve({ exitCode: null, stderr: err.message });\n });\n });\n}\n\nexport function isSshDisabledError(stderr: string): boolean {\n const lower = stderr.toLowerCase();\n return lower.includes(\"not authorized\") || lower.includes(\"ssh support is disabled\");\n}\n\nexport function spawnSshTunnel(\n appName: string,\n localPort: number,\n remotePort: number,\n context: CfExecContext,\n): ReturnType<typeof spawn> {\n const tunnelArg = `${localPort.toString()}:localhost:${remotePort.toString()}`;\n const isWindows = process.platform === \"win32\";\n return spawn(resolveBin(context), [\"ssh\", appName, \"-N\", \"-L\", tunnelArg], {\n env: buildEnv(context.cfHome),\n shell: isWindows,\n detached: !isWindows,\n });\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const SAPTOOLS_DIR_NAME = \".saptools\";\nexport const CF_DEBUGGER_STATE_FILENAME = \"cf-debugger-state.json\";\nexport const CF_DEBUGGER_LOCK_FILENAME = \"cf-debugger-state.lock\";\nexport const CF_DEBUGGER_HOMES_DIRNAME = \"cf-debugger-homes\";\n\nexport function saptoolsDir(): string {\n return join(homedir(), SAPTOOLS_DIR_NAME);\n}\n\nexport function stateFilePath(): string {\n return join(saptoolsDir(), CF_DEBUGGER_STATE_FILENAME);\n}\n\nexport function stateLockPath(): string {\n return join(saptoolsDir(), CF_DEBUGGER_LOCK_FILENAME);\n}\n\nexport function sessionCfHomeDir(sessionId: string): string {\n return join(saptoolsDir(), CF_DEBUGGER_HOMES_DIRNAME, sessionId);\n}\n","import { execFile } from \"node:child_process\";\nimport { createConnection, createServer } from \"node:net\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nasync function findListeningPidsWithNetstat(port: number): Promise<readonly number[]> {\n try {\n const { stdout } = await execFileAsync(\"netstat\", [\"-ano\"]);\n const pids = new Set<number>();\n for (const line of stdout.split(\"\\n\")) {\n if (!line.includes(`:${port.toString()}`) || !line.includes(\"LISTENING\")) {\n continue;\n }\n const parts = line.trim().split(/\\s+/);\n const last = parts[parts.length - 1];\n if (last === undefined) {\n continue;\n }\n const pid = Number.parseInt(last, 10);\n if (!Number.isNaN(pid)) {\n pids.add(pid);\n }\n }\n return [...pids];\n } catch {\n return [];\n }\n}\n\nasync function findListeningPidsWithLsof(port: number): Promise<readonly number[]> {\n try {\n const { stdout } = await execFileAsync(\"lsof\", [\"-nP\", \"-t\", \"-i\", `tcp:${port.toString()}`, \"-sTCP:LISTEN\"]);\n return stdout\n .trim()\n .split(\"\\n\")\n .filter((line) => line.length > 0)\n .map((line) => Number.parseInt(line, 10))\n .filter((pid) => !Number.isNaN(pid));\n } catch {\n return [];\n }\n}\n\nasync function findListeningPids(port: number): Promise<readonly number[]> {\n if (process.platform === \"win32\") {\n return await findListeningPidsWithNetstat(port);\n }\n return await findListeningPidsWithLsof(port);\n}\n\nexport async function isPortFree(port: number): Promise<boolean> {\n return await new Promise<boolean>((resolve) => {\n const server = createServer();\n server.once(\"error\", () => {\n resolve(false);\n });\n server.once(\"listening\", () => {\n server.close(() => {\n resolve(true);\n });\n });\n server.listen(port, \"127.0.0.1\");\n });\n}\n\nexport async function probeTunnelReady(port: number, timeoutMs: number): Promise<boolean> {\n const pollIntervalMs = 250;\n const started = Date.now();\n\n while (Date.now() - started < timeoutMs) {\n const connected = await new Promise<boolean>((resolve) => {\n const socket = createConnection({ port, host: \"127.0.0.1\" });\n socket.setTimeout(200);\n socket.once(\"connect\", () => {\n socket.destroy();\n resolve(true);\n });\n socket.once(\"error\", () => {\n socket.destroy();\n resolve(false);\n });\n socket.once(\"timeout\", () => {\n socket.destroy();\n resolve(false);\n });\n });\n\n if (connected) {\n return true;\n }\n\n await new Promise<void>((resolve) => {\n setTimeout(resolve, pollIntervalMs);\n });\n }\n\n return false;\n}\n\nexport async function findListeningProcessId(port: number): Promise<number | undefined> {\n const pids = await findListeningPids(port);\n return pids[0];\n}\n\nexport async function killProcessOnPort(port: number): Promise<void> {\n const portStr = port.toString();\n if (process.platform === \"win32\") {\n try {\n const pids = await findListeningPidsWithNetstat(port);\n for (const pid of pids) {\n try {\n // cspell:ignore taskkill\n await execFileAsync(\"taskkill\", [\"/F\", \"/PID\", pid.toString()]);\n } catch {\n // ignore\n }\n }\n } catch {\n // ignore\n }\n return;\n }\n\n try {\n const { stdout } = await execFileAsync(\"lsof\", [\"-t\", \"-i\", `tcp:${portStr}`]);\n const lines = stdout.trim().split(\"\\n\").filter((line) => line.length > 0);\n for (const line of lines) {\n const pid = Number.parseInt(line, 10);\n if (Number.isNaN(pid)) {\n continue;\n }\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {\n // already dead\n }\n }\n } catch {\n // lsof missing or no match — ignore\n }\n}\n","export interface RegionInfo {\n readonly key: string;\n readonly apiEndpoint: string;\n}\n\nconst REGION_API_ENDPOINTS: Readonly<Record<string, string>> = {\n ae01: \"https://api.cf.ae01.hana.ondemand.com\",\n ap01: \"https://api.cf.ap01.hana.ondemand.com\",\n ap10: \"https://api.cf.ap10.hana.ondemand.com\",\n ap11: \"https://api.cf.ap11.hana.ondemand.com\",\n ap12: \"https://api.cf.ap12.hana.ondemand.com\",\n ap20: \"https://api.cf.ap20.hana.ondemand.com\",\n ap21: \"https://api.cf.ap21.hana.ondemand.com\",\n ap30: \"https://api.cf.ap30.hana.ondemand.com\",\n br10: \"https://api.cf.br10.hana.ondemand.com\",\n br20: \"https://api.cf.br20.hana.ondemand.com\",\n br30: \"https://api.cf.br30.hana.ondemand.com\",\n ca10: \"https://api.cf.ca10.hana.ondemand.com\",\n ca20: \"https://api.cf.ca20.hana.ondemand.com\",\n ch20: \"https://api.cf.ch20.hana.ondemand.com\",\n eu10: \"https://api.cf.eu10.hana.ondemand.com\",\n eu11: \"https://api.cf.eu11.hana.ondemand.com\",\n eu12: \"https://api.cf.eu12.hana.ondemand.com\",\n eu20: \"https://api.cf.eu20.hana.ondemand.com\",\n eu21: \"https://api.cf.eu21.hana.ondemand.com\",\n eu30: \"https://api.cf.eu30.hana.ondemand.com\",\n eu31: \"https://api.cf.eu31.hana.ondemand.com\",\n in30: \"https://api.cf.in30.hana.ondemand.com\",\n jp10: \"https://api.cf.jp10.hana.ondemand.com\",\n jp20: \"https://api.cf.jp20.hana.ondemand.com\",\n jp30: \"https://api.cf.jp30.hana.ondemand.com\",\n kr30: \"https://api.cf.kr30.hana.ondemand.com\",\n us10: \"https://api.cf.us10.hana.ondemand.com\",\n us11: \"https://api.cf.us11.hana.ondemand.com\",\n us20: \"https://api.cf.us20.hana.ondemand.com\",\n us21: \"https://api.cf.us21.hana.ondemand.com\",\n us30: \"https://api.cf.us30.hana.ondemand.com\",\n us31: \"https://api.cf.us31.hana.ondemand.com\",\n};\n\nexport function resolveApiEndpoint(regionKey: string, override?: string): string {\n if (override !== undefined && override !== \"\") {\n return override;\n }\n const endpoint = REGION_API_ENDPOINTS[regionKey];\n if (endpoint === undefined) {\n throw new Error(\n `Unknown region key: ${regionKey}. Pass \\`apiEndpoint\\` explicitly to override.`,\n );\n }\n return endpoint;\n}\n\nexport function listKnownRegionKeys(): readonly string[] {\n return Object.keys(REGION_API_ENDPOINTS);\n}\n","import { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { hostname as getHostname } from \"node:os\";\nimport { dirname } from \"node:path\";\nimport process from \"node:process\";\n\nimport { withFileLock } from \"../lock.js\";\nimport { stateFilePath, stateLockPath } from \"../paths.js\";\nimport { CfDebuggerError } from \"../types.js\";\nimport type { ActiveSession, SessionKey, StateFile } from \"../types.js\";\n\nasync function readJsonFile<T>(path: string): Promise<T | undefined> {\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") {\n return undefined;\n }\n throw err;\n }\n try {\n return JSON.parse(raw) as T;\n } catch {\n process.stderr.write(\n `[cf-debugger] warning: state file at ${path} is not valid JSON; resetting to empty.\\n`,\n );\n return undefined;\n }\n}\n\nasync function writeJsonFileAtomic(path: string, value: unknown): Promise<void> {\n const tempPath = `${path}.${randomUUID()}.tmp`;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(tempPath, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n await rename(tempPath, path);\n}\n\nfunction emptyState(): StateFile {\n return { version: \"1\", sessions: [] };\n}\n\nfunction isValidState(value: unknown): value is StateFile {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const candidate = value as Partial<StateFile>;\n return candidate.version === \"1\" && Array.isArray(candidate.sessions);\n}\n\nexport function isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ESRCH\") {\n return false;\n }\n return true;\n }\n}\n\nfunction filterStaleSessions(sessions: readonly ActiveSession[]): readonly ActiveSession[] {\n const host = getHostname();\n return sessions.filter((session) => {\n if (session.hostname !== host) {\n return true;\n }\n return isPidAlive(session.pid);\n });\n}\n\nasync function readStateRaw(): Promise<StateFile> {\n const parsed = await readJsonFile<unknown>(stateFilePath());\n if (!isValidState(parsed)) {\n return emptyState();\n }\n return parsed;\n}\n\nasync function writeState(state: StateFile): Promise<void> {\n await writeJsonFileAtomic(stateFilePath(), state);\n}\n\nexport interface StateReaderResult {\n readonly sessions: readonly ActiveSession[];\n readonly removed: readonly ActiveSession[];\n}\n\nasync function readAndPruneLocked(): Promise<StateReaderResult> {\n const raw = await readStateRaw();\n const pruned = filterStaleSessions(raw.sessions);\n const removed = raw.sessions.filter(\n (session) => !pruned.some((active) => active.sessionId === session.sessionId),\n );\n\n if (removed.length > 0) {\n await writeState({ version: \"1\", sessions: pruned });\n }\n\n return { sessions: pruned, removed };\n}\n\nexport async function readActiveSessions(): Promise<readonly ActiveSession[]> {\n const result = await withFileLock(stateLockPath(), readAndPruneLocked);\n return result.sessions;\n}\n\nexport async function readAndPruneActiveSessions(): Promise<StateReaderResult> {\n return await withFileLock(stateLockPath(), readAndPruneLocked);\n}\n\nexport function sessionKeyString(key: SessionKey): string {\n return `${key.region}:${key.org}:${key.space}:${key.app}`;\n}\n\nexport function matchesKey(session: SessionKey, key: SessionKey): boolean {\n return (\n session.region === key.region &&\n session.org === key.org &&\n session.space === key.space &&\n session.app === key.app\n );\n}\n\nexport interface RegisterSessionResult {\n readonly session: ActiveSession;\n readonly existing?: ActiveSession;\n}\n\nexport interface RegisterSessionInput extends SessionKey {\n readonly apiEndpoint: string;\n readonly preferredPort?: number;\n readonly portProbe: (port: number) => Promise<boolean>;\n readonly sessionIdFactory?: () => string;\n readonly cfHomeForSession: (sessionId: string) => string;\n readonly basePort?: number;\n readonly maxPort?: number;\n}\n\nconst DEFAULT_BASE_PORT = 20_000;\nconst DEFAULT_MAX_PORT = 20_999;\n\nasync function pickPort(\n preferred: number | undefined,\n reserved: ReadonlySet<number>,\n probe: (port: number) => Promise<boolean>,\n basePort: number,\n maxPort: number,\n): Promise<number> {\n const tryOrder: number[] = [];\n if (preferred !== undefined) {\n tryOrder.push(preferred);\n }\n for (let port = basePort; port <= maxPort; port++) {\n if (port !== preferred) {\n tryOrder.push(port);\n }\n }\n\n for (const port of tryOrder) {\n if (reserved.has(port)) {\n continue;\n }\n const free = await probe(port);\n if (free) {\n return port;\n }\n }\n throw new CfDebuggerError(\n \"PORT_UNAVAILABLE\",\n `No free local port available in range ${basePort.toString()}–${maxPort.toString()}`,\n );\n}\n\nexport async function registerNewSession(\n input: RegisterSessionInput,\n): Promise<RegisterSessionResult> {\n return await withFileLock(stateLockPath(), async (): Promise<RegisterSessionResult> => {\n const pruneResult = await readAndPruneLocked();\n const existing = pruneResult.sessions.find((session) => matchesKey(session, input));\n if (existing) {\n return { session: existing, existing };\n }\n\n const reservedPorts = new Set(pruneResult.sessions.map((session) => session.localPort));\n const localPort = await pickPort(\n input.preferredPort,\n reservedPorts,\n input.portProbe,\n input.basePort ?? DEFAULT_BASE_PORT,\n input.maxPort ?? DEFAULT_MAX_PORT,\n );\n\n const sessionId = (input.sessionIdFactory ?? randomUUID)();\n const cfHomeDir = input.cfHomeForSession(sessionId);\n\n const session: ActiveSession = {\n sessionId,\n pid: process.pid,\n hostname: getHostname(),\n region: input.region,\n org: input.org,\n space: input.space,\n app: input.app,\n apiEndpoint: input.apiEndpoint,\n localPort,\n remotePort: 9229,\n cfHomeDir,\n startedAt: new Date().toISOString(),\n status: \"starting\",\n };\n\n const nextSessions: readonly ActiveSession[] = [...pruneResult.sessions, session];\n await writeState({ version: \"1\", sessions: nextSessions });\n\n return { session };\n });\n}\n\nexport async function updateSessionStatus(\n sessionId: string,\n status: ActiveSession[\"status\"],\n message?: string,\n): Promise<ActiveSession | undefined> {\n return await withFileLock(stateLockPath(), async (): Promise<ActiveSession | undefined> => {\n const raw = await readStateRaw();\n let updated: ActiveSession | undefined;\n const nextSessions = raw.sessions.map((session): ActiveSession => {\n if (session.sessionId !== sessionId) {\n return session;\n }\n const base: ActiveSession = {\n sessionId: session.sessionId,\n pid: session.pid,\n hostname: session.hostname,\n region: session.region,\n org: session.org,\n space: session.space,\n app: session.app,\n apiEndpoint: session.apiEndpoint,\n localPort: session.localPort,\n remotePort: session.remotePort,\n cfHomeDir: session.cfHomeDir,\n startedAt: session.startedAt,\n status,\n };\n const next: ActiveSession = message === undefined ? base : { ...base, message };\n updated = next;\n return next;\n });\n\n if (updated) {\n await writeState({ version: \"1\", sessions: nextSessions });\n }\n return updated;\n });\n}\n\nexport async function updateSessionPid(\n sessionId: string,\n pid: number,\n): Promise<ActiveSession | undefined> {\n return await withFileLock(stateLockPath(), async (): Promise<ActiveSession | undefined> => {\n const raw = await readStateRaw();\n let updated: ActiveSession | undefined;\n const nextSessions = raw.sessions.map((session): ActiveSession => {\n if (session.sessionId !== sessionId) {\n return session;\n }\n const next: ActiveSession = {\n sessionId: session.sessionId,\n pid,\n hostname: session.hostname,\n region: session.region,\n org: session.org,\n space: session.space,\n app: session.app,\n apiEndpoint: session.apiEndpoint,\n localPort: session.localPort,\n remotePort: session.remotePort,\n cfHomeDir: session.cfHomeDir,\n startedAt: session.startedAt,\n status: session.status,\n ...(session.message === undefined ? {} : { message: session.message }),\n };\n updated = next;\n return next;\n });\n\n if (updated !== undefined) {\n await writeState({ version: \"1\", sessions: nextSessions });\n }\n return updated;\n });\n}\n\nexport async function removeSession(sessionId: string): Promise<ActiveSession | undefined> {\n return await withFileLock(stateLockPath(), async (): Promise<ActiveSession | undefined> => {\n const raw = await readStateRaw();\n const target = raw.sessions.find((session) => session.sessionId === sessionId);\n if (!target) {\n return undefined;\n }\n const remaining = raw.sessions.filter((session) => session.sessionId !== sessionId);\n await writeState({ version: \"1\", sessions: remaining });\n return target;\n });\n}\n","import { mkdir, open, unlink } from \"node:fs/promises\";\nimport type { FileHandle } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nconst DEFAULT_POLL_MS = 50;\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nasync function acquireFileLock(\n lockPath: string,\n timeoutMs: number,\n pollMs: number,\n): Promise<FileHandle> {\n const deadline = Date.now() + timeoutMs;\n await mkdir(dirname(lockPath), { recursive: true });\n\n for (;;) {\n try {\n return await open(lockPath, \"wx\");\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"EEXIST\") {\n throw err;\n }\n }\n\n if (Date.now() > deadline) {\n throw new Error(`Timed out acquiring file lock at ${lockPath}`);\n }\n\n await sleep(pollMs);\n }\n}\n\nasync function releaseFileLock(lockPath: string, handle: FileHandle): Promise<void> {\n await handle.close();\n await unlink(lockPath).catch((err: unknown) => {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"ENOENT\") {\n throw err;\n }\n });\n}\n\nexport interface WithLockOptions {\n readonly timeoutMs?: number;\n readonly pollMs?: number;\n}\n\nexport async function withFileLock<T>(\n lockPath: string,\n work: () => Promise<T>,\n options?: WithLockOptions,\n): Promise<T> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const pollMs = options?.pollMs ?? DEFAULT_POLL_MS;\n const handle = await acquireFileLock(lockPath, timeoutMs, pollMs);\n try {\n return await work();\n } finally {\n await releaseFileLock(lockPath, handle);\n }\n}\n","export const DEFAULT_TUNNEL_READY_TIMEOUT_MS = 30_000;\nexport const POST_USR1_DELAY_MS = 300;\nexport const PORT_CLEANUP_DELAY_MS = 600;\nexport const CHILD_SIGTERM_GRACE_MS = 2_000;\nexport const PORT_RECLAIM_DELAY_MS = 250;\nexport const PID_TERMINATION_POLL_MS = 100;\n","import { hostname as getHostname } from \"node:os\";\n\nimport { killProcessOnPort } from \"../port.js\";\nimport { readAndPruneActiveSessions } from \"../state.js\";\nimport type { ActiveSession } from \"../types.js\";\n\nexport async function pruneAndCleanupOrphans(): Promise<readonly ActiveSession[]> {\n const result = await readAndPruneActiveSessions();\n const host = getHostname();\n for (const removed of result.removed) {\n if (removed.hostname === host) {\n void killProcessOnPort(removed.localPort);\n }\n }\n return result.sessions;\n}\n","import type { ChildProcess } from \"node:child_process\";\nimport process from \"node:process\";\n\nimport { isPidAlive } from \"../state.js\";\n\nimport { CHILD_SIGTERM_GRACE_MS, PID_TERMINATION_POLL_MS } from \"./constants.js\";\n\nfunction signalPidOrGroup(pid: number, signal: NodeJS.Signals): void {\n const isWindows = process.platform === \"win32\";\n if (!isWindows) {\n try {\n process.kill(-pid, signal);\n return;\n } catch {\n // fall through to direct pid signal\n }\n }\n try {\n process.kill(pid, signal);\n } catch {\n // already gone\n }\n}\n\nexport async function terminatePidOrGroup(\n pid: number,\n timeoutMs: number = CHILD_SIGTERM_GRACE_MS,\n): Promise<void> {\n if (!isPidAlive(pid)) {\n return;\n }\n\n signalPidOrGroup(pid, \"SIGTERM\");\n const startedAt = Date.now();\n while (Date.now() - startedAt < timeoutMs) {\n if (!isPidAlive(pid)) {\n return;\n }\n await new Promise<void>((resolve) => {\n setTimeout(resolve, PID_TERMINATION_POLL_MS);\n });\n }\n\n signalPidOrGroup(pid, \"SIGKILL\");\n}\n\nexport async function killProcessGroupOrProc(\n child: ChildProcess,\n timeoutMs: number = CHILD_SIGTERM_GRACE_MS,\n): Promise<void> {\n if (child.pid === undefined) {\n return;\n }\n if (child.exitCode !== null || child.signalCode !== null) {\n return;\n }\n await terminatePidOrGroup(child.pid, timeoutMs);\n}\n","import { rm } from \"node:fs/promises\";\nimport process from \"node:process\";\n\nimport { killProcessOnPort } from \"../port.js\";\nimport { matchesKey, removeSession } from \"../state.js\";\nimport type { ActiveSession, SessionKey } from \"../types.js\";\n\nimport { PORT_CLEANUP_DELAY_MS } from \"./constants.js\";\nimport { pruneAndCleanupOrphans } from \"./orphans.js\";\nimport { terminatePidOrGroup } from \"./processes.js\";\n\nexport interface StopOptions {\n readonly sessionId?: string;\n readonly key?: SessionKey;\n}\n\nexport async function stopDebugger(options: StopOptions): Promise<ActiveSession | undefined> {\n const sessions = await pruneAndCleanupOrphans();\n let target: ActiveSession | undefined;\n if (options.sessionId !== undefined) {\n target = sessions.find((s) => s.sessionId === options.sessionId);\n } else if (options.key !== undefined) {\n const key = options.key;\n target = sessions.find((s) => matchesKey(s, key));\n }\n if (target === undefined) {\n return undefined;\n }\n if (target.pid !== process.pid) {\n try {\n await terminatePidOrGroup(target.pid);\n } catch {\n // process already gone — cleanup below\n }\n }\n setTimeout(() => {\n void killProcessOnPort(target.localPort);\n }, PORT_CLEANUP_DELAY_MS);\n const removed = await removeSession(target.sessionId);\n try {\n await rm(target.cfHomeDir, { recursive: true, force: true });\n } catch {\n // best-effort\n }\n return removed ?? target;\n}\n\nexport async function stopAllDebuggers(): Promise<number> {\n const sessions = await pruneAndCleanupOrphans();\n let stopped = 0;\n for (const session of sessions) {\n const result = await stopDebugger({ sessionId: session.sessionId });\n if (result) {\n stopped += 1;\n }\n }\n return stopped;\n}\n\nexport async function listSessions(): Promise<readonly ActiveSession[]> {\n return await pruneAndCleanupOrphans();\n}\n\nexport async function getSession(key: SessionKey): Promise<ActiveSession | undefined> {\n const sessions = await pruneAndCleanupOrphans();\n return sessions.find((s) => matchesKey(s, key));\n}\n"],"mappings":"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","names":["process","mkdir","process","execFile","promisify","execFileAsync","mkdir","dirname","process","process","mkdir","dirname","session","process","getHostname","getHostname","process","process","process","mkdir","rm","process","process","rm","process"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/debug-session/start.ts","../src/types.ts","../src/cloud-foundry/execute.ts","../src/cloud-foundry/commands.ts","../src/cloud-foundry/ssh.ts","../src/paths.ts","../src/network/ports.ts","../src/regions.ts","../src/session-state/store.ts","../src/lock.ts","../src/debug-session/constants.ts","../src/debug-session/orphans.ts","../src/debug-session/processes.ts","../src/debug-session/sessions.ts"],"sourcesContent":["import process from \"node:process\";\n\nimport { Command } from \"commander\";\n\nimport {\n getSession,\n listSessions,\n startDebugger,\n stopAllDebuggers,\n stopDebugger,\n} from \"./debugger.js\";\nimport type { SessionKey, SessionStatus } from \"./types.js\";\nimport { CfDebuggerError } from \"./types.js\";\n\nfunction readRequiredOption(value: string | undefined, flag: string): string {\n if (value === undefined || value === \"\") {\n process.stderr.write(`Missing required option ${flag}\\n`);\n process.exit(1);\n }\n return value;\n}\n\nfunction parseOptionalPort(raw: string | undefined): number | undefined {\n if (raw === undefined) {\n return undefined;\n }\n const port = Number.parseInt(raw, 10);\n if (Number.isNaN(port) || port <= 0 || port > 65_535) {\n process.stderr.write(`Invalid port: ${raw}\\n`);\n process.exit(1);\n }\n return port;\n}\n\nfunction parseOptionalTimeout(raw: string | undefined): number | undefined {\n if (raw === undefined) {\n return undefined;\n }\n const seconds = Number.parseInt(raw, 10);\n if (Number.isNaN(seconds) || seconds <= 0) {\n process.stderr.write(`Invalid timeout: ${raw}\\n`);\n process.exit(1);\n }\n return seconds * 1000;\n}\n\ninterface StartCommandOptions {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n readonly port?: string;\n readonly timeout?: string;\n readonly verbose?: boolean;\n}\n\ninterface StopCommandOptions {\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}\n\ninterface StatusCommandOptions {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n}\n\nfunction logStatus(verbose: boolean, status: SessionStatus, message?: string): void {\n if (verbose) {\n const suffix = message === undefined ? \"\" : `: ${message}`;\n process.stdout.write(`[cf-debugger] ${status}${suffix}\\n`);\n }\n}\n\nasync function handleStart(opts: StartCommandOptions): Promise<void> {\n const region = readRequiredOption(opts.region, \"--region\");\n const org = readRequiredOption(opts.org, \"--org\");\n const space = readRequiredOption(opts.space, \"--space\");\n const app = readRequiredOption(opts.app, \"--app\");\n const verbose = opts.verbose ?? false;\n\n const preferredPort = parseOptionalPort(opts.port);\n const tunnelReadyTimeoutMs = parseOptionalTimeout(opts.timeout);\n\n const abortController = new AbortController();\n const onStartupSignal = (exitCode: number) => (): void => {\n abortController.abort();\n process.stderr.write(`\\nAborting startup for ${app}...\\n`);\n setTimeout(() => {\n process.exit(exitCode);\n }, 5_000).unref();\n };\n const startupSigint = onStartupSignal(130);\n const startupSigterm = onStartupSignal(143);\n process.on(\"SIGINT\", startupSigint);\n process.on(\"SIGTERM\", startupSigterm);\n\n let handle;\n try {\n handle = await startDebugger({\n region,\n org,\n space,\n app,\n verbose,\n signal: abortController.signal,\n ...(preferredPort === undefined ? {} : { preferredPort }),\n ...(tunnelReadyTimeoutMs === undefined ? {} : { tunnelReadyTimeoutMs }),\n onStatus: (status, message) => {\n logStatus(verbose, status, message);\n },\n });\n } finally {\n process.off(\"SIGINT\", startupSigint);\n process.off(\"SIGTERM\", startupSigterm);\n }\n\n process.stdout.write(\n `Debugger ready for ${app} (${region}/${org}/${space}).\\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 ` PID: ${handle.session.pid.toString()}\\n` +\n `Press Ctrl+C to stop.\\n`,\n );\n\n let disposePromise: Promise<void> | undefined;\n const dispose = async (): Promise<void> => {\n disposePromise ??= (async (): Promise<void> => {\n process.stdout.write(`\\nStopping debugger for ${app}...\\n`);\n try {\n await handle.dispose();\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`Error during stop: ${msg}\\n`);\n }\n })();\n await disposePromise;\n };\n\n process.on(\"SIGINT\", () => {\n void dispose().then(() => {\n process.exit(130);\n });\n });\n process.on(\"SIGTERM\", () => {\n void dispose().then(() => {\n process.exit(143);\n });\n });\n\n const code = await handle.waitForExit();\n await dispose();\n process.exit(code ?? 0);\n}\n\nfunction resolveKeyFromOpts(opts: StopCommandOptions): SessionKey | undefined {\n if (\n opts.region !== undefined &&\n opts.org !== undefined &&\n opts.space !== undefined &&\n opts.app !== undefined\n ) {\n return {\n region: opts.region,\n org: opts.org,\n space: opts.space,\n app: opts.app,\n };\n }\n return undefined;\n}\n\nasync function handleStop(opts: StopCommandOptions): Promise<void> {\n if (opts.all === true) {\n const count = await stopAllDebuggers();\n process.stdout.write(`Stopped ${count.toString()} session(s).\\n`);\n return;\n }\n const key = resolveKeyFromOpts(opts);\n const result = await stopDebugger({\n ...(opts.sessionId === undefined ? {} : { sessionId: opts.sessionId }),\n ...(key === undefined ? {} : { key }),\n });\n if (result === undefined) {\n process.stderr.write(\"No matching session found.\\n\");\n process.exit(1);\n }\n process.stdout.write(\n `Stopped session ${result.sessionId} (${result.app}, port ${result.localPort.toString()}).\\n`,\n );\n}\n\nasync function handleList(): Promise<void> {\n const sessions = await listSessions();\n process.stdout.write(`${JSON.stringify(sessions, null, 2)}\\n`);\n}\n\nasync function handleStatus(opts: StatusCommandOptions): Promise<void> {\n const session = await getSession({\n region: opts.region,\n org: opts.org,\n space: opts.space,\n app: opts.app,\n });\n process.stdout.write(`${JSON.stringify(session ?? null, null, 2)}\\n`);\n}\n\nexport async function main(argv: readonly string[]): Promise<void> {\n const program = new Command();\n\n program\n .name(\"cf-debugger\")\n .description(\"Open an SSH debug tunnel to a SAP BTP Cloud Foundry app's Node.js inspector\");\n\n program\n .command(\"start\")\n .description(\"Open a debug tunnel for one app\")\n .requiredOption(\"--region <key>\", \"CF region key (e.g. eu10)\")\n .requiredOption(\"--org <name>\", \"CF org name\")\n .requiredOption(\"--space <name>\", \"CF space name\")\n .requiredOption(\"--app <name>\", \"CF app name\")\n .option(\"--port <number>\", \"Preferred local port (auto-assigned if omitted)\")\n .option(\"--timeout <seconds>\", \"Tunnel-ready timeout in seconds (default: 180)\")\n .option(\"--verbose\", \"Print status transitions\", false)\n .action(async (opts: StartCommandOptions): Promise<void> => {\n await handleStart(opts);\n });\n\n program\n .command(\"stop\")\n .description(\"Stop one session (by key or id) or all sessions with --all\")\n .option(\"--region <key>\")\n .option(\"--org <name>\")\n .option(\"--space <name>\")\n .option(\"--app <name>\")\n .option(\"--session-id <id>\")\n .option(\"--all\", \"Stop every active session\", false)\n .action(async (opts: StopCommandOptions): Promise<void> => {\n await handleStop(opts);\n });\n\n program\n .command(\"list\")\n .description(\"Print every active debugger session as JSON\")\n .action(async (): Promise<void> => {\n await handleList();\n });\n\n program\n .command(\"status\")\n .description(\"Print one session by key as JSON (null if not active)\")\n .requiredOption(\"--region <key>\")\n .requiredOption(\"--org <name>\")\n .requiredOption(\"--space <name>\")\n .requiredOption(\"--app <name>\")\n .action(async (opts: StatusCommandOptions): Promise<void> => {\n await handleStatus(opts);\n });\n\n await program.parseAsync([...argv]);\n}\n\ntry {\n await main(process.argv);\n} catch (err: unknown) {\n if (err instanceof CfDebuggerError) {\n if (err.code === \"ABORTED\") {\n process.stderr.write(`Aborted: ${err.message}\\n`);\n process.exit(130);\n }\n process.stderr.write(`Error [${err.code}]: ${err.message}\\n`);\n } else {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`Error: ${msg}\\n`);\n }\n process.exit(1);\n}\n","import type { ChildProcess } from \"node:child_process\";\nimport { mkdir, rm } from \"node:fs/promises\";\nimport process from \"node:process\";\n\nimport type { CfExecContext } from \"../cf.js\";\nimport {\n cfEnableSsh,\n cfLogin,\n cfRestartApp,\n cfSshEnabled,\n cfSshOneShot,\n cfTarget,\n isSshDisabledError,\n spawnSshTunnel,\n} from \"../cf.js\";\nimport { sessionCfHomeDir } from \"../paths.js\";\nimport { findListeningProcessId, isPortFree, killProcessOnPort, probeTunnelReady } from \"../port.js\";\nimport { resolveApiEndpoint } from \"../regions.js\";\nimport {\n registerNewSession,\n removeSession,\n sessionKeyString,\n updateSessionPid,\n updateSessionStatus,\n} from \"../state.js\";\nimport type { ActiveSession, DebuggerHandle, SessionStatus, StartDebuggerOptions } from \"../types.js\";\nimport { CfDebuggerError } from \"../types.js\";\n\nimport {\n DEFAULT_TUNNEL_READY_TIMEOUT_MS,\n PORT_CLEANUP_DELAY_MS,\n PORT_RECLAIM_DELAY_MS,\n POST_USR1_DELAY_MS,\n} from \"./constants.js\";\nimport { pruneAndCleanupOrphans } from \"./orphans.js\";\nimport { killProcessGroupOrProc } from \"./processes.js\";\n\ntype StatusEmitter = (status: SessionStatus, message?: string) => void;\n\ninterface TunnelResult {\n readonly child: ChildProcess;\n readonly activePid: number;\n}\n\ntype SignalResult = Awaited<ReturnType<typeof cfSshOneShot>>;\n\nfunction signalFailureDetail(result: SignalResult): string {\n if (result.timedOutAfterMs !== undefined) {\n return `timed out after ${(result.timedOutAfterMs / 1000).toString()}s`;\n }\n const stderr = result.stderr.trim();\n if (stderr.length > 0) {\n return stderr;\n }\n if (result.signal !== undefined) {\n return `terminated by signal ${result.signal}`;\n }\n return `exit code ${String(result.exitCode)}`;\n}\n\nfunction checkAbort(signal: AbortSignal | undefined): void {\n if (signal?.aborted) {\n throw new CfDebuggerError(\"ABORTED\", \"Operation aborted by caller\");\n }\n}\n\nfunction requireCredentials(options: StartDebuggerOptions): {\n readonly email: string;\n readonly password: string;\n} {\n const email = options.email ?? process.env[\"SAP_EMAIL\"];\n const password = options.password ?? process.env[\"SAP_PASSWORD\"];\n if (email === undefined || email === \"\") {\n throw new CfDebuggerError(\n \"MISSING_CREDENTIALS\",\n \"SAP email is required. Pass `email` or set SAP_EMAIL env var.\",\n );\n }\n if (password === undefined || password === \"\") {\n throw new CfDebuggerError(\n \"MISSING_CREDENTIALS\",\n \"SAP password is required. Pass `password` or set SAP_PASSWORD env var.\",\n );\n }\n return { email, password };\n}\n\nasync function registerSession(\n options: StartDebuggerOptions,\n apiEndpoint: string,\n): Promise<ActiveSession> {\n const registration = await registerNewSession({\n region: options.region,\n org: options.org,\n space: options.space,\n app: options.app,\n apiEndpoint,\n ...(options.preferredPort === undefined ? {} : { preferredPort: options.preferredPort }),\n portProbe: isPortFree,\n cfHomeForSession: sessionCfHomeDir,\n });\n\n if (registration.existing) {\n throw new CfDebuggerError(\n \"SESSION_ALREADY_RUNNING\",\n `A debugger session is already running for ${sessionKeyString(options)} ` +\n `on port ${registration.existing.localPort.toString()} ` +\n `(pid ${registration.existing.pid.toString()}, sessionId ${registration.existing.sessionId}). ` +\n `Stop it first with \\`cf-debugger stop\\`.`,\n );\n }\n return registration.session;\n}\n\nasync function loginAndTarget(\n options: StartDebuggerOptions,\n apiEndpoint: string,\n email: string,\n password: string,\n context: CfExecContext,\n sessionId: string,\n emit: StatusEmitter,\n): Promise<void> {\n emit(\"logging-in\");\n await updateSessionStatus(sessionId, \"logging-in\");\n await cfLogin(apiEndpoint, email, password, context);\n checkAbort(options.signal);\n\n emit(\"targeting\");\n await updateSessionStatus(sessionId, \"targeting\");\n await cfTarget(options.org, options.space, context);\n checkAbort(options.signal);\n}\n\nasync function signalRemoteNode(\n options: StartDebuggerOptions,\n context: CfExecContext,\n sessionId: string,\n emit: StatusEmitter,\n): Promise<void> {\n emit(\"signaling\");\n await updateSessionStatus(sessionId, \"signaling\");\n const signalResult = await cfSshOneShot(options.app, `kill -s USR1 $(pidof node)`, context);\n\n if (!isSshDisabledError(signalResult.stderr)) {\n if (signalResult.exitCode === 0) {\n return;\n }\n throw new CfDebuggerError(\n \"USR1_SIGNAL_FAILED\",\n `Failed to send SIGUSR1 to the Node.js process on ${options.app}: ${signalFailureDetail(signalResult)}`,\n signalResult.stderr,\n );\n }\n\n const alreadyEnabled = await cfSshEnabled(options.app, context);\n if (!alreadyEnabled) {\n emit(\"ssh-enabling\", \"Enabling SSH on the app\");\n await updateSessionStatus(sessionId, \"ssh-enabling\");\n await cfEnableSsh(options.app, context);\n }\n emit(\"ssh-restarting\", \"Restarting app so SSH becomes active\");\n await updateSessionStatus(sessionId, \"ssh-restarting\");\n await cfRestartApp(options.app, context);\n checkAbort(options.signal);\n\n await retryRemoteSignal(options, context, sessionId, emit);\n}\n\nasync function retryRemoteSignal(\n options: StartDebuggerOptions,\n context: CfExecContext,\n sessionId: string,\n emit: StatusEmitter,\n): Promise<void> {\n emit(\"signaling\");\n await updateSessionStatus(sessionId, \"signaling\");\n const retrySignalResult = await cfSshOneShot(\n options.app,\n `kill -s USR1 $(pidof node)`,\n context,\n );\n if (retrySignalResult.exitCode === 0) {\n return;\n }\n throw new CfDebuggerError(\n \"USR1_SIGNAL_FAILED\",\n `Failed to send SIGUSR1 to the Node.js process on ${options.app} after enabling SSH: ${\n signalFailureDetail(retrySignalResult)\n }`,\n retrySignalResult.stderr,\n );\n}\n\nasync function waitAfterSignal(signal: AbortSignal | undefined): Promise<void> {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, POST_USR1_DELAY_MS);\n });\n checkAbort(signal);\n}\n\nasync function ensurePortAvailable(localPort: number): Promise<void> {\n if (await isPortFree(localPort)) {\n return;\n }\n await killProcessOnPort(localPort);\n await new Promise<void>((resolve) => {\n setTimeout(resolve, PORT_RECLAIM_DELAY_MS);\n });\n if (!(await isPortFree(localPort))) {\n throw new CfDebuggerError(\n \"PORT_UNAVAILABLE\",\n `Local port ${localPort.toString()} is in use and could not be reclaimed for the tunnel.`,\n );\n }\n}\n\nasync function openReadyTunnel(\n options: StartDebuggerOptions,\n session: ActiveSession,\n context: CfExecContext,\n tunnelReadyTimeoutMs: number,\n onChild: (child: ChildProcess) => void,\n): Promise<TunnelResult> {\n await ensurePortAvailable(session.localPort);\n const child = spawnSshTunnel(options.app, session.localPort, session.remotePort, context);\n onChild(child);\n if (child.pid !== undefined) {\n await updateSessionPid(session.sessionId, child.pid);\n }\n\n const ready = await probeTunnelReady(session.localPort, tunnelReadyTimeoutMs);\n checkAbort(options.signal);\n if (!ready) {\n throw new CfDebuggerError(\n \"TUNNEL_NOT_READY\",\n `SSH tunnel on port ${session.localPort.toString()} did not become ready within ` +\n `${Math.round(tunnelReadyTimeoutMs / 1000).toString()}s.`,\n );\n }\n\n const listeningPid = await findListeningProcessId(session.localPort);\n const activePid = listeningPid ?? child.pid ?? session.pid;\n if (activePid !== session.pid) {\n await updateSessionPid(session.sessionId, activePid);\n }\n return { child, activePid };\n}\n\nfunction attachTunnelEvents(\n child: ChildProcess,\n markClosed: () => void,\n resolveExit: (code: number | null) => void,\n emit: StatusEmitter,\n): void {\n child.on(\"close\", (code) => {\n markClosed();\n resolveExit(code);\n });\n\n child.on(\"error\", (err: Error) => {\n emit(\"error\", err.message);\n });\n}\n\nfunction createHandle(\n session: ActiveSession,\n emit: StatusEmitter,\n finalize: () => Promise<void>,\n exitPromise: Promise<number | null>,\n): DebuggerHandle {\n let disposePromise: Promise<void> | undefined;\n return {\n session,\n dispose: async (): Promise<void> => {\n disposePromise ??= (async (): Promise<void> => {\n emit(\"stopping\");\n await updateSessionStatus(session.sessionId, \"stopping\");\n await finalize();\n })();\n await disposePromise;\n },\n waitForExit: async (): Promise<number | null> => {\n return await exitPromise;\n },\n };\n}\n\nexport async function startDebugger(options: StartDebuggerOptions): Promise<DebuggerHandle> {\n const { email, password } = requireCredentials(options);\n const apiEndpoint = resolveApiEndpoint(options.region, options.apiEndpoint);\n const tunnelReadyTimeoutMs = options.tunnelReadyTimeoutMs ?? DEFAULT_TUNNEL_READY_TIMEOUT_MS;\n const emit = (status: SessionStatus, message?: string): void => {\n options.onStatus?.(status, message);\n };\n\n checkAbort(options.signal);\n await pruneAndCleanupOrphans();\n\n const session = await registerSession(options, apiEndpoint);\n const context: CfExecContext = { cfHome: session.cfHomeDir };\n let child: ChildProcess | undefined;\n let tunnelClosed = false;\n let exitResolve: (code: number | null) => void = (_code) => {\n throw new Error(\"Exit resolver was used before initialization\");\n };\n const exitPromise = new Promise<number | null>((resolve) => {\n exitResolve = resolve;\n });\n\n const finalize = async (): Promise<void> => {\n if (!tunnelClosed) {\n tunnelClosed = true;\n if (child) {\n await killProcessGroupOrProc(child);\n }\n setTimeout(() => {\n void killProcessOnPort(session.localPort);\n }, PORT_CLEANUP_DELAY_MS);\n }\n await removeSession(session.sessionId);\n await cleanupFilesystem(session.cfHomeDir);\n emit(\"stopped\");\n };\n\n try {\n await mkdir(session.cfHomeDir, { recursive: true });\n await loginAndTarget(options, apiEndpoint, email, password, context, session.sessionId, emit);\n await killProcessOnPort(session.localPort);\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 200);\n });\n await signalRemoteNode(options, context, session.sessionId, emit);\n await waitAfterSignal(options.signal);\n\n emit(\"tunneling\");\n await updateSessionStatus(session.sessionId, \"tunneling\");\n const tunnel = await openReadyTunnel(\n options,\n session,\n context,\n tunnelReadyTimeoutMs,\n (tunnelChild) => {\n child = tunnelChild;\n attachTunnelEvents(tunnelChild, () => {\n tunnelClosed = true;\n }, exitResolve, emit);\n },\n );\n child = tunnel.child;\n\n emit(\"ready\");\n const readySession = await updateSessionStatus(session.sessionId, \"ready\");\n const activeSession = readySession ?? { ...session, pid: tunnel.activePid, status: \"ready\" };\n return createHandle(activeSession, emit, finalize, exitPromise);\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n emit(\"error\", message);\n await finalize();\n throw err;\n }\n}\n\nasync function cleanupFilesystem(cfHomeDir: string): Promise<void> {\n try {\n await rm(cfHomeDir, { recursive: true, force: true });\n } catch {\n // best-effort\n }\n}\n","export interface SessionKey {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n}\n\nexport type SessionStatus =\n | \"starting\"\n | \"logging-in\"\n | \"targeting\"\n | \"ssh-enabling\"\n | \"ssh-restarting\"\n | \"signaling\"\n | \"tunneling\"\n | \"ready\"\n | \"stopping\"\n | \"stopped\"\n | \"error\";\n\nexport interface ActiveSession extends SessionKey {\n readonly sessionId: string;\n readonly pid: number;\n readonly hostname: string;\n readonly localPort: number;\n readonly remotePort: number;\n readonly apiEndpoint: string;\n readonly cfHomeDir: string;\n readonly startedAt: string;\n readonly status: SessionStatus;\n readonly message?: string;\n}\n\nexport interface StartDebuggerOptions extends SessionKey {\n readonly email?: string;\n readonly password?: string;\n readonly apiEndpoint?: string;\n readonly preferredPort?: number;\n readonly tunnelReadyTimeoutMs?: number;\n readonly verbose?: boolean;\n readonly onStatus?: (status: SessionStatus, message?: string) => void;\n readonly signal?: AbortSignal;\n}\n\nexport interface DebuggerHandle {\n readonly session: ActiveSession;\n dispose(): Promise<void>;\n waitForExit(): Promise<number | null>;\n}\n\nexport interface StateFile {\n readonly version: \"1\";\n readonly sessions: readonly ActiveSession[];\n}\n\nexport class CfDebuggerError extends Error {\n public readonly code: string;\n public readonly stderr?: string;\n\n public constructor(code: string, message: string, stderr?: string) {\n super(message);\n this.name = \"CfDebuggerError\";\n this.code = code;\n if (stderr !== undefined) {\n this.stderr = stderr;\n }\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport { CfDebuggerError } from \"../types.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst MAX_BUFFER = 16 * 1024 * 1024;\nexport const DEFAULT_CF_COMMAND_TIMEOUT_MS = 180_000;\nconst REDACTED_ARG = \"<redacted>\";\n\nexport interface CfExecContext {\n readonly cfHome: string;\n readonly command?: string;\n}\n\nexport function buildEnv(cfHome: string): NodeJS.ProcessEnv {\n return { ...process.env, CF_HOME: cfHome };\n}\n\nexport function resolveBin(context: CfExecContext): string {\n return context.command ?? process.env[\"CF_DEBUGGER_CF_BIN\"] ?? \"cf\";\n}\n\nfunction sensitiveArgs(args: readonly string[]): readonly string[] {\n if (args[0] !== \"auth\") {\n return [];\n }\n return args.slice(1).filter((arg) => arg.length > 0);\n}\n\nfunction redactText(text: string, values: readonly string[]): string {\n return values.reduce((current, value) => current.split(value).join(REDACTED_ARG), text);\n}\n\nfunction formatArgsForError(args: readonly string[]): string {\n if (args[0] !== \"auth\") {\n return args.join(\" \");\n }\n return args.map((arg, index) => (index === 0 ? arg : REDACTED_ARG)).join(\" \");\n}\n\nexport async function runCf(\n args: readonly string[],\n context: CfExecContext,\n timeoutMs: number = DEFAULT_CF_COMMAND_TIMEOUT_MS,\n): Promise<string> {\n try {\n const { stdout } = await execFileAsync(resolveBin(context), [...args], {\n env: buildEnv(context.cfHome),\n maxBuffer: MAX_BUFFER,\n timeout: timeoutMs,\n });\n return stdout;\n } catch (err: unknown) {\n const e = err as NodeJS.ErrnoException & { stderr?: string; stdout?: string };\n const redactionValues = sensitiveArgs(args);\n const stderr = redactText(e.stderr?.trim() ?? \"\", redactionValues);\n const fallbackMessage = redactText(e.message, redactionValues);\n throw new CfDebuggerError(\n \"CF_CLI_FAILED\",\n `cf ${formatArgsForError(args)} failed: ${stderr.length > 0 ? stderr : fallbackMessage}`,\n stderr,\n );\n }\n}\n","import { CfDebuggerError } from \"../types.js\";\n\nimport { type CfExecContext, runCf } from \"./execute.js\";\nimport { parseAppNames, parseNameTable } from \"./parsers.js\";\n\nconst CF_AUTH_MAX_ATTEMPTS = 3;\n\nexport async function cfApi(apiEndpoint: string, context: CfExecContext): Promise<void> {\n await runCf([\"api\", apiEndpoint], context);\n}\n\nexport async function cfAuth(\n email: string,\n password: string,\n context: CfExecContext,\n): Promise<void> {\n let lastError: unknown;\n for (let attempt = 0; attempt < CF_AUTH_MAX_ATTEMPTS; attempt++) {\n try {\n await runCf([\"auth\", email, password], context);\n return;\n } catch (err: unknown) {\n lastError = err;\n if (attempt < CF_AUTH_MAX_ATTEMPTS - 1) {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 1000 * (attempt + 1));\n });\n }\n }\n }\n if (lastError instanceof Error) {\n throw lastError;\n }\n throw new CfDebuggerError(\"CF_AUTH_FAILED\", `cf auth failed: ${String(lastError)}`);\n}\n\nexport async function cfLogin(\n apiEndpoint: string,\n email: string,\n password: string,\n context: CfExecContext,\n): Promise<void> {\n try {\n await cfApi(apiEndpoint, context);\n await cfAuth(email, password, context);\n } catch (err: unknown) {\n if (err instanceof CfDebuggerError) {\n throw new CfDebuggerError(\"CF_LOGIN_FAILED\", err.message, err.stderr);\n }\n throw err;\n }\n}\n\nexport async function cfTarget(\n org: string,\n space: string,\n context: CfExecContext,\n): Promise<void> {\n try {\n await runCf([\"target\", \"-o\", org, \"-s\", space], context);\n } catch (err: unknown) {\n if (err instanceof CfDebuggerError) {\n throw new CfDebuggerError(\"CF_TARGET_FAILED\", err.message, err.stderr);\n }\n throw err;\n }\n}\n\nexport async function cfAppExists(appName: string, context: CfExecContext): Promise<boolean> {\n try {\n await runCf([\"app\", appName], context);\n return true;\n } catch (err: unknown) {\n const stderr = (err as CfDebuggerError).stderr ?? \"\";\n if (stderr.toLowerCase().includes(\"not found\")) {\n return false;\n }\n throw err;\n }\n}\n\nexport async function cfSshEnabled(appName: string, context: CfExecContext): Promise<boolean> {\n try {\n const stdout = await runCf([\"ssh-enabled\", appName], context);\n return stdout.toLowerCase().includes(\"ssh support is enabled\");\n } catch {\n return false;\n }\n}\n\nexport async function cfEnableSsh(appName: string, context: CfExecContext): Promise<void> {\n try {\n await runCf([\"enable-ssh\", appName], context);\n } catch (err: unknown) {\n if (err instanceof CfDebuggerError) {\n throw new CfDebuggerError(\"SSH_NOT_ENABLED\", err.message, err.stderr);\n }\n throw err;\n }\n}\n\nexport async function cfRestartApp(appName: string, context: CfExecContext): Promise<void> {\n await runCf([\"restart\", appName], context);\n}\n\nexport async function cfApps(context: CfExecContext): Promise<readonly string[]> {\n const stdout = await runCf([\"apps\"], context);\n return parseAppNames(stdout);\n}\n\nexport async function cfOrgs(context: CfExecContext): Promise<readonly string[]> {\n const stdout = await runCf([\"orgs\"], context);\n return parseNameTable(stdout);\n}\n\nexport async function cfSpaces(context: CfExecContext): Promise<readonly string[]> {\n const stdout = await runCf([\"spaces\"], context);\n return parseNameTable(stdout);\n}\n","import { spawn } from \"node:child_process\";\n\nimport {\n buildEnv,\n DEFAULT_CF_COMMAND_TIMEOUT_MS,\n resolveBin,\n type CfExecContext,\n} from \"./execute.js\";\n\nexport interface CfSshSignalResult {\n readonly exitCode: number | null;\n readonly stderr: string;\n readonly signal?: NodeJS.Signals;\n readonly timedOutAfterMs?: number;\n}\n\nexport async function cfSshOneShot(\n appName: string,\n command: string,\n context: CfExecContext,\n timeoutMs: number = DEFAULT_CF_COMMAND_TIMEOUT_MS,\n): Promise<CfSshSignalResult> {\n return await new Promise<CfSshSignalResult>((resolve) => {\n const child = spawn(resolveBin(context), [\"ssh\", appName, \"-c\", command], {\n env: buildEnv(context.cfHome),\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n let stderrBuf = \"\";\n let settled = false;\n\n const timeout = setTimeout(() => {\n if (settled) {\n return;\n }\n settled = true;\n try {\n child.kill();\n } catch {\n // already gone\n }\n resolve({ exitCode: null, stderr: stderrBuf, timedOutAfterMs: timeoutMs });\n }, timeoutMs);\n\n child.stderr.on(\"data\", (data: Buffer | string) => {\n stderrBuf += data.toString();\n });\n\n child.on(\"close\", (code, signal) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timeout);\n resolve(\n signal === null\n ? { exitCode: code, stderr: stderrBuf }\n : { exitCode: code, stderr: stderrBuf, signal },\n );\n });\n\n child.on(\"error\", (err: Error) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timeout);\n resolve({ exitCode: null, stderr: err.message });\n });\n });\n}\n\nexport function isSshDisabledError(stderr: string): boolean {\n const lower = stderr.toLowerCase();\n return lower.includes(\"not authorized\") || lower.includes(\"ssh support is disabled\");\n}\n\nexport function spawnSshTunnel(\n appName: string,\n localPort: number,\n remotePort: number,\n context: CfExecContext,\n): ReturnType<typeof spawn> {\n const tunnelArg = `${localPort.toString()}:localhost:${remotePort.toString()}`;\n const isWindows = process.platform === \"win32\";\n return spawn(resolveBin(context), [\"ssh\", appName, \"-N\", \"-L\", tunnelArg], {\n env: buildEnv(context.cfHome),\n shell: isWindows,\n detached: !isWindows,\n });\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const SAPTOOLS_DIR_NAME = \".saptools\";\nexport const CF_DEBUGGER_STATE_FILENAME = \"cf-debugger-state.json\";\nexport const CF_DEBUGGER_LOCK_FILENAME = \"cf-debugger-state.lock\";\nexport const CF_DEBUGGER_HOMES_DIRNAME = \"cf-debugger-homes\";\n\nexport function saptoolsDir(): string {\n return join(homedir(), SAPTOOLS_DIR_NAME);\n}\n\nexport function stateFilePath(): string {\n return join(saptoolsDir(), CF_DEBUGGER_STATE_FILENAME);\n}\n\nexport function stateLockPath(): string {\n return join(saptoolsDir(), CF_DEBUGGER_LOCK_FILENAME);\n}\n\nexport function sessionCfHomeDir(sessionId: string): string {\n return join(saptoolsDir(), CF_DEBUGGER_HOMES_DIRNAME, sessionId);\n}\n","import { execFile } from \"node:child_process\";\nimport { createConnection, createServer } from \"node:net\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nasync function findListeningPidsWithNetstat(port: number): Promise<readonly number[]> {\n try {\n const { stdout } = await execFileAsync(\"netstat\", [\"-ano\"]);\n const pids = new Set<number>();\n for (const line of stdout.split(\"\\n\")) {\n if (!line.includes(`:${port.toString()}`) || !line.includes(\"LISTENING\")) {\n continue;\n }\n const parts = line.trim().split(/\\s+/);\n const last = parts[parts.length - 1];\n if (last === undefined) {\n continue;\n }\n const pid = Number.parseInt(last, 10);\n if (!Number.isNaN(pid)) {\n pids.add(pid);\n }\n }\n return [...pids];\n } catch {\n return [];\n }\n}\n\nasync function findListeningPidsWithLsof(port: number): Promise<readonly number[]> {\n try {\n const { stdout } = await execFileAsync(\"lsof\", [\"-nP\", \"-t\", \"-i\", `tcp:${port.toString()}`, \"-sTCP:LISTEN\"]);\n return stdout\n .trim()\n .split(\"\\n\")\n .filter((line) => line.length > 0)\n .map((line) => Number.parseInt(line, 10))\n .filter((pid) => !Number.isNaN(pid));\n } catch {\n return [];\n }\n}\n\nasync function findListeningPids(port: number): Promise<readonly number[]> {\n if (process.platform === \"win32\") {\n return await findListeningPidsWithNetstat(port);\n }\n return await findListeningPidsWithLsof(port);\n}\n\nexport async function isPortFree(port: number): Promise<boolean> {\n return await new Promise<boolean>((resolve) => {\n const server = createServer();\n server.once(\"error\", () => {\n resolve(false);\n });\n server.once(\"listening\", () => {\n server.close(() => {\n resolve(true);\n });\n });\n server.listen(port, \"127.0.0.1\");\n });\n}\n\nexport async function probeTunnelReady(port: number, timeoutMs: number): Promise<boolean> {\n const pollIntervalMs = 250;\n const started = Date.now();\n\n while (Date.now() - started < timeoutMs) {\n const connected = await new Promise<boolean>((resolve) => {\n const socket = createConnection({ port, host: \"127.0.0.1\" });\n socket.setTimeout(200);\n socket.once(\"connect\", () => {\n socket.destroy();\n resolve(true);\n });\n socket.once(\"error\", () => {\n socket.destroy();\n resolve(false);\n });\n socket.once(\"timeout\", () => {\n socket.destroy();\n resolve(false);\n });\n });\n\n if (connected) {\n return true;\n }\n\n await new Promise<void>((resolve) => {\n setTimeout(resolve, pollIntervalMs);\n });\n }\n\n return false;\n}\n\nexport async function findListeningProcessId(port: number): Promise<number | undefined> {\n const pids = await findListeningPids(port);\n return pids[0];\n}\n\nexport async function killProcessOnPort(port: number): Promise<void> {\n const portStr = port.toString();\n if (process.platform === \"win32\") {\n try {\n const pids = await findListeningPidsWithNetstat(port);\n for (const pid of pids) {\n try {\n // cspell:ignore taskkill\n await execFileAsync(\"taskkill\", [\"/F\", \"/PID\", pid.toString()]);\n } catch {\n // ignore\n }\n }\n } catch {\n // ignore\n }\n return;\n }\n\n try {\n const { stdout } = await execFileAsync(\"lsof\", [\"-t\", \"-i\", `tcp:${portStr}`]);\n const lines = stdout.trim().split(\"\\n\").filter((line) => line.length > 0);\n for (const line of lines) {\n const pid = Number.parseInt(line, 10);\n if (Number.isNaN(pid)) {\n continue;\n }\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {\n // already dead\n }\n }\n } catch {\n // lsof missing or no match — ignore\n }\n}\n","export interface RegionInfo {\n readonly key: string;\n readonly apiEndpoint: string;\n}\n\nconst REGION_API_ENDPOINTS: Readonly<Record<string, string>> = {\n ae01: \"https://api.cf.ae01.hana.ondemand.com\",\n ap01: \"https://api.cf.ap01.hana.ondemand.com\",\n ap10: \"https://api.cf.ap10.hana.ondemand.com\",\n ap11: \"https://api.cf.ap11.hana.ondemand.com\",\n ap12: \"https://api.cf.ap12.hana.ondemand.com\",\n ap20: \"https://api.cf.ap20.hana.ondemand.com\",\n ap21: \"https://api.cf.ap21.hana.ondemand.com\",\n ap30: \"https://api.cf.ap30.hana.ondemand.com\",\n br10: \"https://api.cf.br10.hana.ondemand.com\",\n br20: \"https://api.cf.br20.hana.ondemand.com\",\n br30: \"https://api.cf.br30.hana.ondemand.com\",\n ca10: \"https://api.cf.ca10.hana.ondemand.com\",\n ca20: \"https://api.cf.ca20.hana.ondemand.com\",\n ch20: \"https://api.cf.ch20.hana.ondemand.com\",\n eu10: \"https://api.cf.eu10.hana.ondemand.com\",\n eu11: \"https://api.cf.eu11.hana.ondemand.com\",\n eu12: \"https://api.cf.eu12.hana.ondemand.com\",\n eu20: \"https://api.cf.eu20.hana.ondemand.com\",\n eu21: \"https://api.cf.eu21.hana.ondemand.com\",\n eu30: \"https://api.cf.eu30.hana.ondemand.com\",\n eu31: \"https://api.cf.eu31.hana.ondemand.com\",\n in30: \"https://api.cf.in30.hana.ondemand.com\",\n jp10: \"https://api.cf.jp10.hana.ondemand.com\",\n jp20: \"https://api.cf.jp20.hana.ondemand.com\",\n jp30: \"https://api.cf.jp30.hana.ondemand.com\",\n kr30: \"https://api.cf.kr30.hana.ondemand.com\",\n us10: \"https://api.cf.us10.hana.ondemand.com\",\n us11: \"https://api.cf.us11.hana.ondemand.com\",\n us20: \"https://api.cf.us20.hana.ondemand.com\",\n us21: \"https://api.cf.us21.hana.ondemand.com\",\n us30: \"https://api.cf.us30.hana.ondemand.com\",\n us31: \"https://api.cf.us31.hana.ondemand.com\",\n};\n\nexport function resolveApiEndpoint(regionKey: string, override?: string): string {\n if (override !== undefined && override !== \"\") {\n return override;\n }\n const endpoint = REGION_API_ENDPOINTS[regionKey];\n if (endpoint === undefined) {\n throw new Error(\n `Unknown region key: ${regionKey}. Pass \\`apiEndpoint\\` explicitly to override.`,\n );\n }\n return endpoint;\n}\n\nexport function listKnownRegionKeys(): readonly string[] {\n return Object.keys(REGION_API_ENDPOINTS);\n}\n","import { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { hostname as getHostname } from \"node:os\";\nimport { dirname } from \"node:path\";\nimport process from \"node:process\";\n\nimport { withFileLock } from \"../lock.js\";\nimport { stateFilePath, stateLockPath } from \"../paths.js\";\nimport { CfDebuggerError } from \"../types.js\";\nimport type { ActiveSession, SessionKey, StateFile } from \"../types.js\";\n\nasync function readJsonFile<T>(path: string): Promise<T | undefined> {\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") {\n return undefined;\n }\n throw err;\n }\n try {\n return JSON.parse(raw) as T;\n } catch {\n process.stderr.write(\n `[cf-debugger] warning: state file at ${path} is not valid JSON; resetting to empty.\\n`,\n );\n return undefined;\n }\n}\n\nasync function writeJsonFileAtomic(path: string, value: unknown): Promise<void> {\n const tempPath = `${path}.${randomUUID()}.tmp`;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(tempPath, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n await rename(tempPath, path);\n}\n\nfunction emptyState(): StateFile {\n return { version: \"1\", sessions: [] };\n}\n\nfunction isValidState(value: unknown): value is StateFile {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const candidate = value as Partial<StateFile>;\n return candidate.version === \"1\" && Array.isArray(candidate.sessions);\n}\n\nexport function isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ESRCH\") {\n return false;\n }\n return true;\n }\n}\n\nfunction filterStaleSessions(sessions: readonly ActiveSession[]): readonly ActiveSession[] {\n const host = getHostname();\n return sessions.filter((session) => {\n if (session.hostname !== host) {\n return true;\n }\n return isPidAlive(session.pid);\n });\n}\n\nasync function readStateRaw(): Promise<StateFile> {\n const parsed = await readJsonFile<unknown>(stateFilePath());\n if (!isValidState(parsed)) {\n return emptyState();\n }\n return parsed;\n}\n\nasync function writeState(state: StateFile): Promise<void> {\n await writeJsonFileAtomic(stateFilePath(), state);\n}\n\nexport interface StateReaderResult {\n readonly sessions: readonly ActiveSession[];\n readonly removed: readonly ActiveSession[];\n}\n\nasync function readAndPruneLocked(): Promise<StateReaderResult> {\n const raw = await readStateRaw();\n const pruned = filterStaleSessions(raw.sessions);\n const removed = raw.sessions.filter(\n (session) => !pruned.some((active) => active.sessionId === session.sessionId),\n );\n\n if (removed.length > 0) {\n await writeState({ version: \"1\", sessions: pruned });\n }\n\n return { sessions: pruned, removed };\n}\n\nexport async function readActiveSessions(): Promise<readonly ActiveSession[]> {\n const result = await withFileLock(stateLockPath(), readAndPruneLocked);\n return result.sessions;\n}\n\nexport async function readSessionSnapshot(): Promise<readonly ActiveSession[]> {\n return await withFileLock(stateLockPath(), async (): Promise<readonly ActiveSession[]> => {\n const raw = await readStateRaw();\n return raw.sessions;\n });\n}\n\nexport async function readAndPruneActiveSessions(): Promise<StateReaderResult> {\n return await withFileLock(stateLockPath(), readAndPruneLocked);\n}\n\nexport function sessionKeyString(key: SessionKey): string {\n return `${key.region}:${key.org}:${key.space}:${key.app}`;\n}\n\nexport function matchesKey(session: SessionKey, key: SessionKey): boolean {\n return (\n session.region === key.region &&\n session.org === key.org &&\n session.space === key.space &&\n session.app === key.app\n );\n}\n\nexport interface RegisterSessionResult {\n readonly session: ActiveSession;\n readonly existing?: ActiveSession;\n}\n\nexport interface RegisterSessionInput extends SessionKey {\n readonly apiEndpoint: string;\n readonly preferredPort?: number;\n readonly portProbe: (port: number) => Promise<boolean>;\n readonly sessionIdFactory?: () => string;\n readonly cfHomeForSession: (sessionId: string) => string;\n readonly basePort?: number;\n readonly maxPort?: number;\n}\n\nconst DEFAULT_BASE_PORT = 20_000;\nconst DEFAULT_MAX_PORT = 20_999;\n\nasync function pickPort(\n preferred: number | undefined,\n reserved: ReadonlySet<number>,\n probe: (port: number) => Promise<boolean>,\n basePort: number,\n maxPort: number,\n): Promise<number> {\n const tryOrder: number[] = [];\n if (preferred !== undefined) {\n tryOrder.push(preferred);\n }\n for (let port = basePort; port <= maxPort; port++) {\n if (port !== preferred) {\n tryOrder.push(port);\n }\n }\n\n for (const port of tryOrder) {\n if (reserved.has(port)) {\n continue;\n }\n const free = await probe(port);\n if (free) {\n return port;\n }\n }\n throw new CfDebuggerError(\n \"PORT_UNAVAILABLE\",\n `No free local port available in range ${basePort.toString()}–${maxPort.toString()}`,\n );\n}\n\nexport async function registerNewSession(\n input: RegisterSessionInput,\n): Promise<RegisterSessionResult> {\n return await withFileLock(stateLockPath(), async (): Promise<RegisterSessionResult> => {\n const pruneResult = await readAndPruneLocked();\n const existing = pruneResult.sessions.find((session) => matchesKey(session, input));\n if (existing) {\n return { session: existing, existing };\n }\n\n const reservedPorts = new Set(pruneResult.sessions.map((session) => session.localPort));\n const localPort = await pickPort(\n input.preferredPort,\n reservedPorts,\n input.portProbe,\n input.basePort ?? DEFAULT_BASE_PORT,\n input.maxPort ?? DEFAULT_MAX_PORT,\n );\n\n const sessionId = (input.sessionIdFactory ?? randomUUID)();\n const cfHomeDir = input.cfHomeForSession(sessionId);\n\n const session: ActiveSession = {\n sessionId,\n pid: process.pid,\n hostname: getHostname(),\n region: input.region,\n org: input.org,\n space: input.space,\n app: input.app,\n apiEndpoint: input.apiEndpoint,\n localPort,\n remotePort: 9229,\n cfHomeDir,\n startedAt: new Date().toISOString(),\n status: \"starting\",\n };\n\n const nextSessions: readonly ActiveSession[] = [...pruneResult.sessions, session];\n await writeState({ version: \"1\", sessions: nextSessions });\n\n return { session };\n });\n}\n\nexport async function updateSessionStatus(\n sessionId: string,\n status: ActiveSession[\"status\"],\n message?: string,\n): Promise<ActiveSession | undefined> {\n return await withFileLock(stateLockPath(), async (): Promise<ActiveSession | undefined> => {\n const raw = await readStateRaw();\n let updated: ActiveSession | undefined;\n const nextSessions = raw.sessions.map((session): ActiveSession => {\n if (session.sessionId !== sessionId) {\n return session;\n }\n const base: ActiveSession = {\n sessionId: session.sessionId,\n pid: session.pid,\n hostname: session.hostname,\n region: session.region,\n org: session.org,\n space: session.space,\n app: session.app,\n apiEndpoint: session.apiEndpoint,\n localPort: session.localPort,\n remotePort: session.remotePort,\n cfHomeDir: session.cfHomeDir,\n startedAt: session.startedAt,\n status,\n };\n const next: ActiveSession = message === undefined ? base : { ...base, message };\n updated = next;\n return next;\n });\n\n if (updated) {\n await writeState({ version: \"1\", sessions: nextSessions });\n }\n return updated;\n });\n}\n\nexport async function updateSessionPid(\n sessionId: string,\n pid: number,\n): Promise<ActiveSession | undefined> {\n return await withFileLock(stateLockPath(), async (): Promise<ActiveSession | undefined> => {\n const raw = await readStateRaw();\n let updated: ActiveSession | undefined;\n const nextSessions = raw.sessions.map((session): ActiveSession => {\n if (session.sessionId !== sessionId) {\n return session;\n }\n const next: ActiveSession = {\n sessionId: session.sessionId,\n pid,\n hostname: session.hostname,\n region: session.region,\n org: session.org,\n space: session.space,\n app: session.app,\n apiEndpoint: session.apiEndpoint,\n localPort: session.localPort,\n remotePort: session.remotePort,\n cfHomeDir: session.cfHomeDir,\n startedAt: session.startedAt,\n status: session.status,\n ...(session.message === undefined ? {} : { message: session.message }),\n };\n updated = next;\n return next;\n });\n\n if (updated !== undefined) {\n await writeState({ version: \"1\", sessions: nextSessions });\n }\n return updated;\n });\n}\n\nexport async function removeSession(sessionId: string): Promise<ActiveSession | undefined> {\n return await withFileLock(stateLockPath(), async (): Promise<ActiveSession | undefined> => {\n const raw = await readStateRaw();\n const target = raw.sessions.find((session) => session.sessionId === sessionId);\n if (!target) {\n return undefined;\n }\n const remaining = raw.sessions.filter((session) => session.sessionId !== sessionId);\n await writeState({ version: \"1\", sessions: remaining });\n return target;\n });\n}\n","import { mkdir, open, unlink } from \"node:fs/promises\";\nimport type { FileHandle } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nconst DEFAULT_POLL_MS = 50;\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nasync function acquireFileLock(\n lockPath: string,\n timeoutMs: number,\n pollMs: number,\n): Promise<FileHandle> {\n const deadline = Date.now() + timeoutMs;\n await mkdir(dirname(lockPath), { recursive: true });\n\n for (;;) {\n try {\n return await open(lockPath, \"wx\");\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"EEXIST\") {\n throw err;\n }\n }\n\n if (Date.now() > deadline) {\n throw new Error(`Timed out acquiring file lock at ${lockPath}`);\n }\n\n await sleep(pollMs);\n }\n}\n\nasync function releaseFileLock(lockPath: string, handle: FileHandle): Promise<void> {\n await handle.close();\n await unlink(lockPath).catch((err: unknown) => {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"ENOENT\") {\n throw err;\n }\n });\n}\n\nexport interface WithLockOptions {\n readonly timeoutMs?: number;\n readonly pollMs?: number;\n}\n\nexport async function withFileLock<T>(\n lockPath: string,\n work: () => Promise<T>,\n options?: WithLockOptions,\n): Promise<T> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const pollMs = options?.pollMs ?? DEFAULT_POLL_MS;\n const handle = await acquireFileLock(lockPath, timeoutMs, pollMs);\n try {\n return await work();\n } finally {\n await releaseFileLock(lockPath, handle);\n }\n}\n","export const DEFAULT_TUNNEL_READY_TIMEOUT_MS = 180_000;\nexport const POST_USR1_DELAY_MS = 300;\nexport const PORT_CLEANUP_DELAY_MS = 600;\nexport const CHILD_SIGTERM_GRACE_MS = 2_000;\nexport const PORT_RECLAIM_DELAY_MS = 250;\nexport const PID_TERMINATION_POLL_MS = 100;\n","import { hostname as getHostname } from \"node:os\";\n\nimport { killProcessOnPort } from \"../port.js\";\nimport { readAndPruneActiveSessions } from \"../state.js\";\nimport type { ActiveSession } from \"../types.js\";\n\nexport async function pruneAndCleanupOrphans(): Promise<readonly ActiveSession[]> {\n const result = await readAndPruneActiveSessions();\n const host = getHostname();\n for (const removed of result.removed) {\n if (removed.hostname === host) {\n void killProcessOnPort(removed.localPort);\n }\n }\n return result.sessions;\n}\n","import type { ChildProcess } from \"node:child_process\";\nimport process from \"node:process\";\n\nimport { isPidAlive } from \"../state.js\";\n\nimport { CHILD_SIGTERM_GRACE_MS, PID_TERMINATION_POLL_MS } from \"./constants.js\";\n\nfunction signalPidOrGroup(pid: number, signal: NodeJS.Signals): void {\n const isWindows = process.platform === \"win32\";\n if (!isWindows) {\n try {\n process.kill(-pid, signal);\n return;\n } catch {\n // fall through to direct pid signal\n }\n }\n try {\n process.kill(pid, signal);\n } catch {\n // already gone\n }\n}\n\nexport async function terminatePidOrGroup(\n pid: number,\n timeoutMs: number = CHILD_SIGTERM_GRACE_MS,\n): Promise<void> {\n if (!isPidAlive(pid)) {\n return;\n }\n\n signalPidOrGroup(pid, \"SIGTERM\");\n const startedAt = Date.now();\n while (Date.now() - startedAt < timeoutMs) {\n if (!isPidAlive(pid)) {\n return;\n }\n await new Promise<void>((resolve) => {\n setTimeout(resolve, PID_TERMINATION_POLL_MS);\n });\n }\n\n signalPidOrGroup(pid, \"SIGKILL\");\n}\n\nexport async function killProcessGroupOrProc(\n child: ChildProcess,\n timeoutMs: number = CHILD_SIGTERM_GRACE_MS,\n): Promise<void> {\n if (child.pid === undefined) {\n return;\n }\n if (child.exitCode !== null || child.signalCode !== null) {\n return;\n }\n await terminatePidOrGroup(child.pid, timeoutMs);\n}\n","import { rm } from \"node:fs/promises\";\nimport process from \"node:process\";\n\nimport { killProcessOnPort } from \"../port.js\";\nimport { matchesKey, readSessionSnapshot, removeSession } from \"../state.js\";\nimport type { ActiveSession, SessionKey } from \"../types.js\";\n\nimport { PORT_CLEANUP_DELAY_MS } from \"./constants.js\";\nimport { pruneAndCleanupOrphans } from \"./orphans.js\";\nimport { terminatePidOrGroup } from \"./processes.js\";\n\nexport interface StopOptions {\n readonly sessionId?: string;\n readonly key?: SessionKey;\n}\n\nexport async function stopDebugger(options: StopOptions): Promise<ActiveSession | undefined> {\n const sessions = await pruneAndCleanupOrphans();\n let target: ActiveSession | undefined;\n if (options.sessionId !== undefined) {\n target = sessions.find((s) => s.sessionId === options.sessionId);\n } else if (options.key !== undefined) {\n const key = options.key;\n target = sessions.find((s) => matchesKey(s, key));\n }\n if (target === undefined) {\n return undefined;\n }\n if (target.pid !== process.pid) {\n try {\n await terminatePidOrGroup(target.pid);\n } catch {\n // process already gone — cleanup below\n }\n }\n setTimeout(() => {\n void killProcessOnPort(target.localPort);\n }, PORT_CLEANUP_DELAY_MS);\n const removed = await removeSession(target.sessionId);\n try {\n await rm(target.cfHomeDir, { recursive: true, force: true });\n } catch {\n // best-effort\n }\n return removed ?? target;\n}\n\nexport async function stopAllDebuggers(): Promise<number> {\n const sessions = await pruneAndCleanupOrphans();\n let stopped = 0;\n for (const session of sessions) {\n const result = await stopDebugger({ sessionId: session.sessionId });\n if (result) {\n stopped += 1;\n }\n }\n return stopped;\n}\n\nexport async function listSessions(): Promise<readonly ActiveSession[]> {\n return await readSessionSnapshot();\n}\n\nexport async function getSession(key: SessionKey): Promise<ActiveSession | undefined> {\n const sessions = await readSessionSnapshot();\n return sessions.find((s) => matchesKey(s, key));\n}\n"],"mappings":"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","names":["process","mkdir","process","execFile","promisify","execFileAsync","mkdir","dirname","process","process","mkdir","dirname","session","process","getHostname","getHostname","process","process","process","mkdir","rm","process","process","rm","process"]}
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import { execFile } from "child_process";
|
|
|
23
23
|
import { promisify } from "util";
|
|
24
24
|
var execFileAsync = promisify(execFile);
|
|
25
25
|
var MAX_BUFFER = 16 * 1024 * 1024;
|
|
26
|
-
var
|
|
26
|
+
var DEFAULT_CF_COMMAND_TIMEOUT_MS = 18e4;
|
|
27
27
|
var REDACTED_ARG = "<redacted>";
|
|
28
28
|
function buildEnv(cfHome) {
|
|
29
29
|
return { ...process.env, CF_HOME: cfHome };
|
|
@@ -46,7 +46,7 @@ function formatArgsForError(args) {
|
|
|
46
46
|
}
|
|
47
47
|
return args.map((arg, index) => index === 0 ? arg : REDACTED_ARG).join(" ");
|
|
48
48
|
}
|
|
49
|
-
async function runCf(args, context, timeoutMs =
|
|
49
|
+
async function runCf(args, context, timeoutMs = DEFAULT_CF_COMMAND_TIMEOUT_MS) {
|
|
50
50
|
try {
|
|
51
51
|
const { stdout } = await execFileAsync(resolveBin(context), [...args], {
|
|
52
52
|
env: buildEnv(context.cfHome),
|
|
@@ -68,7 +68,6 @@ async function runCf(args, context, timeoutMs = CF_CLI_TIMEOUT_MS) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// src/cloud-foundry/commands.ts
|
|
71
|
-
var CF_RESTART_TIMEOUT_MS = 12e4;
|
|
72
71
|
var CF_AUTH_MAX_ATTEMPTS = 3;
|
|
73
72
|
async function cfApi(apiEndpoint, context) {
|
|
74
73
|
await runCf(["api", apiEndpoint], context);
|
|
@@ -133,13 +132,12 @@ async function cfEnableSsh(appName, context) {
|
|
|
133
132
|
}
|
|
134
133
|
}
|
|
135
134
|
async function cfRestartApp(appName, context) {
|
|
136
|
-
await runCf(["restart", appName], context
|
|
135
|
+
await runCf(["restart", appName], context);
|
|
137
136
|
}
|
|
138
137
|
|
|
139
138
|
// src/cloud-foundry/ssh.ts
|
|
140
139
|
import { spawn } from "child_process";
|
|
141
|
-
|
|
142
|
-
async function cfSshOneShot(appName, command, context) {
|
|
140
|
+
async function cfSshOneShot(appName, command, context, timeoutMs = DEFAULT_CF_COMMAND_TIMEOUT_MS) {
|
|
143
141
|
return await new Promise((resolve) => {
|
|
144
142
|
const child = spawn(resolveBin(context), ["ssh", appName, "-c", command], {
|
|
145
143
|
env: buildEnv(context.cfHome),
|
|
@@ -156,18 +154,20 @@ async function cfSshOneShot(appName, command, context) {
|
|
|
156
154
|
child.kill();
|
|
157
155
|
} catch {
|
|
158
156
|
}
|
|
159
|
-
resolve({ exitCode: null, stderr: stderrBuf });
|
|
160
|
-
},
|
|
157
|
+
resolve({ exitCode: null, stderr: stderrBuf, timedOutAfterMs: timeoutMs });
|
|
158
|
+
}, timeoutMs);
|
|
161
159
|
child.stderr.on("data", (data) => {
|
|
162
160
|
stderrBuf += data.toString();
|
|
163
161
|
});
|
|
164
|
-
child.on("close", (code) => {
|
|
162
|
+
child.on("close", (code, signal) => {
|
|
165
163
|
if (settled) {
|
|
166
164
|
return;
|
|
167
165
|
}
|
|
168
166
|
settled = true;
|
|
169
167
|
clearTimeout(timeout);
|
|
170
|
-
resolve(
|
|
168
|
+
resolve(
|
|
169
|
+
signal === null ? { exitCode: code, stderr: stderrBuf } : { exitCode: code, stderr: stderrBuf, signal }
|
|
170
|
+
);
|
|
171
171
|
});
|
|
172
172
|
child.on("error", (err) => {
|
|
173
173
|
if (settled) {
|
|
@@ -521,6 +521,12 @@ async function readAndPruneLocked() {
|
|
|
521
521
|
}
|
|
522
522
|
return { sessions: pruned, removed };
|
|
523
523
|
}
|
|
524
|
+
async function readSessionSnapshot() {
|
|
525
|
+
return await withFileLock(stateLockPath(), async () => {
|
|
526
|
+
const raw = await readStateRaw();
|
|
527
|
+
return raw.sessions;
|
|
528
|
+
});
|
|
529
|
+
}
|
|
524
530
|
async function readAndPruneActiveSessions() {
|
|
525
531
|
return await withFileLock(stateLockPath(), readAndPruneLocked);
|
|
526
532
|
}
|
|
@@ -673,7 +679,7 @@ async function removeSession(sessionId) {
|
|
|
673
679
|
}
|
|
674
680
|
|
|
675
681
|
// src/debug-session/constants.ts
|
|
676
|
-
var DEFAULT_TUNNEL_READY_TIMEOUT_MS =
|
|
682
|
+
var DEFAULT_TUNNEL_READY_TIMEOUT_MS = 18e4;
|
|
677
683
|
var POST_USR1_DELAY_MS = 300;
|
|
678
684
|
var PORT_CLEANUP_DELAY_MS = 600;
|
|
679
685
|
var CHILD_SIGTERM_GRACE_MS = 2e3;
|
|
@@ -736,6 +742,19 @@ async function killProcessGroupOrProc(child, timeoutMs = CHILD_SIGTERM_GRACE_MS)
|
|
|
736
742
|
}
|
|
737
743
|
|
|
738
744
|
// src/debug-session/start.ts
|
|
745
|
+
function signalFailureDetail(result) {
|
|
746
|
+
if (result.timedOutAfterMs !== void 0) {
|
|
747
|
+
return `timed out after ${(result.timedOutAfterMs / 1e3).toString()}s`;
|
|
748
|
+
}
|
|
749
|
+
const stderr = result.stderr.trim();
|
|
750
|
+
if (stderr.length > 0) {
|
|
751
|
+
return stderr;
|
|
752
|
+
}
|
|
753
|
+
if (result.signal !== void 0) {
|
|
754
|
+
return `terminated by signal ${result.signal}`;
|
|
755
|
+
}
|
|
756
|
+
return `exit code ${String(result.exitCode)}`;
|
|
757
|
+
}
|
|
739
758
|
function checkAbort(signal) {
|
|
740
759
|
if (signal?.aborted) {
|
|
741
760
|
throw new CfDebuggerError("ABORTED", "Operation aborted by caller");
|
|
@@ -795,10 +814,9 @@ async function signalRemoteNode(options, context, sessionId, emit) {
|
|
|
795
814
|
if (signalResult.exitCode === 0) {
|
|
796
815
|
return;
|
|
797
816
|
}
|
|
798
|
-
const detail = signalResult.stderr.trim().length > 0 ? signalResult.stderr.trim() : `exit code ${String(signalResult.exitCode)}`;
|
|
799
817
|
throw new CfDebuggerError(
|
|
800
818
|
"USR1_SIGNAL_FAILED",
|
|
801
|
-
`Failed to send SIGUSR1 to the Node.js process on ${options.app}: ${
|
|
819
|
+
`Failed to send SIGUSR1 to the Node.js process on ${options.app}: ${signalFailureDetail(signalResult)}`,
|
|
802
820
|
signalResult.stderr
|
|
803
821
|
);
|
|
804
822
|
}
|
|
@@ -825,10 +843,9 @@ async function retryRemoteSignal(options, context, sessionId, emit) {
|
|
|
825
843
|
if (retrySignalResult.exitCode === 0) {
|
|
826
844
|
return;
|
|
827
845
|
}
|
|
828
|
-
const detail = retrySignalResult.stderr.trim().length > 0 ? retrySignalResult.stderr.trim() : `exit code ${String(retrySignalResult.exitCode)}`;
|
|
829
846
|
throw new CfDebuggerError(
|
|
830
847
|
"USR1_SIGNAL_FAILED",
|
|
831
|
-
`Failed to send SIGUSR1 to the Node.js process on ${options.app} after enabling SSH: ${
|
|
848
|
+
`Failed to send SIGUSR1 to the Node.js process on ${options.app} after enabling SSH: ${signalFailureDetail(retrySignalResult)}`,
|
|
832
849
|
retrySignalResult.stderr
|
|
833
850
|
);
|
|
834
851
|
}
|
|
@@ -1019,10 +1036,10 @@ async function stopAllDebuggers() {
|
|
|
1019
1036
|
return stopped;
|
|
1020
1037
|
}
|
|
1021
1038
|
async function listSessions() {
|
|
1022
|
-
return await
|
|
1039
|
+
return await readSessionSnapshot();
|
|
1023
1040
|
}
|
|
1024
1041
|
async function getSession(key) {
|
|
1025
|
-
const sessions = await
|
|
1042
|
+
const sessions = await readSessionSnapshot();
|
|
1026
1043
|
return sessions.find((s) => matchesKey(s, key));
|
|
1027
1044
|
}
|
|
1028
1045
|
export {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/debug-session/start.ts","../src/cloud-foundry/execute.ts","../src/cloud-foundry/commands.ts","../src/cloud-foundry/ssh.ts","../src/paths.ts","../src/network/ports.ts","../src/regions.ts","../src/session-state/store.ts","../src/lock.ts","../src/debug-session/constants.ts","../src/debug-session/orphans.ts","../src/debug-session/processes.ts","../src/debug-session/sessions.ts"],"sourcesContent":["export interface SessionKey {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n}\n\nexport type SessionStatus =\n | \"starting\"\n | \"logging-in\"\n | \"targeting\"\n | \"ssh-enabling\"\n | \"ssh-restarting\"\n | \"signaling\"\n | \"tunneling\"\n | \"ready\"\n | \"stopping\"\n | \"stopped\"\n | \"error\";\n\nexport interface ActiveSession extends SessionKey {\n readonly sessionId: string;\n readonly pid: number;\n readonly hostname: string;\n readonly localPort: number;\n readonly remotePort: number;\n readonly apiEndpoint: string;\n readonly cfHomeDir: string;\n readonly startedAt: string;\n readonly status: SessionStatus;\n readonly message?: string;\n}\n\nexport interface StartDebuggerOptions extends SessionKey {\n readonly email?: string;\n readonly password?: string;\n readonly apiEndpoint?: string;\n readonly preferredPort?: number;\n readonly tunnelReadyTimeoutMs?: number;\n readonly verbose?: boolean;\n readonly onStatus?: (status: SessionStatus, message?: string) => void;\n readonly signal?: AbortSignal;\n}\n\nexport interface DebuggerHandle {\n readonly session: ActiveSession;\n dispose(): Promise<void>;\n waitForExit(): Promise<number | null>;\n}\n\nexport interface StateFile {\n readonly version: \"1\";\n readonly sessions: readonly ActiveSession[];\n}\n\nexport class CfDebuggerError extends Error {\n public readonly code: string;\n public readonly stderr?: string;\n\n public constructor(code: string, message: string, stderr?: string) {\n super(message);\n this.name = \"CfDebuggerError\";\n this.code = code;\n if (stderr !== undefined) {\n this.stderr = stderr;\n }\n }\n}\n","import type { ChildProcess } from \"node:child_process\";\nimport { mkdir, rm } from \"node:fs/promises\";\nimport process from \"node:process\";\n\nimport type { CfExecContext } from \"../cf.js\";\nimport {\n cfEnableSsh,\n cfLogin,\n cfRestartApp,\n cfSshEnabled,\n cfSshOneShot,\n cfTarget,\n isSshDisabledError,\n spawnSshTunnel,\n} from \"../cf.js\";\nimport { sessionCfHomeDir } from \"../paths.js\";\nimport { findListeningProcessId, isPortFree, killProcessOnPort, probeTunnelReady } from \"../port.js\";\nimport { resolveApiEndpoint } from \"../regions.js\";\nimport {\n registerNewSession,\n removeSession,\n sessionKeyString,\n updateSessionPid,\n updateSessionStatus,\n} from \"../state.js\";\nimport type { ActiveSession, DebuggerHandle, SessionStatus, StartDebuggerOptions } from \"../types.js\";\nimport { CfDebuggerError } from \"../types.js\";\n\nimport {\n DEFAULT_TUNNEL_READY_TIMEOUT_MS,\n PORT_CLEANUP_DELAY_MS,\n PORT_RECLAIM_DELAY_MS,\n POST_USR1_DELAY_MS,\n} from \"./constants.js\";\nimport { pruneAndCleanupOrphans } from \"./orphans.js\";\nimport { killProcessGroupOrProc } from \"./processes.js\";\n\ntype StatusEmitter = (status: SessionStatus, message?: string) => void;\n\ninterface TunnelResult {\n readonly child: ChildProcess;\n readonly activePid: number;\n}\n\nfunction checkAbort(signal: AbortSignal | undefined): void {\n if (signal?.aborted) {\n throw new CfDebuggerError(\"ABORTED\", \"Operation aborted by caller\");\n }\n}\n\nfunction requireCredentials(options: StartDebuggerOptions): {\n readonly email: string;\n readonly password: string;\n} {\n const email = options.email ?? process.env[\"SAP_EMAIL\"];\n const password = options.password ?? process.env[\"SAP_PASSWORD\"];\n if (email === undefined || email === \"\") {\n throw new CfDebuggerError(\n \"MISSING_CREDENTIALS\",\n \"SAP email is required. Pass `email` or set SAP_EMAIL env var.\",\n );\n }\n if (password === undefined || password === \"\") {\n throw new CfDebuggerError(\n \"MISSING_CREDENTIALS\",\n \"SAP password is required. Pass `password` or set SAP_PASSWORD env var.\",\n );\n }\n return { email, password };\n}\n\nasync function registerSession(\n options: StartDebuggerOptions,\n apiEndpoint: string,\n): Promise<ActiveSession> {\n const registration = await registerNewSession({\n region: options.region,\n org: options.org,\n space: options.space,\n app: options.app,\n apiEndpoint,\n ...(options.preferredPort === undefined ? {} : { preferredPort: options.preferredPort }),\n portProbe: isPortFree,\n cfHomeForSession: sessionCfHomeDir,\n });\n\n if (registration.existing) {\n throw new CfDebuggerError(\n \"SESSION_ALREADY_RUNNING\",\n `A debugger session is already running for ${sessionKeyString(options)} ` +\n `on port ${registration.existing.localPort.toString()} ` +\n `(pid ${registration.existing.pid.toString()}, sessionId ${registration.existing.sessionId}). ` +\n `Stop it first with \\`cf-debugger stop\\`.`,\n );\n }\n return registration.session;\n}\n\nasync function loginAndTarget(\n options: StartDebuggerOptions,\n apiEndpoint: string,\n email: string,\n password: string,\n context: CfExecContext,\n sessionId: string,\n emit: StatusEmitter,\n): Promise<void> {\n emit(\"logging-in\");\n await updateSessionStatus(sessionId, \"logging-in\");\n await cfLogin(apiEndpoint, email, password, context);\n checkAbort(options.signal);\n\n emit(\"targeting\");\n await updateSessionStatus(sessionId, \"targeting\");\n await cfTarget(options.org, options.space, context);\n checkAbort(options.signal);\n}\n\nasync function signalRemoteNode(\n options: StartDebuggerOptions,\n context: CfExecContext,\n sessionId: string,\n emit: StatusEmitter,\n): Promise<void> {\n emit(\"signaling\");\n await updateSessionStatus(sessionId, \"signaling\");\n const signalResult = await cfSshOneShot(options.app, `kill -s USR1 $(pidof node)`, context);\n\n if (!isSshDisabledError(signalResult.stderr)) {\n if (signalResult.exitCode === 0) {\n return;\n }\n const detail = signalResult.stderr.trim().length > 0\n ? signalResult.stderr.trim()\n : `exit code ${String(signalResult.exitCode)}`;\n throw new CfDebuggerError(\n \"USR1_SIGNAL_FAILED\",\n `Failed to send SIGUSR1 to the Node.js process on ${options.app}: ${detail}`,\n signalResult.stderr,\n );\n }\n\n const alreadyEnabled = await cfSshEnabled(options.app, context);\n if (!alreadyEnabled) {\n emit(\"ssh-enabling\", \"Enabling SSH on the app\");\n await updateSessionStatus(sessionId, \"ssh-enabling\");\n await cfEnableSsh(options.app, context);\n }\n emit(\"ssh-restarting\", \"Restarting app so SSH becomes active\");\n await updateSessionStatus(sessionId, \"ssh-restarting\");\n await cfRestartApp(options.app, context);\n checkAbort(options.signal);\n\n await retryRemoteSignal(options, context, sessionId, emit);\n}\n\nasync function retryRemoteSignal(\n options: StartDebuggerOptions,\n context: CfExecContext,\n sessionId: string,\n emit: StatusEmitter,\n): Promise<void> {\n emit(\"signaling\");\n await updateSessionStatus(sessionId, \"signaling\");\n const retrySignalResult = await cfSshOneShot(\n options.app,\n `kill -s USR1 $(pidof node)`,\n context,\n );\n if (retrySignalResult.exitCode === 0) {\n return;\n }\n const detail = retrySignalResult.stderr.trim().length > 0\n ? retrySignalResult.stderr.trim()\n : `exit code ${String(retrySignalResult.exitCode)}`;\n throw new CfDebuggerError(\n \"USR1_SIGNAL_FAILED\",\n `Failed to send SIGUSR1 to the Node.js process on ${options.app} after enabling SSH: ${detail}`,\n retrySignalResult.stderr,\n );\n}\n\nasync function waitAfterSignal(signal: AbortSignal | undefined): Promise<void> {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, POST_USR1_DELAY_MS);\n });\n checkAbort(signal);\n}\n\nasync function ensurePortAvailable(localPort: number): Promise<void> {\n if (await isPortFree(localPort)) {\n return;\n }\n await killProcessOnPort(localPort);\n await new Promise<void>((resolve) => {\n setTimeout(resolve, PORT_RECLAIM_DELAY_MS);\n });\n if (!(await isPortFree(localPort))) {\n throw new CfDebuggerError(\n \"PORT_UNAVAILABLE\",\n `Local port ${localPort.toString()} is in use and could not be reclaimed for the tunnel.`,\n );\n }\n}\n\nasync function openReadyTunnel(\n options: StartDebuggerOptions,\n session: ActiveSession,\n context: CfExecContext,\n tunnelReadyTimeoutMs: number,\n onChild: (child: ChildProcess) => void,\n): Promise<TunnelResult> {\n await ensurePortAvailable(session.localPort);\n const child = spawnSshTunnel(options.app, session.localPort, session.remotePort, context);\n onChild(child);\n if (child.pid !== undefined) {\n await updateSessionPid(session.sessionId, child.pid);\n }\n\n const ready = await probeTunnelReady(session.localPort, tunnelReadyTimeoutMs);\n checkAbort(options.signal);\n if (!ready) {\n throw new CfDebuggerError(\n \"TUNNEL_NOT_READY\",\n `SSH tunnel on port ${session.localPort.toString()} did not become ready within ` +\n `${Math.round(tunnelReadyTimeoutMs / 1000).toString()}s.`,\n );\n }\n\n const listeningPid = await findListeningProcessId(session.localPort);\n const activePid = listeningPid ?? child.pid ?? session.pid;\n if (activePid !== session.pid) {\n await updateSessionPid(session.sessionId, activePid);\n }\n return { child, activePid };\n}\n\nfunction attachTunnelEvents(\n child: ChildProcess,\n markClosed: () => void,\n resolveExit: (code: number | null) => void,\n emit: StatusEmitter,\n): void {\n child.on(\"close\", (code) => {\n markClosed();\n resolveExit(code);\n });\n\n child.on(\"error\", (err: Error) => {\n emit(\"error\", err.message);\n });\n}\n\nfunction createHandle(\n session: ActiveSession,\n emit: StatusEmitter,\n finalize: () => Promise<void>,\n exitPromise: Promise<number | null>,\n): DebuggerHandle {\n let disposePromise: Promise<void> | undefined;\n return {\n session,\n dispose: async (): Promise<void> => {\n disposePromise ??= (async (): Promise<void> => {\n emit(\"stopping\");\n await updateSessionStatus(session.sessionId, \"stopping\");\n await finalize();\n })();\n await disposePromise;\n },\n waitForExit: async (): Promise<number | null> => {\n return await exitPromise;\n },\n };\n}\n\nexport async function startDebugger(options: StartDebuggerOptions): Promise<DebuggerHandle> {\n const { email, password } = requireCredentials(options);\n const apiEndpoint = resolveApiEndpoint(options.region, options.apiEndpoint);\n const tunnelReadyTimeoutMs = options.tunnelReadyTimeoutMs ?? DEFAULT_TUNNEL_READY_TIMEOUT_MS;\n const emit = (status: SessionStatus, message?: string): void => {\n options.onStatus?.(status, message);\n };\n\n checkAbort(options.signal);\n await pruneAndCleanupOrphans();\n\n const session = await registerSession(options, apiEndpoint);\n const context: CfExecContext = { cfHome: session.cfHomeDir };\n let child: ChildProcess | undefined;\n let tunnelClosed = false;\n let exitResolve: (code: number | null) => void = (_code) => {\n throw new Error(\"Exit resolver was used before initialization\");\n };\n const exitPromise = new Promise<number | null>((resolve) => {\n exitResolve = resolve;\n });\n\n const finalize = async (): Promise<void> => {\n if (!tunnelClosed) {\n tunnelClosed = true;\n if (child) {\n await killProcessGroupOrProc(child);\n }\n setTimeout(() => {\n void killProcessOnPort(session.localPort);\n }, PORT_CLEANUP_DELAY_MS);\n }\n await removeSession(session.sessionId);\n await cleanupFilesystem(session.cfHomeDir);\n emit(\"stopped\");\n };\n\n try {\n await mkdir(session.cfHomeDir, { recursive: true });\n await loginAndTarget(options, apiEndpoint, email, password, context, session.sessionId, emit);\n await killProcessOnPort(session.localPort);\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 200);\n });\n await signalRemoteNode(options, context, session.sessionId, emit);\n await waitAfterSignal(options.signal);\n\n emit(\"tunneling\");\n await updateSessionStatus(session.sessionId, \"tunneling\");\n const tunnel = await openReadyTunnel(\n options,\n session,\n context,\n tunnelReadyTimeoutMs,\n (tunnelChild) => {\n child = tunnelChild;\n attachTunnelEvents(tunnelChild, () => {\n tunnelClosed = true;\n }, exitResolve, emit);\n },\n );\n child = tunnel.child;\n\n emit(\"ready\");\n const readySession = await updateSessionStatus(session.sessionId, \"ready\");\n const activeSession = readySession ?? { ...session, pid: tunnel.activePid, status: \"ready\" };\n return createHandle(activeSession, emit, finalize, exitPromise);\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n emit(\"error\", message);\n await finalize();\n throw err;\n }\n}\n\nasync function cleanupFilesystem(cfHomeDir: string): Promise<void> {\n try {\n await rm(cfHomeDir, { recursive: true, force: true });\n } catch {\n // best-effort\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport { CfDebuggerError } from \"../types.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst MAX_BUFFER = 16 * 1024 * 1024;\nconst CF_CLI_TIMEOUT_MS = 30_000;\nconst REDACTED_ARG = \"<redacted>\";\n\nexport interface CfExecContext {\n readonly cfHome: string;\n readonly command?: string;\n}\n\nexport function buildEnv(cfHome: string): NodeJS.ProcessEnv {\n return { ...process.env, CF_HOME: cfHome };\n}\n\nexport function resolveBin(context: CfExecContext): string {\n return context.command ?? process.env[\"CF_DEBUGGER_CF_BIN\"] ?? \"cf\";\n}\n\nfunction sensitiveArgs(args: readonly string[]): readonly string[] {\n if (args[0] !== \"auth\") {\n return [];\n }\n return args.slice(1).filter((arg) => arg.length > 0);\n}\n\nfunction redactText(text: string, values: readonly string[]): string {\n return values.reduce((current, value) => current.split(value).join(REDACTED_ARG), text);\n}\n\nfunction formatArgsForError(args: readonly string[]): string {\n if (args[0] !== \"auth\") {\n return args.join(\" \");\n }\n return args.map((arg, index) => (index === 0 ? arg : REDACTED_ARG)).join(\" \");\n}\n\nexport async function runCf(\n args: readonly string[],\n context: CfExecContext,\n timeoutMs: number = CF_CLI_TIMEOUT_MS,\n): Promise<string> {\n try {\n const { stdout } = await execFileAsync(resolveBin(context), [...args], {\n env: buildEnv(context.cfHome),\n maxBuffer: MAX_BUFFER,\n timeout: timeoutMs,\n });\n return stdout;\n } catch (err: unknown) {\n const e = err as NodeJS.ErrnoException & { stderr?: string; stdout?: string };\n const redactionValues = sensitiveArgs(args);\n const stderr = redactText(e.stderr?.trim() ?? \"\", redactionValues);\n const fallbackMessage = redactText(e.message, redactionValues);\n throw new CfDebuggerError(\n \"CF_CLI_FAILED\",\n `cf ${formatArgsForError(args)} failed: ${stderr.length > 0 ? stderr : fallbackMessage}`,\n stderr,\n );\n }\n}\n","import { CfDebuggerError } from \"../types.js\";\n\nimport { type CfExecContext, runCf } from \"./execute.js\";\nimport { parseAppNames, parseNameTable } from \"./parsers.js\";\n\nconst CF_RESTART_TIMEOUT_MS = 120_000;\nconst CF_AUTH_MAX_ATTEMPTS = 3;\n\nexport async function cfApi(apiEndpoint: string, context: CfExecContext): Promise<void> {\n await runCf([\"api\", apiEndpoint], context);\n}\n\nexport async function cfAuth(\n email: string,\n password: string,\n context: CfExecContext,\n): Promise<void> {\n let lastError: unknown;\n for (let attempt = 0; attempt < CF_AUTH_MAX_ATTEMPTS; attempt++) {\n try {\n await runCf([\"auth\", email, password], context);\n return;\n } catch (err: unknown) {\n lastError = err;\n if (attempt < CF_AUTH_MAX_ATTEMPTS - 1) {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 1000 * (attempt + 1));\n });\n }\n }\n }\n if (lastError instanceof Error) {\n throw lastError;\n }\n throw new CfDebuggerError(\"CF_AUTH_FAILED\", `cf auth failed: ${String(lastError)}`);\n}\n\nexport async function cfLogin(\n apiEndpoint: string,\n email: string,\n password: string,\n context: CfExecContext,\n): Promise<void> {\n try {\n await cfApi(apiEndpoint, context);\n await cfAuth(email, password, context);\n } catch (err: unknown) {\n if (err instanceof CfDebuggerError) {\n throw new CfDebuggerError(\"CF_LOGIN_FAILED\", err.message, err.stderr);\n }\n throw err;\n }\n}\n\nexport async function cfTarget(\n org: string,\n space: string,\n context: CfExecContext,\n): Promise<void> {\n try {\n await runCf([\"target\", \"-o\", org, \"-s\", space], context);\n } catch (err: unknown) {\n if (err instanceof CfDebuggerError) {\n throw new CfDebuggerError(\"CF_TARGET_FAILED\", err.message, err.stderr);\n }\n throw err;\n }\n}\n\nexport async function cfAppExists(appName: string, context: CfExecContext): Promise<boolean> {\n try {\n await runCf([\"app\", appName], context);\n return true;\n } catch (err: unknown) {\n const stderr = (err as CfDebuggerError).stderr ?? \"\";\n if (stderr.toLowerCase().includes(\"not found\")) {\n return false;\n }\n throw err;\n }\n}\n\nexport async function cfSshEnabled(appName: string, context: CfExecContext): Promise<boolean> {\n try {\n const stdout = await runCf([\"ssh-enabled\", appName], context);\n return stdout.toLowerCase().includes(\"ssh support is enabled\");\n } catch {\n return false;\n }\n}\n\nexport async function cfEnableSsh(appName: string, context: CfExecContext): Promise<void> {\n try {\n await runCf([\"enable-ssh\", appName], context);\n } catch (err: unknown) {\n if (err instanceof CfDebuggerError) {\n throw new CfDebuggerError(\"SSH_NOT_ENABLED\", err.message, err.stderr);\n }\n throw err;\n }\n}\n\nexport async function cfRestartApp(appName: string, context: CfExecContext): Promise<void> {\n await runCf([\"restart\", appName], context, CF_RESTART_TIMEOUT_MS);\n}\n\nexport async function cfApps(context: CfExecContext): Promise<readonly string[]> {\n const stdout = await runCf([\"apps\"], context);\n return parseAppNames(stdout);\n}\n\nexport async function cfOrgs(context: CfExecContext): Promise<readonly string[]> {\n const stdout = await runCf([\"orgs\"], context);\n return parseNameTable(stdout);\n}\n\nexport async function cfSpaces(context: CfExecContext): Promise<readonly string[]> {\n const stdout = await runCf([\"spaces\"], context);\n return parseNameTable(stdout);\n}\n","import { spawn } from \"node:child_process\";\n\nimport { buildEnv, resolveBin, type CfExecContext } from \"./execute.js\";\n\nconst CF_SSH_SIGNAL_TIMEOUT_MS = 15_000;\n\nexport interface CfSshSignalResult {\n readonly exitCode: number | null;\n readonly stderr: string;\n}\n\nexport async function cfSshOneShot(\n appName: string,\n command: string,\n context: CfExecContext,\n): Promise<CfSshSignalResult> {\n return await new Promise<CfSshSignalResult>((resolve) => {\n const child = spawn(resolveBin(context), [\"ssh\", appName, \"-c\", command], {\n env: buildEnv(context.cfHome),\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n let stderrBuf = \"\";\n let settled = false;\n\n const timeout = setTimeout(() => {\n if (settled) {\n return;\n }\n settled = true;\n try {\n child.kill();\n } catch {\n // already gone\n }\n resolve({ exitCode: null, stderr: stderrBuf });\n }, CF_SSH_SIGNAL_TIMEOUT_MS);\n\n child.stderr.on(\"data\", (data: Buffer | string) => {\n stderrBuf += data.toString();\n });\n\n child.on(\"close\", (code) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timeout);\n resolve({ exitCode: code, stderr: stderrBuf });\n });\n\n child.on(\"error\", (err: Error) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timeout);\n resolve({ exitCode: null, stderr: err.message });\n });\n });\n}\n\nexport function isSshDisabledError(stderr: string): boolean {\n const lower = stderr.toLowerCase();\n return lower.includes(\"not authorized\") || lower.includes(\"ssh support is disabled\");\n}\n\nexport function spawnSshTunnel(\n appName: string,\n localPort: number,\n remotePort: number,\n context: CfExecContext,\n): ReturnType<typeof spawn> {\n const tunnelArg = `${localPort.toString()}:localhost:${remotePort.toString()}`;\n const isWindows = process.platform === \"win32\";\n return spawn(resolveBin(context), [\"ssh\", appName, \"-N\", \"-L\", tunnelArg], {\n env: buildEnv(context.cfHome),\n shell: isWindows,\n detached: !isWindows,\n });\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const SAPTOOLS_DIR_NAME = \".saptools\";\nexport const CF_DEBUGGER_STATE_FILENAME = \"cf-debugger-state.json\";\nexport const CF_DEBUGGER_LOCK_FILENAME = \"cf-debugger-state.lock\";\nexport const CF_DEBUGGER_HOMES_DIRNAME = \"cf-debugger-homes\";\n\nexport function saptoolsDir(): string {\n return join(homedir(), SAPTOOLS_DIR_NAME);\n}\n\nexport function stateFilePath(): string {\n return join(saptoolsDir(), CF_DEBUGGER_STATE_FILENAME);\n}\n\nexport function stateLockPath(): string {\n return join(saptoolsDir(), CF_DEBUGGER_LOCK_FILENAME);\n}\n\nexport function sessionCfHomeDir(sessionId: string): string {\n return join(saptoolsDir(), CF_DEBUGGER_HOMES_DIRNAME, sessionId);\n}\n","import { execFile } from \"node:child_process\";\nimport { createConnection, createServer } from \"node:net\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nasync function findListeningPidsWithNetstat(port: number): Promise<readonly number[]> {\n try {\n const { stdout } = await execFileAsync(\"netstat\", [\"-ano\"]);\n const pids = new Set<number>();\n for (const line of stdout.split(\"\\n\")) {\n if (!line.includes(`:${port.toString()}`) || !line.includes(\"LISTENING\")) {\n continue;\n }\n const parts = line.trim().split(/\\s+/);\n const last = parts[parts.length - 1];\n if (last === undefined) {\n continue;\n }\n const pid = Number.parseInt(last, 10);\n if (!Number.isNaN(pid)) {\n pids.add(pid);\n }\n }\n return [...pids];\n } catch {\n return [];\n }\n}\n\nasync function findListeningPidsWithLsof(port: number): Promise<readonly number[]> {\n try {\n const { stdout } = await execFileAsync(\"lsof\", [\"-nP\", \"-t\", \"-i\", `tcp:${port.toString()}`, \"-sTCP:LISTEN\"]);\n return stdout\n .trim()\n .split(\"\\n\")\n .filter((line) => line.length > 0)\n .map((line) => Number.parseInt(line, 10))\n .filter((pid) => !Number.isNaN(pid));\n } catch {\n return [];\n }\n}\n\nasync function findListeningPids(port: number): Promise<readonly number[]> {\n if (process.platform === \"win32\") {\n return await findListeningPidsWithNetstat(port);\n }\n return await findListeningPidsWithLsof(port);\n}\n\nexport async function isPortFree(port: number): Promise<boolean> {\n return await new Promise<boolean>((resolve) => {\n const server = createServer();\n server.once(\"error\", () => {\n resolve(false);\n });\n server.once(\"listening\", () => {\n server.close(() => {\n resolve(true);\n });\n });\n server.listen(port, \"127.0.0.1\");\n });\n}\n\nexport async function probeTunnelReady(port: number, timeoutMs: number): Promise<boolean> {\n const pollIntervalMs = 250;\n const started = Date.now();\n\n while (Date.now() - started < timeoutMs) {\n const connected = await new Promise<boolean>((resolve) => {\n const socket = createConnection({ port, host: \"127.0.0.1\" });\n socket.setTimeout(200);\n socket.once(\"connect\", () => {\n socket.destroy();\n resolve(true);\n });\n socket.once(\"error\", () => {\n socket.destroy();\n resolve(false);\n });\n socket.once(\"timeout\", () => {\n socket.destroy();\n resolve(false);\n });\n });\n\n if (connected) {\n return true;\n }\n\n await new Promise<void>((resolve) => {\n setTimeout(resolve, pollIntervalMs);\n });\n }\n\n return false;\n}\n\nexport async function findListeningProcessId(port: number): Promise<number | undefined> {\n const pids = await findListeningPids(port);\n return pids[0];\n}\n\nexport async function killProcessOnPort(port: number): Promise<void> {\n const portStr = port.toString();\n if (process.platform === \"win32\") {\n try {\n const pids = await findListeningPidsWithNetstat(port);\n for (const pid of pids) {\n try {\n // cspell:ignore taskkill\n await execFileAsync(\"taskkill\", [\"/F\", \"/PID\", pid.toString()]);\n } catch {\n // ignore\n }\n }\n } catch {\n // ignore\n }\n return;\n }\n\n try {\n const { stdout } = await execFileAsync(\"lsof\", [\"-t\", \"-i\", `tcp:${portStr}`]);\n const lines = stdout.trim().split(\"\\n\").filter((line) => line.length > 0);\n for (const line of lines) {\n const pid = Number.parseInt(line, 10);\n if (Number.isNaN(pid)) {\n continue;\n }\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {\n // already dead\n }\n }\n } catch {\n // lsof missing or no match — ignore\n }\n}\n","export interface RegionInfo {\n readonly key: string;\n readonly apiEndpoint: string;\n}\n\nconst REGION_API_ENDPOINTS: Readonly<Record<string, string>> = {\n ae01: \"https://api.cf.ae01.hana.ondemand.com\",\n ap01: \"https://api.cf.ap01.hana.ondemand.com\",\n ap10: \"https://api.cf.ap10.hana.ondemand.com\",\n ap11: \"https://api.cf.ap11.hana.ondemand.com\",\n ap12: \"https://api.cf.ap12.hana.ondemand.com\",\n ap20: \"https://api.cf.ap20.hana.ondemand.com\",\n ap21: \"https://api.cf.ap21.hana.ondemand.com\",\n ap30: \"https://api.cf.ap30.hana.ondemand.com\",\n br10: \"https://api.cf.br10.hana.ondemand.com\",\n br20: \"https://api.cf.br20.hana.ondemand.com\",\n br30: \"https://api.cf.br30.hana.ondemand.com\",\n ca10: \"https://api.cf.ca10.hana.ondemand.com\",\n ca20: \"https://api.cf.ca20.hana.ondemand.com\",\n ch20: \"https://api.cf.ch20.hana.ondemand.com\",\n eu10: \"https://api.cf.eu10.hana.ondemand.com\",\n eu11: \"https://api.cf.eu11.hana.ondemand.com\",\n eu12: \"https://api.cf.eu12.hana.ondemand.com\",\n eu20: \"https://api.cf.eu20.hana.ondemand.com\",\n eu21: \"https://api.cf.eu21.hana.ondemand.com\",\n eu30: \"https://api.cf.eu30.hana.ondemand.com\",\n eu31: \"https://api.cf.eu31.hana.ondemand.com\",\n in30: \"https://api.cf.in30.hana.ondemand.com\",\n jp10: \"https://api.cf.jp10.hana.ondemand.com\",\n jp20: \"https://api.cf.jp20.hana.ondemand.com\",\n jp30: \"https://api.cf.jp30.hana.ondemand.com\",\n kr30: \"https://api.cf.kr30.hana.ondemand.com\",\n us10: \"https://api.cf.us10.hana.ondemand.com\",\n us11: \"https://api.cf.us11.hana.ondemand.com\",\n us20: \"https://api.cf.us20.hana.ondemand.com\",\n us21: \"https://api.cf.us21.hana.ondemand.com\",\n us30: \"https://api.cf.us30.hana.ondemand.com\",\n us31: \"https://api.cf.us31.hana.ondemand.com\",\n};\n\nexport function resolveApiEndpoint(regionKey: string, override?: string): string {\n if (override !== undefined && override !== \"\") {\n return override;\n }\n const endpoint = REGION_API_ENDPOINTS[regionKey];\n if (endpoint === undefined) {\n throw new Error(\n `Unknown region key: ${regionKey}. Pass \\`apiEndpoint\\` explicitly to override.`,\n );\n }\n return endpoint;\n}\n\nexport function listKnownRegionKeys(): readonly string[] {\n return Object.keys(REGION_API_ENDPOINTS);\n}\n","import { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { hostname as getHostname } from \"node:os\";\nimport { dirname } from \"node:path\";\nimport process from \"node:process\";\n\nimport { withFileLock } from \"../lock.js\";\nimport { stateFilePath, stateLockPath } from \"../paths.js\";\nimport { CfDebuggerError } from \"../types.js\";\nimport type { ActiveSession, SessionKey, StateFile } from \"../types.js\";\n\nasync function readJsonFile<T>(path: string): Promise<T | undefined> {\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") {\n return undefined;\n }\n throw err;\n }\n try {\n return JSON.parse(raw) as T;\n } catch {\n process.stderr.write(\n `[cf-debugger] warning: state file at ${path} is not valid JSON; resetting to empty.\\n`,\n );\n return undefined;\n }\n}\n\nasync function writeJsonFileAtomic(path: string, value: unknown): Promise<void> {\n const tempPath = `${path}.${randomUUID()}.tmp`;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(tempPath, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n await rename(tempPath, path);\n}\n\nfunction emptyState(): StateFile {\n return { version: \"1\", sessions: [] };\n}\n\nfunction isValidState(value: unknown): value is StateFile {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const candidate = value as Partial<StateFile>;\n return candidate.version === \"1\" && Array.isArray(candidate.sessions);\n}\n\nexport function isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ESRCH\") {\n return false;\n }\n return true;\n }\n}\n\nfunction filterStaleSessions(sessions: readonly ActiveSession[]): readonly ActiveSession[] {\n const host = getHostname();\n return sessions.filter((session) => {\n if (session.hostname !== host) {\n return true;\n }\n return isPidAlive(session.pid);\n });\n}\n\nasync function readStateRaw(): Promise<StateFile> {\n const parsed = await readJsonFile<unknown>(stateFilePath());\n if (!isValidState(parsed)) {\n return emptyState();\n }\n return parsed;\n}\n\nasync function writeState(state: StateFile): Promise<void> {\n await writeJsonFileAtomic(stateFilePath(), state);\n}\n\nexport interface StateReaderResult {\n readonly sessions: readonly ActiveSession[];\n readonly removed: readonly ActiveSession[];\n}\n\nasync function readAndPruneLocked(): Promise<StateReaderResult> {\n const raw = await readStateRaw();\n const pruned = filterStaleSessions(raw.sessions);\n const removed = raw.sessions.filter(\n (session) => !pruned.some((active) => active.sessionId === session.sessionId),\n );\n\n if (removed.length > 0) {\n await writeState({ version: \"1\", sessions: pruned });\n }\n\n return { sessions: pruned, removed };\n}\n\nexport async function readActiveSessions(): Promise<readonly ActiveSession[]> {\n const result = await withFileLock(stateLockPath(), readAndPruneLocked);\n return result.sessions;\n}\n\nexport async function readAndPruneActiveSessions(): Promise<StateReaderResult> {\n return await withFileLock(stateLockPath(), readAndPruneLocked);\n}\n\nexport function sessionKeyString(key: SessionKey): string {\n return `${key.region}:${key.org}:${key.space}:${key.app}`;\n}\n\nexport function matchesKey(session: SessionKey, key: SessionKey): boolean {\n return (\n session.region === key.region &&\n session.org === key.org &&\n session.space === key.space &&\n session.app === key.app\n );\n}\n\nexport interface RegisterSessionResult {\n readonly session: ActiveSession;\n readonly existing?: ActiveSession;\n}\n\nexport interface RegisterSessionInput extends SessionKey {\n readonly apiEndpoint: string;\n readonly preferredPort?: number;\n readonly portProbe: (port: number) => Promise<boolean>;\n readonly sessionIdFactory?: () => string;\n readonly cfHomeForSession: (sessionId: string) => string;\n readonly basePort?: number;\n readonly maxPort?: number;\n}\n\nconst DEFAULT_BASE_PORT = 20_000;\nconst DEFAULT_MAX_PORT = 20_999;\n\nasync function pickPort(\n preferred: number | undefined,\n reserved: ReadonlySet<number>,\n probe: (port: number) => Promise<boolean>,\n basePort: number,\n maxPort: number,\n): Promise<number> {\n const tryOrder: number[] = [];\n if (preferred !== undefined) {\n tryOrder.push(preferred);\n }\n for (let port = basePort; port <= maxPort; port++) {\n if (port !== preferred) {\n tryOrder.push(port);\n }\n }\n\n for (const port of tryOrder) {\n if (reserved.has(port)) {\n continue;\n }\n const free = await probe(port);\n if (free) {\n return port;\n }\n }\n throw new CfDebuggerError(\n \"PORT_UNAVAILABLE\",\n `No free local port available in range ${basePort.toString()}–${maxPort.toString()}`,\n );\n}\n\nexport async function registerNewSession(\n input: RegisterSessionInput,\n): Promise<RegisterSessionResult> {\n return await withFileLock(stateLockPath(), async (): Promise<RegisterSessionResult> => {\n const pruneResult = await readAndPruneLocked();\n const existing = pruneResult.sessions.find((session) => matchesKey(session, input));\n if (existing) {\n return { session: existing, existing };\n }\n\n const reservedPorts = new Set(pruneResult.sessions.map((session) => session.localPort));\n const localPort = await pickPort(\n input.preferredPort,\n reservedPorts,\n input.portProbe,\n input.basePort ?? DEFAULT_BASE_PORT,\n input.maxPort ?? DEFAULT_MAX_PORT,\n );\n\n const sessionId = (input.sessionIdFactory ?? randomUUID)();\n const cfHomeDir = input.cfHomeForSession(sessionId);\n\n const session: ActiveSession = {\n sessionId,\n pid: process.pid,\n hostname: getHostname(),\n region: input.region,\n org: input.org,\n space: input.space,\n app: input.app,\n apiEndpoint: input.apiEndpoint,\n localPort,\n remotePort: 9229,\n cfHomeDir,\n startedAt: new Date().toISOString(),\n status: \"starting\",\n };\n\n const nextSessions: readonly ActiveSession[] = [...pruneResult.sessions, session];\n await writeState({ version: \"1\", sessions: nextSessions });\n\n return { session };\n });\n}\n\nexport async function updateSessionStatus(\n sessionId: string,\n status: ActiveSession[\"status\"],\n message?: string,\n): Promise<ActiveSession | undefined> {\n return await withFileLock(stateLockPath(), async (): Promise<ActiveSession | undefined> => {\n const raw = await readStateRaw();\n let updated: ActiveSession | undefined;\n const nextSessions = raw.sessions.map((session): ActiveSession => {\n if (session.sessionId !== sessionId) {\n return session;\n }\n const base: ActiveSession = {\n sessionId: session.sessionId,\n pid: session.pid,\n hostname: session.hostname,\n region: session.region,\n org: session.org,\n space: session.space,\n app: session.app,\n apiEndpoint: session.apiEndpoint,\n localPort: session.localPort,\n remotePort: session.remotePort,\n cfHomeDir: session.cfHomeDir,\n startedAt: session.startedAt,\n status,\n };\n const next: ActiveSession = message === undefined ? base : { ...base, message };\n updated = next;\n return next;\n });\n\n if (updated) {\n await writeState({ version: \"1\", sessions: nextSessions });\n }\n return updated;\n });\n}\n\nexport async function updateSessionPid(\n sessionId: string,\n pid: number,\n): Promise<ActiveSession | undefined> {\n return await withFileLock(stateLockPath(), async (): Promise<ActiveSession | undefined> => {\n const raw = await readStateRaw();\n let updated: ActiveSession | undefined;\n const nextSessions = raw.sessions.map((session): ActiveSession => {\n if (session.sessionId !== sessionId) {\n return session;\n }\n const next: ActiveSession = {\n sessionId: session.sessionId,\n pid,\n hostname: session.hostname,\n region: session.region,\n org: session.org,\n space: session.space,\n app: session.app,\n apiEndpoint: session.apiEndpoint,\n localPort: session.localPort,\n remotePort: session.remotePort,\n cfHomeDir: session.cfHomeDir,\n startedAt: session.startedAt,\n status: session.status,\n ...(session.message === undefined ? {} : { message: session.message }),\n };\n updated = next;\n return next;\n });\n\n if (updated !== undefined) {\n await writeState({ version: \"1\", sessions: nextSessions });\n }\n return updated;\n });\n}\n\nexport async function removeSession(sessionId: string): Promise<ActiveSession | undefined> {\n return await withFileLock(stateLockPath(), async (): Promise<ActiveSession | undefined> => {\n const raw = await readStateRaw();\n const target = raw.sessions.find((session) => session.sessionId === sessionId);\n if (!target) {\n return undefined;\n }\n const remaining = raw.sessions.filter((session) => session.sessionId !== sessionId);\n await writeState({ version: \"1\", sessions: remaining });\n return target;\n });\n}\n","import { mkdir, open, unlink } from \"node:fs/promises\";\nimport type { FileHandle } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nconst DEFAULT_POLL_MS = 50;\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nasync function acquireFileLock(\n lockPath: string,\n timeoutMs: number,\n pollMs: number,\n): Promise<FileHandle> {\n const deadline = Date.now() + timeoutMs;\n await mkdir(dirname(lockPath), { recursive: true });\n\n for (;;) {\n try {\n return await open(lockPath, \"wx\");\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"EEXIST\") {\n throw err;\n }\n }\n\n if (Date.now() > deadline) {\n throw new Error(`Timed out acquiring file lock at ${lockPath}`);\n }\n\n await sleep(pollMs);\n }\n}\n\nasync function releaseFileLock(lockPath: string, handle: FileHandle): Promise<void> {\n await handle.close();\n await unlink(lockPath).catch((err: unknown) => {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"ENOENT\") {\n throw err;\n }\n });\n}\n\nexport interface WithLockOptions {\n readonly timeoutMs?: number;\n readonly pollMs?: number;\n}\n\nexport async function withFileLock<T>(\n lockPath: string,\n work: () => Promise<T>,\n options?: WithLockOptions,\n): Promise<T> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const pollMs = options?.pollMs ?? DEFAULT_POLL_MS;\n const handle = await acquireFileLock(lockPath, timeoutMs, pollMs);\n try {\n return await work();\n } finally {\n await releaseFileLock(lockPath, handle);\n }\n}\n","export const DEFAULT_TUNNEL_READY_TIMEOUT_MS = 30_000;\nexport const POST_USR1_DELAY_MS = 300;\nexport const PORT_CLEANUP_DELAY_MS = 600;\nexport const CHILD_SIGTERM_GRACE_MS = 2_000;\nexport const PORT_RECLAIM_DELAY_MS = 250;\nexport const PID_TERMINATION_POLL_MS = 100;\n","import { hostname as getHostname } from \"node:os\";\n\nimport { killProcessOnPort } from \"../port.js\";\nimport { readAndPruneActiveSessions } from \"../state.js\";\nimport type { ActiveSession } from \"../types.js\";\n\nexport async function pruneAndCleanupOrphans(): Promise<readonly ActiveSession[]> {\n const result = await readAndPruneActiveSessions();\n const host = getHostname();\n for (const removed of result.removed) {\n if (removed.hostname === host) {\n void killProcessOnPort(removed.localPort);\n }\n }\n return result.sessions;\n}\n","import type { ChildProcess } from \"node:child_process\";\nimport process from \"node:process\";\n\nimport { isPidAlive } from \"../state.js\";\n\nimport { CHILD_SIGTERM_GRACE_MS, PID_TERMINATION_POLL_MS } from \"./constants.js\";\n\nfunction signalPidOrGroup(pid: number, signal: NodeJS.Signals): void {\n const isWindows = process.platform === \"win32\";\n if (!isWindows) {\n try {\n process.kill(-pid, signal);\n return;\n } catch {\n // fall through to direct pid signal\n }\n }\n try {\n process.kill(pid, signal);\n } catch {\n // already gone\n }\n}\n\nexport async function terminatePidOrGroup(\n pid: number,\n timeoutMs: number = CHILD_SIGTERM_GRACE_MS,\n): Promise<void> {\n if (!isPidAlive(pid)) {\n return;\n }\n\n signalPidOrGroup(pid, \"SIGTERM\");\n const startedAt = Date.now();\n while (Date.now() - startedAt < timeoutMs) {\n if (!isPidAlive(pid)) {\n return;\n }\n await new Promise<void>((resolve) => {\n setTimeout(resolve, PID_TERMINATION_POLL_MS);\n });\n }\n\n signalPidOrGroup(pid, \"SIGKILL\");\n}\n\nexport async function killProcessGroupOrProc(\n child: ChildProcess,\n timeoutMs: number = CHILD_SIGTERM_GRACE_MS,\n): Promise<void> {\n if (child.pid === undefined) {\n return;\n }\n if (child.exitCode !== null || child.signalCode !== null) {\n return;\n }\n await terminatePidOrGroup(child.pid, timeoutMs);\n}\n","import { rm } from \"node:fs/promises\";\nimport process from \"node:process\";\n\nimport { killProcessOnPort } from \"../port.js\";\nimport { matchesKey, removeSession } from \"../state.js\";\nimport type { ActiveSession, SessionKey } from \"../types.js\";\n\nimport { PORT_CLEANUP_DELAY_MS } from \"./constants.js\";\nimport { pruneAndCleanupOrphans } from \"./orphans.js\";\nimport { terminatePidOrGroup } from \"./processes.js\";\n\nexport interface StopOptions {\n readonly sessionId?: string;\n readonly key?: SessionKey;\n}\n\nexport async function stopDebugger(options: StopOptions): Promise<ActiveSession | undefined> {\n const sessions = await pruneAndCleanupOrphans();\n let target: ActiveSession | undefined;\n if (options.sessionId !== undefined) {\n target = sessions.find((s) => s.sessionId === options.sessionId);\n } else if (options.key !== undefined) {\n const key = options.key;\n target = sessions.find((s) => matchesKey(s, key));\n }\n if (target === undefined) {\n return undefined;\n }\n if (target.pid !== process.pid) {\n try {\n await terminatePidOrGroup(target.pid);\n } catch {\n // process already gone — cleanup below\n }\n }\n setTimeout(() => {\n void killProcessOnPort(target.localPort);\n }, PORT_CLEANUP_DELAY_MS);\n const removed = await removeSession(target.sessionId);\n try {\n await rm(target.cfHomeDir, { recursive: true, force: true });\n } catch {\n // best-effort\n }\n return removed ?? target;\n}\n\nexport async function stopAllDebuggers(): Promise<number> {\n const sessions = await pruneAndCleanupOrphans();\n let stopped = 0;\n for (const session of sessions) {\n const result = await stopDebugger({ sessionId: session.sessionId });\n if (result) {\n stopped += 1;\n }\n }\n return stopped;\n}\n\nexport async function listSessions(): Promise<readonly ActiveSession[]> {\n return await pruneAndCleanupOrphans();\n}\n\nexport async function getSession(key: SessionKey): Promise<ActiveSession | undefined> {\n const sessions = await pruneAndCleanupOrphans();\n return sessions.find((s) => matchesKey(s, key));\n}\n"],"mappings":"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","names":["mkdir","process","execFile","promisify","execFileAsync","mkdir","dirname","process","process","mkdir","dirname","session","process","getHostname","getHostname","process","process","process","mkdir","rm","process","process","rm"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/debug-session/start.ts","../src/cloud-foundry/execute.ts","../src/cloud-foundry/commands.ts","../src/cloud-foundry/ssh.ts","../src/paths.ts","../src/network/ports.ts","../src/regions.ts","../src/session-state/store.ts","../src/lock.ts","../src/debug-session/constants.ts","../src/debug-session/orphans.ts","../src/debug-session/processes.ts","../src/debug-session/sessions.ts"],"sourcesContent":["export interface SessionKey {\n readonly region: string;\n readonly org: string;\n readonly space: string;\n readonly app: string;\n}\n\nexport type SessionStatus =\n | \"starting\"\n | \"logging-in\"\n | \"targeting\"\n | \"ssh-enabling\"\n | \"ssh-restarting\"\n | \"signaling\"\n | \"tunneling\"\n | \"ready\"\n | \"stopping\"\n | \"stopped\"\n | \"error\";\n\nexport interface ActiveSession extends SessionKey {\n readonly sessionId: string;\n readonly pid: number;\n readonly hostname: string;\n readonly localPort: number;\n readonly remotePort: number;\n readonly apiEndpoint: string;\n readonly cfHomeDir: string;\n readonly startedAt: string;\n readonly status: SessionStatus;\n readonly message?: string;\n}\n\nexport interface StartDebuggerOptions extends SessionKey {\n readonly email?: string;\n readonly password?: string;\n readonly apiEndpoint?: string;\n readonly preferredPort?: number;\n readonly tunnelReadyTimeoutMs?: number;\n readonly verbose?: boolean;\n readonly onStatus?: (status: SessionStatus, message?: string) => void;\n readonly signal?: AbortSignal;\n}\n\nexport interface DebuggerHandle {\n readonly session: ActiveSession;\n dispose(): Promise<void>;\n waitForExit(): Promise<number | null>;\n}\n\nexport interface StateFile {\n readonly version: \"1\";\n readonly sessions: readonly ActiveSession[];\n}\n\nexport class CfDebuggerError extends Error {\n public readonly code: string;\n public readonly stderr?: string;\n\n public constructor(code: string, message: string, stderr?: string) {\n super(message);\n this.name = \"CfDebuggerError\";\n this.code = code;\n if (stderr !== undefined) {\n this.stderr = stderr;\n }\n }\n}\n","import type { ChildProcess } from \"node:child_process\";\nimport { mkdir, rm } from \"node:fs/promises\";\nimport process from \"node:process\";\n\nimport type { CfExecContext } from \"../cf.js\";\nimport {\n cfEnableSsh,\n cfLogin,\n cfRestartApp,\n cfSshEnabled,\n cfSshOneShot,\n cfTarget,\n isSshDisabledError,\n spawnSshTunnel,\n} from \"../cf.js\";\nimport { sessionCfHomeDir } from \"../paths.js\";\nimport { findListeningProcessId, isPortFree, killProcessOnPort, probeTunnelReady } from \"../port.js\";\nimport { resolveApiEndpoint } from \"../regions.js\";\nimport {\n registerNewSession,\n removeSession,\n sessionKeyString,\n updateSessionPid,\n updateSessionStatus,\n} from \"../state.js\";\nimport type { ActiveSession, DebuggerHandle, SessionStatus, StartDebuggerOptions } from \"../types.js\";\nimport { CfDebuggerError } from \"../types.js\";\n\nimport {\n DEFAULT_TUNNEL_READY_TIMEOUT_MS,\n PORT_CLEANUP_DELAY_MS,\n PORT_RECLAIM_DELAY_MS,\n POST_USR1_DELAY_MS,\n} from \"./constants.js\";\nimport { pruneAndCleanupOrphans } from \"./orphans.js\";\nimport { killProcessGroupOrProc } from \"./processes.js\";\n\ntype StatusEmitter = (status: SessionStatus, message?: string) => void;\n\ninterface TunnelResult {\n readonly child: ChildProcess;\n readonly activePid: number;\n}\n\ntype SignalResult = Awaited<ReturnType<typeof cfSshOneShot>>;\n\nfunction signalFailureDetail(result: SignalResult): string {\n if (result.timedOutAfterMs !== undefined) {\n return `timed out after ${(result.timedOutAfterMs / 1000).toString()}s`;\n }\n const stderr = result.stderr.trim();\n if (stderr.length > 0) {\n return stderr;\n }\n if (result.signal !== undefined) {\n return `terminated by signal ${result.signal}`;\n }\n return `exit code ${String(result.exitCode)}`;\n}\n\nfunction checkAbort(signal: AbortSignal | undefined): void {\n if (signal?.aborted) {\n throw new CfDebuggerError(\"ABORTED\", \"Operation aborted by caller\");\n }\n}\n\nfunction requireCredentials(options: StartDebuggerOptions): {\n readonly email: string;\n readonly password: string;\n} {\n const email = options.email ?? process.env[\"SAP_EMAIL\"];\n const password = options.password ?? process.env[\"SAP_PASSWORD\"];\n if (email === undefined || email === \"\") {\n throw new CfDebuggerError(\n \"MISSING_CREDENTIALS\",\n \"SAP email is required. Pass `email` or set SAP_EMAIL env var.\",\n );\n }\n if (password === undefined || password === \"\") {\n throw new CfDebuggerError(\n \"MISSING_CREDENTIALS\",\n \"SAP password is required. Pass `password` or set SAP_PASSWORD env var.\",\n );\n }\n return { email, password };\n}\n\nasync function registerSession(\n options: StartDebuggerOptions,\n apiEndpoint: string,\n): Promise<ActiveSession> {\n const registration = await registerNewSession({\n region: options.region,\n org: options.org,\n space: options.space,\n app: options.app,\n apiEndpoint,\n ...(options.preferredPort === undefined ? {} : { preferredPort: options.preferredPort }),\n portProbe: isPortFree,\n cfHomeForSession: sessionCfHomeDir,\n });\n\n if (registration.existing) {\n throw new CfDebuggerError(\n \"SESSION_ALREADY_RUNNING\",\n `A debugger session is already running for ${sessionKeyString(options)} ` +\n `on port ${registration.existing.localPort.toString()} ` +\n `(pid ${registration.existing.pid.toString()}, sessionId ${registration.existing.sessionId}). ` +\n `Stop it first with \\`cf-debugger stop\\`.`,\n );\n }\n return registration.session;\n}\n\nasync function loginAndTarget(\n options: StartDebuggerOptions,\n apiEndpoint: string,\n email: string,\n password: string,\n context: CfExecContext,\n sessionId: string,\n emit: StatusEmitter,\n): Promise<void> {\n emit(\"logging-in\");\n await updateSessionStatus(sessionId, \"logging-in\");\n await cfLogin(apiEndpoint, email, password, context);\n checkAbort(options.signal);\n\n emit(\"targeting\");\n await updateSessionStatus(sessionId, \"targeting\");\n await cfTarget(options.org, options.space, context);\n checkAbort(options.signal);\n}\n\nasync function signalRemoteNode(\n options: StartDebuggerOptions,\n context: CfExecContext,\n sessionId: string,\n emit: StatusEmitter,\n): Promise<void> {\n emit(\"signaling\");\n await updateSessionStatus(sessionId, \"signaling\");\n const signalResult = await cfSshOneShot(options.app, `kill -s USR1 $(pidof node)`, context);\n\n if (!isSshDisabledError(signalResult.stderr)) {\n if (signalResult.exitCode === 0) {\n return;\n }\n throw new CfDebuggerError(\n \"USR1_SIGNAL_FAILED\",\n `Failed to send SIGUSR1 to the Node.js process on ${options.app}: ${signalFailureDetail(signalResult)}`,\n signalResult.stderr,\n );\n }\n\n const alreadyEnabled = await cfSshEnabled(options.app, context);\n if (!alreadyEnabled) {\n emit(\"ssh-enabling\", \"Enabling SSH on the app\");\n await updateSessionStatus(sessionId, \"ssh-enabling\");\n await cfEnableSsh(options.app, context);\n }\n emit(\"ssh-restarting\", \"Restarting app so SSH becomes active\");\n await updateSessionStatus(sessionId, \"ssh-restarting\");\n await cfRestartApp(options.app, context);\n checkAbort(options.signal);\n\n await retryRemoteSignal(options, context, sessionId, emit);\n}\n\nasync function retryRemoteSignal(\n options: StartDebuggerOptions,\n context: CfExecContext,\n sessionId: string,\n emit: StatusEmitter,\n): Promise<void> {\n emit(\"signaling\");\n await updateSessionStatus(sessionId, \"signaling\");\n const retrySignalResult = await cfSshOneShot(\n options.app,\n `kill -s USR1 $(pidof node)`,\n context,\n );\n if (retrySignalResult.exitCode === 0) {\n return;\n }\n throw new CfDebuggerError(\n \"USR1_SIGNAL_FAILED\",\n `Failed to send SIGUSR1 to the Node.js process on ${options.app} after enabling SSH: ${\n signalFailureDetail(retrySignalResult)\n }`,\n retrySignalResult.stderr,\n );\n}\n\nasync function waitAfterSignal(signal: AbortSignal | undefined): Promise<void> {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, POST_USR1_DELAY_MS);\n });\n checkAbort(signal);\n}\n\nasync function ensurePortAvailable(localPort: number): Promise<void> {\n if (await isPortFree(localPort)) {\n return;\n }\n await killProcessOnPort(localPort);\n await new Promise<void>((resolve) => {\n setTimeout(resolve, PORT_RECLAIM_DELAY_MS);\n });\n if (!(await isPortFree(localPort))) {\n throw new CfDebuggerError(\n \"PORT_UNAVAILABLE\",\n `Local port ${localPort.toString()} is in use and could not be reclaimed for the tunnel.`,\n );\n }\n}\n\nasync function openReadyTunnel(\n options: StartDebuggerOptions,\n session: ActiveSession,\n context: CfExecContext,\n tunnelReadyTimeoutMs: number,\n onChild: (child: ChildProcess) => void,\n): Promise<TunnelResult> {\n await ensurePortAvailable(session.localPort);\n const child = spawnSshTunnel(options.app, session.localPort, session.remotePort, context);\n onChild(child);\n if (child.pid !== undefined) {\n await updateSessionPid(session.sessionId, child.pid);\n }\n\n const ready = await probeTunnelReady(session.localPort, tunnelReadyTimeoutMs);\n checkAbort(options.signal);\n if (!ready) {\n throw new CfDebuggerError(\n \"TUNNEL_NOT_READY\",\n `SSH tunnel on port ${session.localPort.toString()} did not become ready within ` +\n `${Math.round(tunnelReadyTimeoutMs / 1000).toString()}s.`,\n );\n }\n\n const listeningPid = await findListeningProcessId(session.localPort);\n const activePid = listeningPid ?? child.pid ?? session.pid;\n if (activePid !== session.pid) {\n await updateSessionPid(session.sessionId, activePid);\n }\n return { child, activePid };\n}\n\nfunction attachTunnelEvents(\n child: ChildProcess,\n markClosed: () => void,\n resolveExit: (code: number | null) => void,\n emit: StatusEmitter,\n): void {\n child.on(\"close\", (code) => {\n markClosed();\n resolveExit(code);\n });\n\n child.on(\"error\", (err: Error) => {\n emit(\"error\", err.message);\n });\n}\n\nfunction createHandle(\n session: ActiveSession,\n emit: StatusEmitter,\n finalize: () => Promise<void>,\n exitPromise: Promise<number | null>,\n): DebuggerHandle {\n let disposePromise: Promise<void> | undefined;\n return {\n session,\n dispose: async (): Promise<void> => {\n disposePromise ??= (async (): Promise<void> => {\n emit(\"stopping\");\n await updateSessionStatus(session.sessionId, \"stopping\");\n await finalize();\n })();\n await disposePromise;\n },\n waitForExit: async (): Promise<number | null> => {\n return await exitPromise;\n },\n };\n}\n\nexport async function startDebugger(options: StartDebuggerOptions): Promise<DebuggerHandle> {\n const { email, password } = requireCredentials(options);\n const apiEndpoint = resolveApiEndpoint(options.region, options.apiEndpoint);\n const tunnelReadyTimeoutMs = options.tunnelReadyTimeoutMs ?? DEFAULT_TUNNEL_READY_TIMEOUT_MS;\n const emit = (status: SessionStatus, message?: string): void => {\n options.onStatus?.(status, message);\n };\n\n checkAbort(options.signal);\n await pruneAndCleanupOrphans();\n\n const session = await registerSession(options, apiEndpoint);\n const context: CfExecContext = { cfHome: session.cfHomeDir };\n let child: ChildProcess | undefined;\n let tunnelClosed = false;\n let exitResolve: (code: number | null) => void = (_code) => {\n throw new Error(\"Exit resolver was used before initialization\");\n };\n const exitPromise = new Promise<number | null>((resolve) => {\n exitResolve = resolve;\n });\n\n const finalize = async (): Promise<void> => {\n if (!tunnelClosed) {\n tunnelClosed = true;\n if (child) {\n await killProcessGroupOrProc(child);\n }\n setTimeout(() => {\n void killProcessOnPort(session.localPort);\n }, PORT_CLEANUP_DELAY_MS);\n }\n await removeSession(session.sessionId);\n await cleanupFilesystem(session.cfHomeDir);\n emit(\"stopped\");\n };\n\n try {\n await mkdir(session.cfHomeDir, { recursive: true });\n await loginAndTarget(options, apiEndpoint, email, password, context, session.sessionId, emit);\n await killProcessOnPort(session.localPort);\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 200);\n });\n await signalRemoteNode(options, context, session.sessionId, emit);\n await waitAfterSignal(options.signal);\n\n emit(\"tunneling\");\n await updateSessionStatus(session.sessionId, \"tunneling\");\n const tunnel = await openReadyTunnel(\n options,\n session,\n context,\n tunnelReadyTimeoutMs,\n (tunnelChild) => {\n child = tunnelChild;\n attachTunnelEvents(tunnelChild, () => {\n tunnelClosed = true;\n }, exitResolve, emit);\n },\n );\n child = tunnel.child;\n\n emit(\"ready\");\n const readySession = await updateSessionStatus(session.sessionId, \"ready\");\n const activeSession = readySession ?? { ...session, pid: tunnel.activePid, status: \"ready\" };\n return createHandle(activeSession, emit, finalize, exitPromise);\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n emit(\"error\", message);\n await finalize();\n throw err;\n }\n}\n\nasync function cleanupFilesystem(cfHomeDir: string): Promise<void> {\n try {\n await rm(cfHomeDir, { recursive: true, force: true });\n } catch {\n // best-effort\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport { CfDebuggerError } from \"../types.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst MAX_BUFFER = 16 * 1024 * 1024;\nexport const DEFAULT_CF_COMMAND_TIMEOUT_MS = 180_000;\nconst REDACTED_ARG = \"<redacted>\";\n\nexport interface CfExecContext {\n readonly cfHome: string;\n readonly command?: string;\n}\n\nexport function buildEnv(cfHome: string): NodeJS.ProcessEnv {\n return { ...process.env, CF_HOME: cfHome };\n}\n\nexport function resolveBin(context: CfExecContext): string {\n return context.command ?? process.env[\"CF_DEBUGGER_CF_BIN\"] ?? \"cf\";\n}\n\nfunction sensitiveArgs(args: readonly string[]): readonly string[] {\n if (args[0] !== \"auth\") {\n return [];\n }\n return args.slice(1).filter((arg) => arg.length > 0);\n}\n\nfunction redactText(text: string, values: readonly string[]): string {\n return values.reduce((current, value) => current.split(value).join(REDACTED_ARG), text);\n}\n\nfunction formatArgsForError(args: readonly string[]): string {\n if (args[0] !== \"auth\") {\n return args.join(\" \");\n }\n return args.map((arg, index) => (index === 0 ? arg : REDACTED_ARG)).join(\" \");\n}\n\nexport async function runCf(\n args: readonly string[],\n context: CfExecContext,\n timeoutMs: number = DEFAULT_CF_COMMAND_TIMEOUT_MS,\n): Promise<string> {\n try {\n const { stdout } = await execFileAsync(resolveBin(context), [...args], {\n env: buildEnv(context.cfHome),\n maxBuffer: MAX_BUFFER,\n timeout: timeoutMs,\n });\n return stdout;\n } catch (err: unknown) {\n const e = err as NodeJS.ErrnoException & { stderr?: string; stdout?: string };\n const redactionValues = sensitiveArgs(args);\n const stderr = redactText(e.stderr?.trim() ?? \"\", redactionValues);\n const fallbackMessage = redactText(e.message, redactionValues);\n throw new CfDebuggerError(\n \"CF_CLI_FAILED\",\n `cf ${formatArgsForError(args)} failed: ${stderr.length > 0 ? stderr : fallbackMessage}`,\n stderr,\n );\n }\n}\n","import { CfDebuggerError } from \"../types.js\";\n\nimport { type CfExecContext, runCf } from \"./execute.js\";\nimport { parseAppNames, parseNameTable } from \"./parsers.js\";\n\nconst CF_AUTH_MAX_ATTEMPTS = 3;\n\nexport async function cfApi(apiEndpoint: string, context: CfExecContext): Promise<void> {\n await runCf([\"api\", apiEndpoint], context);\n}\n\nexport async function cfAuth(\n email: string,\n password: string,\n context: CfExecContext,\n): Promise<void> {\n let lastError: unknown;\n for (let attempt = 0; attempt < CF_AUTH_MAX_ATTEMPTS; attempt++) {\n try {\n await runCf([\"auth\", email, password], context);\n return;\n } catch (err: unknown) {\n lastError = err;\n if (attempt < CF_AUTH_MAX_ATTEMPTS - 1) {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 1000 * (attempt + 1));\n });\n }\n }\n }\n if (lastError instanceof Error) {\n throw lastError;\n }\n throw new CfDebuggerError(\"CF_AUTH_FAILED\", `cf auth failed: ${String(lastError)}`);\n}\n\nexport async function cfLogin(\n apiEndpoint: string,\n email: string,\n password: string,\n context: CfExecContext,\n): Promise<void> {\n try {\n await cfApi(apiEndpoint, context);\n await cfAuth(email, password, context);\n } catch (err: unknown) {\n if (err instanceof CfDebuggerError) {\n throw new CfDebuggerError(\"CF_LOGIN_FAILED\", err.message, err.stderr);\n }\n throw err;\n }\n}\n\nexport async function cfTarget(\n org: string,\n space: string,\n context: CfExecContext,\n): Promise<void> {\n try {\n await runCf([\"target\", \"-o\", org, \"-s\", space], context);\n } catch (err: unknown) {\n if (err instanceof CfDebuggerError) {\n throw new CfDebuggerError(\"CF_TARGET_FAILED\", err.message, err.stderr);\n }\n throw err;\n }\n}\n\nexport async function cfAppExists(appName: string, context: CfExecContext): Promise<boolean> {\n try {\n await runCf([\"app\", appName], context);\n return true;\n } catch (err: unknown) {\n const stderr = (err as CfDebuggerError).stderr ?? \"\";\n if (stderr.toLowerCase().includes(\"not found\")) {\n return false;\n }\n throw err;\n }\n}\n\nexport async function cfSshEnabled(appName: string, context: CfExecContext): Promise<boolean> {\n try {\n const stdout = await runCf([\"ssh-enabled\", appName], context);\n return stdout.toLowerCase().includes(\"ssh support is enabled\");\n } catch {\n return false;\n }\n}\n\nexport async function cfEnableSsh(appName: string, context: CfExecContext): Promise<void> {\n try {\n await runCf([\"enable-ssh\", appName], context);\n } catch (err: unknown) {\n if (err instanceof CfDebuggerError) {\n throw new CfDebuggerError(\"SSH_NOT_ENABLED\", err.message, err.stderr);\n }\n throw err;\n }\n}\n\nexport async function cfRestartApp(appName: string, context: CfExecContext): Promise<void> {\n await runCf([\"restart\", appName], context);\n}\n\nexport async function cfApps(context: CfExecContext): Promise<readonly string[]> {\n const stdout = await runCf([\"apps\"], context);\n return parseAppNames(stdout);\n}\n\nexport async function cfOrgs(context: CfExecContext): Promise<readonly string[]> {\n const stdout = await runCf([\"orgs\"], context);\n return parseNameTable(stdout);\n}\n\nexport async function cfSpaces(context: CfExecContext): Promise<readonly string[]> {\n const stdout = await runCf([\"spaces\"], context);\n return parseNameTable(stdout);\n}\n","import { spawn } from \"node:child_process\";\n\nimport {\n buildEnv,\n DEFAULT_CF_COMMAND_TIMEOUT_MS,\n resolveBin,\n type CfExecContext,\n} from \"./execute.js\";\n\nexport interface CfSshSignalResult {\n readonly exitCode: number | null;\n readonly stderr: string;\n readonly signal?: NodeJS.Signals;\n readonly timedOutAfterMs?: number;\n}\n\nexport async function cfSshOneShot(\n appName: string,\n command: string,\n context: CfExecContext,\n timeoutMs: number = DEFAULT_CF_COMMAND_TIMEOUT_MS,\n): Promise<CfSshSignalResult> {\n return await new Promise<CfSshSignalResult>((resolve) => {\n const child = spawn(resolveBin(context), [\"ssh\", appName, \"-c\", command], {\n env: buildEnv(context.cfHome),\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n let stderrBuf = \"\";\n let settled = false;\n\n const timeout = setTimeout(() => {\n if (settled) {\n return;\n }\n settled = true;\n try {\n child.kill();\n } catch {\n // already gone\n }\n resolve({ exitCode: null, stderr: stderrBuf, timedOutAfterMs: timeoutMs });\n }, timeoutMs);\n\n child.stderr.on(\"data\", (data: Buffer | string) => {\n stderrBuf += data.toString();\n });\n\n child.on(\"close\", (code, signal) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timeout);\n resolve(\n signal === null\n ? { exitCode: code, stderr: stderrBuf }\n : { exitCode: code, stderr: stderrBuf, signal },\n );\n });\n\n child.on(\"error\", (err: Error) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timeout);\n resolve({ exitCode: null, stderr: err.message });\n });\n });\n}\n\nexport function isSshDisabledError(stderr: string): boolean {\n const lower = stderr.toLowerCase();\n return lower.includes(\"not authorized\") || lower.includes(\"ssh support is disabled\");\n}\n\nexport function spawnSshTunnel(\n appName: string,\n localPort: number,\n remotePort: number,\n context: CfExecContext,\n): ReturnType<typeof spawn> {\n const tunnelArg = `${localPort.toString()}:localhost:${remotePort.toString()}`;\n const isWindows = process.platform === \"win32\";\n return spawn(resolveBin(context), [\"ssh\", appName, \"-N\", \"-L\", tunnelArg], {\n env: buildEnv(context.cfHome),\n shell: isWindows,\n detached: !isWindows,\n });\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const SAPTOOLS_DIR_NAME = \".saptools\";\nexport const CF_DEBUGGER_STATE_FILENAME = \"cf-debugger-state.json\";\nexport const CF_DEBUGGER_LOCK_FILENAME = \"cf-debugger-state.lock\";\nexport const CF_DEBUGGER_HOMES_DIRNAME = \"cf-debugger-homes\";\n\nexport function saptoolsDir(): string {\n return join(homedir(), SAPTOOLS_DIR_NAME);\n}\n\nexport function stateFilePath(): string {\n return join(saptoolsDir(), CF_DEBUGGER_STATE_FILENAME);\n}\n\nexport function stateLockPath(): string {\n return join(saptoolsDir(), CF_DEBUGGER_LOCK_FILENAME);\n}\n\nexport function sessionCfHomeDir(sessionId: string): string {\n return join(saptoolsDir(), CF_DEBUGGER_HOMES_DIRNAME, sessionId);\n}\n","import { execFile } from \"node:child_process\";\nimport { createConnection, createServer } from \"node:net\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nasync function findListeningPidsWithNetstat(port: number): Promise<readonly number[]> {\n try {\n const { stdout } = await execFileAsync(\"netstat\", [\"-ano\"]);\n const pids = new Set<number>();\n for (const line of stdout.split(\"\\n\")) {\n if (!line.includes(`:${port.toString()}`) || !line.includes(\"LISTENING\")) {\n continue;\n }\n const parts = line.trim().split(/\\s+/);\n const last = parts[parts.length - 1];\n if (last === undefined) {\n continue;\n }\n const pid = Number.parseInt(last, 10);\n if (!Number.isNaN(pid)) {\n pids.add(pid);\n }\n }\n return [...pids];\n } catch {\n return [];\n }\n}\n\nasync function findListeningPidsWithLsof(port: number): Promise<readonly number[]> {\n try {\n const { stdout } = await execFileAsync(\"lsof\", [\"-nP\", \"-t\", \"-i\", `tcp:${port.toString()}`, \"-sTCP:LISTEN\"]);\n return stdout\n .trim()\n .split(\"\\n\")\n .filter((line) => line.length > 0)\n .map((line) => Number.parseInt(line, 10))\n .filter((pid) => !Number.isNaN(pid));\n } catch {\n return [];\n }\n}\n\nasync function findListeningPids(port: number): Promise<readonly number[]> {\n if (process.platform === \"win32\") {\n return await findListeningPidsWithNetstat(port);\n }\n return await findListeningPidsWithLsof(port);\n}\n\nexport async function isPortFree(port: number): Promise<boolean> {\n return await new Promise<boolean>((resolve) => {\n const server = createServer();\n server.once(\"error\", () => {\n resolve(false);\n });\n server.once(\"listening\", () => {\n server.close(() => {\n resolve(true);\n });\n });\n server.listen(port, \"127.0.0.1\");\n });\n}\n\nexport async function probeTunnelReady(port: number, timeoutMs: number): Promise<boolean> {\n const pollIntervalMs = 250;\n const started = Date.now();\n\n while (Date.now() - started < timeoutMs) {\n const connected = await new Promise<boolean>((resolve) => {\n const socket = createConnection({ port, host: \"127.0.0.1\" });\n socket.setTimeout(200);\n socket.once(\"connect\", () => {\n socket.destroy();\n resolve(true);\n });\n socket.once(\"error\", () => {\n socket.destroy();\n resolve(false);\n });\n socket.once(\"timeout\", () => {\n socket.destroy();\n resolve(false);\n });\n });\n\n if (connected) {\n return true;\n }\n\n await new Promise<void>((resolve) => {\n setTimeout(resolve, pollIntervalMs);\n });\n }\n\n return false;\n}\n\nexport async function findListeningProcessId(port: number): Promise<number | undefined> {\n const pids = await findListeningPids(port);\n return pids[0];\n}\n\nexport async function killProcessOnPort(port: number): Promise<void> {\n const portStr = port.toString();\n if (process.platform === \"win32\") {\n try {\n const pids = await findListeningPidsWithNetstat(port);\n for (const pid of pids) {\n try {\n // cspell:ignore taskkill\n await execFileAsync(\"taskkill\", [\"/F\", \"/PID\", pid.toString()]);\n } catch {\n // ignore\n }\n }\n } catch {\n // ignore\n }\n return;\n }\n\n try {\n const { stdout } = await execFileAsync(\"lsof\", [\"-t\", \"-i\", `tcp:${portStr}`]);\n const lines = stdout.trim().split(\"\\n\").filter((line) => line.length > 0);\n for (const line of lines) {\n const pid = Number.parseInt(line, 10);\n if (Number.isNaN(pid)) {\n continue;\n }\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {\n // already dead\n }\n }\n } catch {\n // lsof missing or no match — ignore\n }\n}\n","export interface RegionInfo {\n readonly key: string;\n readonly apiEndpoint: string;\n}\n\nconst REGION_API_ENDPOINTS: Readonly<Record<string, string>> = {\n ae01: \"https://api.cf.ae01.hana.ondemand.com\",\n ap01: \"https://api.cf.ap01.hana.ondemand.com\",\n ap10: \"https://api.cf.ap10.hana.ondemand.com\",\n ap11: \"https://api.cf.ap11.hana.ondemand.com\",\n ap12: \"https://api.cf.ap12.hana.ondemand.com\",\n ap20: \"https://api.cf.ap20.hana.ondemand.com\",\n ap21: \"https://api.cf.ap21.hana.ondemand.com\",\n ap30: \"https://api.cf.ap30.hana.ondemand.com\",\n br10: \"https://api.cf.br10.hana.ondemand.com\",\n br20: \"https://api.cf.br20.hana.ondemand.com\",\n br30: \"https://api.cf.br30.hana.ondemand.com\",\n ca10: \"https://api.cf.ca10.hana.ondemand.com\",\n ca20: \"https://api.cf.ca20.hana.ondemand.com\",\n ch20: \"https://api.cf.ch20.hana.ondemand.com\",\n eu10: \"https://api.cf.eu10.hana.ondemand.com\",\n eu11: \"https://api.cf.eu11.hana.ondemand.com\",\n eu12: \"https://api.cf.eu12.hana.ondemand.com\",\n eu20: \"https://api.cf.eu20.hana.ondemand.com\",\n eu21: \"https://api.cf.eu21.hana.ondemand.com\",\n eu30: \"https://api.cf.eu30.hana.ondemand.com\",\n eu31: \"https://api.cf.eu31.hana.ondemand.com\",\n in30: \"https://api.cf.in30.hana.ondemand.com\",\n jp10: \"https://api.cf.jp10.hana.ondemand.com\",\n jp20: \"https://api.cf.jp20.hana.ondemand.com\",\n jp30: \"https://api.cf.jp30.hana.ondemand.com\",\n kr30: \"https://api.cf.kr30.hana.ondemand.com\",\n us10: \"https://api.cf.us10.hana.ondemand.com\",\n us11: \"https://api.cf.us11.hana.ondemand.com\",\n us20: \"https://api.cf.us20.hana.ondemand.com\",\n us21: \"https://api.cf.us21.hana.ondemand.com\",\n us30: \"https://api.cf.us30.hana.ondemand.com\",\n us31: \"https://api.cf.us31.hana.ondemand.com\",\n};\n\nexport function resolveApiEndpoint(regionKey: string, override?: string): string {\n if (override !== undefined && override !== \"\") {\n return override;\n }\n const endpoint = REGION_API_ENDPOINTS[regionKey];\n if (endpoint === undefined) {\n throw new Error(\n `Unknown region key: ${regionKey}. Pass \\`apiEndpoint\\` explicitly to override.`,\n );\n }\n return endpoint;\n}\n\nexport function listKnownRegionKeys(): readonly string[] {\n return Object.keys(REGION_API_ENDPOINTS);\n}\n","import { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { hostname as getHostname } from \"node:os\";\nimport { dirname } from \"node:path\";\nimport process from \"node:process\";\n\nimport { withFileLock } from \"../lock.js\";\nimport { stateFilePath, stateLockPath } from \"../paths.js\";\nimport { CfDebuggerError } from \"../types.js\";\nimport type { ActiveSession, SessionKey, StateFile } from \"../types.js\";\n\nasync function readJsonFile<T>(path: string): Promise<T | undefined> {\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") {\n return undefined;\n }\n throw err;\n }\n try {\n return JSON.parse(raw) as T;\n } catch {\n process.stderr.write(\n `[cf-debugger] warning: state file at ${path} is not valid JSON; resetting to empty.\\n`,\n );\n return undefined;\n }\n}\n\nasync function writeJsonFileAtomic(path: string, value: unknown): Promise<void> {\n const tempPath = `${path}.${randomUUID()}.tmp`;\n await mkdir(dirname(path), { recursive: true });\n await writeFile(tempPath, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n await rename(tempPath, path);\n}\n\nfunction emptyState(): StateFile {\n return { version: \"1\", sessions: [] };\n}\n\nfunction isValidState(value: unknown): value is StateFile {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const candidate = value as Partial<StateFile>;\n return candidate.version === \"1\" && Array.isArray(candidate.sessions);\n}\n\nexport function isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ESRCH\") {\n return false;\n }\n return true;\n }\n}\n\nfunction filterStaleSessions(sessions: readonly ActiveSession[]): readonly ActiveSession[] {\n const host = getHostname();\n return sessions.filter((session) => {\n if (session.hostname !== host) {\n return true;\n }\n return isPidAlive(session.pid);\n });\n}\n\nasync function readStateRaw(): Promise<StateFile> {\n const parsed = await readJsonFile<unknown>(stateFilePath());\n if (!isValidState(parsed)) {\n return emptyState();\n }\n return parsed;\n}\n\nasync function writeState(state: StateFile): Promise<void> {\n await writeJsonFileAtomic(stateFilePath(), state);\n}\n\nexport interface StateReaderResult {\n readonly sessions: readonly ActiveSession[];\n readonly removed: readonly ActiveSession[];\n}\n\nasync function readAndPruneLocked(): Promise<StateReaderResult> {\n const raw = await readStateRaw();\n const pruned = filterStaleSessions(raw.sessions);\n const removed = raw.sessions.filter(\n (session) => !pruned.some((active) => active.sessionId === session.sessionId),\n );\n\n if (removed.length > 0) {\n await writeState({ version: \"1\", sessions: pruned });\n }\n\n return { sessions: pruned, removed };\n}\n\nexport async function readActiveSessions(): Promise<readonly ActiveSession[]> {\n const result = await withFileLock(stateLockPath(), readAndPruneLocked);\n return result.sessions;\n}\n\nexport async function readSessionSnapshot(): Promise<readonly ActiveSession[]> {\n return await withFileLock(stateLockPath(), async (): Promise<readonly ActiveSession[]> => {\n const raw = await readStateRaw();\n return raw.sessions;\n });\n}\n\nexport async function readAndPruneActiveSessions(): Promise<StateReaderResult> {\n return await withFileLock(stateLockPath(), readAndPruneLocked);\n}\n\nexport function sessionKeyString(key: SessionKey): string {\n return `${key.region}:${key.org}:${key.space}:${key.app}`;\n}\n\nexport function matchesKey(session: SessionKey, key: SessionKey): boolean {\n return (\n session.region === key.region &&\n session.org === key.org &&\n session.space === key.space &&\n session.app === key.app\n );\n}\n\nexport interface RegisterSessionResult {\n readonly session: ActiveSession;\n readonly existing?: ActiveSession;\n}\n\nexport interface RegisterSessionInput extends SessionKey {\n readonly apiEndpoint: string;\n readonly preferredPort?: number;\n readonly portProbe: (port: number) => Promise<boolean>;\n readonly sessionIdFactory?: () => string;\n readonly cfHomeForSession: (sessionId: string) => string;\n readonly basePort?: number;\n readonly maxPort?: number;\n}\n\nconst DEFAULT_BASE_PORT = 20_000;\nconst DEFAULT_MAX_PORT = 20_999;\n\nasync function pickPort(\n preferred: number | undefined,\n reserved: ReadonlySet<number>,\n probe: (port: number) => Promise<boolean>,\n basePort: number,\n maxPort: number,\n): Promise<number> {\n const tryOrder: number[] = [];\n if (preferred !== undefined) {\n tryOrder.push(preferred);\n }\n for (let port = basePort; port <= maxPort; port++) {\n if (port !== preferred) {\n tryOrder.push(port);\n }\n }\n\n for (const port of tryOrder) {\n if (reserved.has(port)) {\n continue;\n }\n const free = await probe(port);\n if (free) {\n return port;\n }\n }\n throw new CfDebuggerError(\n \"PORT_UNAVAILABLE\",\n `No free local port available in range ${basePort.toString()}–${maxPort.toString()}`,\n );\n}\n\nexport async function registerNewSession(\n input: RegisterSessionInput,\n): Promise<RegisterSessionResult> {\n return await withFileLock(stateLockPath(), async (): Promise<RegisterSessionResult> => {\n const pruneResult = await readAndPruneLocked();\n const existing = pruneResult.sessions.find((session) => matchesKey(session, input));\n if (existing) {\n return { session: existing, existing };\n }\n\n const reservedPorts = new Set(pruneResult.sessions.map((session) => session.localPort));\n const localPort = await pickPort(\n input.preferredPort,\n reservedPorts,\n input.portProbe,\n input.basePort ?? DEFAULT_BASE_PORT,\n input.maxPort ?? DEFAULT_MAX_PORT,\n );\n\n const sessionId = (input.sessionIdFactory ?? randomUUID)();\n const cfHomeDir = input.cfHomeForSession(sessionId);\n\n const session: ActiveSession = {\n sessionId,\n pid: process.pid,\n hostname: getHostname(),\n region: input.region,\n org: input.org,\n space: input.space,\n app: input.app,\n apiEndpoint: input.apiEndpoint,\n localPort,\n remotePort: 9229,\n cfHomeDir,\n startedAt: new Date().toISOString(),\n status: \"starting\",\n };\n\n const nextSessions: readonly ActiveSession[] = [...pruneResult.sessions, session];\n await writeState({ version: \"1\", sessions: nextSessions });\n\n return { session };\n });\n}\n\nexport async function updateSessionStatus(\n sessionId: string,\n status: ActiveSession[\"status\"],\n message?: string,\n): Promise<ActiveSession | undefined> {\n return await withFileLock(stateLockPath(), async (): Promise<ActiveSession | undefined> => {\n const raw = await readStateRaw();\n let updated: ActiveSession | undefined;\n const nextSessions = raw.sessions.map((session): ActiveSession => {\n if (session.sessionId !== sessionId) {\n return session;\n }\n const base: ActiveSession = {\n sessionId: session.sessionId,\n pid: session.pid,\n hostname: session.hostname,\n region: session.region,\n org: session.org,\n space: session.space,\n app: session.app,\n apiEndpoint: session.apiEndpoint,\n localPort: session.localPort,\n remotePort: session.remotePort,\n cfHomeDir: session.cfHomeDir,\n startedAt: session.startedAt,\n status,\n };\n const next: ActiveSession = message === undefined ? base : { ...base, message };\n updated = next;\n return next;\n });\n\n if (updated) {\n await writeState({ version: \"1\", sessions: nextSessions });\n }\n return updated;\n });\n}\n\nexport async function updateSessionPid(\n sessionId: string,\n pid: number,\n): Promise<ActiveSession | undefined> {\n return await withFileLock(stateLockPath(), async (): Promise<ActiveSession | undefined> => {\n const raw = await readStateRaw();\n let updated: ActiveSession | undefined;\n const nextSessions = raw.sessions.map((session): ActiveSession => {\n if (session.sessionId !== sessionId) {\n return session;\n }\n const next: ActiveSession = {\n sessionId: session.sessionId,\n pid,\n hostname: session.hostname,\n region: session.region,\n org: session.org,\n space: session.space,\n app: session.app,\n apiEndpoint: session.apiEndpoint,\n localPort: session.localPort,\n remotePort: session.remotePort,\n cfHomeDir: session.cfHomeDir,\n startedAt: session.startedAt,\n status: session.status,\n ...(session.message === undefined ? {} : { message: session.message }),\n };\n updated = next;\n return next;\n });\n\n if (updated !== undefined) {\n await writeState({ version: \"1\", sessions: nextSessions });\n }\n return updated;\n });\n}\n\nexport async function removeSession(sessionId: string): Promise<ActiveSession | undefined> {\n return await withFileLock(stateLockPath(), async (): Promise<ActiveSession | undefined> => {\n const raw = await readStateRaw();\n const target = raw.sessions.find((session) => session.sessionId === sessionId);\n if (!target) {\n return undefined;\n }\n const remaining = raw.sessions.filter((session) => session.sessionId !== sessionId);\n await writeState({ version: \"1\", sessions: remaining });\n return target;\n });\n}\n","import { mkdir, open, unlink } from \"node:fs/promises\";\nimport type { FileHandle } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nconst DEFAULT_POLL_MS = 50;\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nasync function acquireFileLock(\n lockPath: string,\n timeoutMs: number,\n pollMs: number,\n): Promise<FileHandle> {\n const deadline = Date.now() + timeoutMs;\n await mkdir(dirname(lockPath), { recursive: true });\n\n for (;;) {\n try {\n return await open(lockPath, \"wx\");\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"EEXIST\") {\n throw err;\n }\n }\n\n if (Date.now() > deadline) {\n throw new Error(`Timed out acquiring file lock at ${lockPath}`);\n }\n\n await sleep(pollMs);\n }\n}\n\nasync function releaseFileLock(lockPath: string, handle: FileHandle): Promise<void> {\n await handle.close();\n await unlink(lockPath).catch((err: unknown) => {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== \"ENOENT\") {\n throw err;\n }\n });\n}\n\nexport interface WithLockOptions {\n readonly timeoutMs?: number;\n readonly pollMs?: number;\n}\n\nexport async function withFileLock<T>(\n lockPath: string,\n work: () => Promise<T>,\n options?: WithLockOptions,\n): Promise<T> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const pollMs = options?.pollMs ?? DEFAULT_POLL_MS;\n const handle = await acquireFileLock(lockPath, timeoutMs, pollMs);\n try {\n return await work();\n } finally {\n await releaseFileLock(lockPath, handle);\n }\n}\n","export const DEFAULT_TUNNEL_READY_TIMEOUT_MS = 180_000;\nexport const POST_USR1_DELAY_MS = 300;\nexport const PORT_CLEANUP_DELAY_MS = 600;\nexport const CHILD_SIGTERM_GRACE_MS = 2_000;\nexport const PORT_RECLAIM_DELAY_MS = 250;\nexport const PID_TERMINATION_POLL_MS = 100;\n","import { hostname as getHostname } from \"node:os\";\n\nimport { killProcessOnPort } from \"../port.js\";\nimport { readAndPruneActiveSessions } from \"../state.js\";\nimport type { ActiveSession } from \"../types.js\";\n\nexport async function pruneAndCleanupOrphans(): Promise<readonly ActiveSession[]> {\n const result = await readAndPruneActiveSessions();\n const host = getHostname();\n for (const removed of result.removed) {\n if (removed.hostname === host) {\n void killProcessOnPort(removed.localPort);\n }\n }\n return result.sessions;\n}\n","import type { ChildProcess } from \"node:child_process\";\nimport process from \"node:process\";\n\nimport { isPidAlive } from \"../state.js\";\n\nimport { CHILD_SIGTERM_GRACE_MS, PID_TERMINATION_POLL_MS } from \"./constants.js\";\n\nfunction signalPidOrGroup(pid: number, signal: NodeJS.Signals): void {\n const isWindows = process.platform === \"win32\";\n if (!isWindows) {\n try {\n process.kill(-pid, signal);\n return;\n } catch {\n // fall through to direct pid signal\n }\n }\n try {\n process.kill(pid, signal);\n } catch {\n // already gone\n }\n}\n\nexport async function terminatePidOrGroup(\n pid: number,\n timeoutMs: number = CHILD_SIGTERM_GRACE_MS,\n): Promise<void> {\n if (!isPidAlive(pid)) {\n return;\n }\n\n signalPidOrGroup(pid, \"SIGTERM\");\n const startedAt = Date.now();\n while (Date.now() - startedAt < timeoutMs) {\n if (!isPidAlive(pid)) {\n return;\n }\n await new Promise<void>((resolve) => {\n setTimeout(resolve, PID_TERMINATION_POLL_MS);\n });\n }\n\n signalPidOrGroup(pid, \"SIGKILL\");\n}\n\nexport async function killProcessGroupOrProc(\n child: ChildProcess,\n timeoutMs: number = CHILD_SIGTERM_GRACE_MS,\n): Promise<void> {\n if (child.pid === undefined) {\n return;\n }\n if (child.exitCode !== null || child.signalCode !== null) {\n return;\n }\n await terminatePidOrGroup(child.pid, timeoutMs);\n}\n","import { rm } from \"node:fs/promises\";\nimport process from \"node:process\";\n\nimport { killProcessOnPort } from \"../port.js\";\nimport { matchesKey, readSessionSnapshot, removeSession } from \"../state.js\";\nimport type { ActiveSession, SessionKey } from \"../types.js\";\n\nimport { PORT_CLEANUP_DELAY_MS } from \"./constants.js\";\nimport { pruneAndCleanupOrphans } from \"./orphans.js\";\nimport { terminatePidOrGroup } from \"./processes.js\";\n\nexport interface StopOptions {\n readonly sessionId?: string;\n readonly key?: SessionKey;\n}\n\nexport async function stopDebugger(options: StopOptions): Promise<ActiveSession | undefined> {\n const sessions = await pruneAndCleanupOrphans();\n let target: ActiveSession | undefined;\n if (options.sessionId !== undefined) {\n target = sessions.find((s) => s.sessionId === options.sessionId);\n } else if (options.key !== undefined) {\n const key = options.key;\n target = sessions.find((s) => matchesKey(s, key));\n }\n if (target === undefined) {\n return undefined;\n }\n if (target.pid !== process.pid) {\n try {\n await terminatePidOrGroup(target.pid);\n } catch {\n // process already gone — cleanup below\n }\n }\n setTimeout(() => {\n void killProcessOnPort(target.localPort);\n }, PORT_CLEANUP_DELAY_MS);\n const removed = await removeSession(target.sessionId);\n try {\n await rm(target.cfHomeDir, { recursive: true, force: true });\n } catch {\n // best-effort\n }\n return removed ?? target;\n}\n\nexport async function stopAllDebuggers(): Promise<number> {\n const sessions = await pruneAndCleanupOrphans();\n let stopped = 0;\n for (const session of sessions) {\n const result = await stopDebugger({ sessionId: session.sessionId });\n if (result) {\n stopped += 1;\n }\n }\n return stopped;\n}\n\nexport async function listSessions(): Promise<readonly ActiveSession[]> {\n return await readSessionSnapshot();\n}\n\nexport async function getSession(key: SessionKey): Promise<ActiveSession | undefined> {\n const sessions = await readSessionSnapshot();\n return sessions.find((s) => matchesKey(s, key));\n}\n"],"mappings":"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","names":["mkdir","process","execFile","promisify","execFileAsync","mkdir","dirname","process","process","mkdir","dirname","session","process","getHostname","getHostname","process","process","process","mkdir","rm","process","process","rm"]}
|
package/package.json
CHANGED