@saptools/cf-debugger 0.1.12 → 0.1.14

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/dist/index.js CHANGED
@@ -478,26 +478,29 @@ async function isPortFree(port) {
478
478
  server.listen(port, "127.0.0.1");
479
479
  });
480
480
  }
481
+ async function isPortListening(port, timeoutMs = 200) {
482
+ return await new Promise((resolve) => {
483
+ const socket = createConnection({ port, host: "127.0.0.1" });
484
+ socket.setTimeout(timeoutMs);
485
+ socket.once("connect", () => {
486
+ socket.destroy();
487
+ resolve(true);
488
+ });
489
+ socket.once("error", () => {
490
+ socket.destroy();
491
+ resolve(false);
492
+ });
493
+ socket.once("timeout", () => {
494
+ socket.destroy();
495
+ resolve(false);
496
+ });
497
+ });
498
+ }
481
499
  async function probeTunnelReady(port, timeoutMs) {
482
500
  const pollIntervalMs = 250;
483
501
  const started = Date.now();
484
502
  while (Date.now() - started < timeoutMs) {
485
- const connected = await new Promise((resolve) => {
486
- const socket = createConnection({ port, host: "127.0.0.1" });
487
- socket.setTimeout(200);
488
- socket.once("connect", () => {
489
- socket.destroy();
490
- resolve(true);
491
- });
492
- socket.once("error", () => {
493
- socket.destroy();
494
- resolve(false);
495
- });
496
- socket.once("timeout", () => {
497
- socket.destroy();
498
- resolve(false);
499
- });
500
- });
503
+ const connected = await isPortListening(port);
501
504
  if (connected) {
502
505
  return true;
503
506
  }
@@ -649,14 +652,31 @@ function isPidAlive(pid) {
649
652
  return true;
650
653
  }
651
654
  }
652
- function filterStaleSessions(sessions) {
655
+ async function isSessionHealthy(session, host) {
656
+ if (session.hostname !== host) {
657
+ return true;
658
+ }
659
+ if (!isPidAlive(session.pid)) {
660
+ return false;
661
+ }
662
+ if (session.status !== "ready") {
663
+ return true;
664
+ }
665
+ if (!await isPortListening(session.localPort)) {
666
+ return false;
667
+ }
668
+ const listeningPid = await findListeningProcessId(session.localPort);
669
+ return listeningPid === void 0 || listeningPid === session.pid;
670
+ }
671
+ async function filterStaleSessions(sessions) {
653
672
  const host = getHostname();
654
- return sessions.filter((session) => {
655
- if (session.hostname !== host) {
656
- return true;
657
- }
658
- return isPidAlive(session.pid);
659
- });
673
+ const checks = await Promise.all(
674
+ sessions.map(async (session) => [
675
+ session,
676
+ await isSessionHealthy(session, host)
677
+ ])
678
+ );
679
+ return checks.filter(([, healthy]) => healthy).map(([session]) => session);
660
680
  }
661
681
  async function readStateRaw() {
662
682
  const parsed = await readJsonFile(stateFilePath());
@@ -670,7 +690,7 @@ async function writeState(state) {
670
690
  }
671
691
  async function readAndPruneLocked() {
672
692
  const raw = await readStateRaw();
673
- const pruned = filterStaleSessions(raw.sessions);
693
+ const pruned = await filterStaleSessions(raw.sessions);
674
694
  const removed = raw.sessions.filter(
675
695
  (session) => !pruned.some((active) => active.sessionId === session.sessionId)
676
696
  );
@@ -679,11 +699,9 @@ async function readAndPruneLocked() {
679
699
  }
680
700
  return { sessions: pruned, removed };
681
701
  }
682
- async function readSessionSnapshot() {
683
- return await withFileLock(stateLockPath(), async () => {
684
- const raw = await readStateRaw();
685
- return raw.sessions;
686
- });
702
+ async function readActiveSessions() {
703
+ const result = await withFileLock(stateLockPath(), readAndPruneLocked);
704
+ return result.sessions;
687
705
  }
688
706
  async function readAndPruneActiveSessions() {
689
707
  return await withFileLock(stateLockPath(), readAndPruneLocked);
@@ -854,7 +872,7 @@ async function pruneAndCleanupOrphans() {
854
872
  void killProcessOnPort(removed.localPort);
855
873
  }
856
874
  }
857
- return result.sessions;
875
+ return result;
858
876
  }
859
877
 
860
878
  // src/debug-session/processes.ts
@@ -1154,19 +1172,18 @@ async function cleanupFilesystem(cfHomeDir) {
1154
1172
  // src/debug-session/sessions.ts
1155
1173
  import { rm as rm2 } from "fs/promises";
1156
1174
  import process5 from "process";
1157
- async function stopDebugger(options) {
1158
- const sessions = await pruneAndCleanupOrphans();
1159
- let target;
1175
+ function findMatchingSession(sessions, options) {
1160
1176
  if (options.sessionId !== void 0) {
1161
- target = sessions.find((s) => s.sessionId === options.sessionId);
1162
- } else if (options.key !== void 0) {
1163
- const key = options.key;
1164
- target = sessions.find((s) => matchesKey(s, key));
1177
+ return sessions.find((s) => s.sessionId === options.sessionId);
1165
1178
  }
1166
- if (target === void 0) {
1167
- return void 0;
1179
+ if (options.key !== void 0) {
1180
+ const key = options.key;
1181
+ return sessions.find((s) => matchesKey(s, key));
1168
1182
  }
1169
- if (target.pid !== process5.pid) {
1183
+ return void 0;
1184
+ }
1185
+ async function cleanupSession(target, stale) {
1186
+ if (!stale && target.pid !== process5.pid) {
1170
1187
  try {
1171
1188
  await terminatePidOrGroup(target.pid);
1172
1189
  } catch {
@@ -1175,17 +1192,29 @@ async function stopDebugger(options) {
1175
1192
  setTimeout(() => {
1176
1193
  void killProcessOnPort(target.localPort);
1177
1194
  }, PORT_CLEANUP_DELAY_MS);
1178
- const removed = await removeSession(target.sessionId);
1195
+ const removed = stale ? void 0 : await removeSession(target.sessionId);
1179
1196
  try {
1180
1197
  await rm2(target.cfHomeDir, { recursive: true, force: true });
1181
1198
  } catch {
1182
1199
  }
1183
- return removed ?? target;
1200
+ return { ...removed ?? target, stale };
1201
+ }
1202
+ async function stopDebugger(options) {
1203
+ const pruneResult = await pruneAndCleanupOrphans();
1204
+ const activeTarget = findMatchingSession(pruneResult.sessions, options);
1205
+ if (activeTarget !== void 0) {
1206
+ return await cleanupSession(activeTarget, false);
1207
+ }
1208
+ const staleTarget = findMatchingSession(pruneResult.removed, options);
1209
+ if (staleTarget !== void 0) {
1210
+ return await cleanupSession(staleTarget, true);
1211
+ }
1212
+ return void 0;
1184
1213
  }
1185
1214
  async function stopAllDebuggers() {
1186
- const sessions = await pruneAndCleanupOrphans();
1187
- let stopped = 0;
1188
- for (const session of sessions) {
1215
+ const pruneResult = await pruneAndCleanupOrphans();
1216
+ let stopped = pruneResult.removed.length;
1217
+ for (const session of pruneResult.sessions) {
1189
1218
  const result = await stopDebugger({ sessionId: session.sessionId });
1190
1219
  if (result) {
1191
1220
  stopped += 1;
@@ -1194,10 +1223,10 @@ async function stopAllDebuggers() {
1194
1223
  return stopped;
1195
1224
  }
1196
1225
  async function listSessions() {
1197
- return await readSessionSnapshot();
1226
+ return await readActiveSessions();
1198
1227
  }
1199
1228
  async function getSession(key) {
1200
- const sessions = await readSessionSnapshot();
1229
+ const sessions = await readActiveSessions();
1201
1230
  return sessions.find((s) => matchesKey(s, key));
1202
1231
  }
1203
1232
  export {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/debug-session/start.ts","../src/cloud-foundry/commands.ts","../src/regions.ts","../src/cloud-foundry/execute.ts","../src/cloud-foundry/ssh.ts","../src/paths.ts","../src/network/ports.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 { listKnownRegionKeys, resolveApiEndpoint } from \"../regions.js\";\nimport { CfDebuggerError } from \"../types.js\";\n\nimport { type CfExecContext, runCf } from \"./execute.js\";\nimport { parseAppNames, parseNameTable } from \"./parsers.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst CF_AUTH_MAX_ATTEMPTS = 3;\nconst CURRENT_TARGET_TIMEOUT_MS = 30_000;\n\nexport interface CurrentCfTargetReadOptions {\n readonly command?: string;\n readonly env?: NodeJS.ProcessEnv;\n readonly timeoutMs?: number;\n}\n\nexport interface CurrentCfTarget {\n readonly apiEndpoint: string;\n readonly region?: string;\n readonly org: string;\n readonly space: string;\n}\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\nexport async function readCurrentCfTarget(\n options: CurrentCfTargetReadOptions = {},\n): Promise<CurrentCfTarget | undefined> {\n try {\n const { stdout } = await execFileAsync(options.command ?? process.env[\"CF_DEBUGGER_CF_BIN\"] ?? \"cf\", [\"target\"], {\n env: options.env ?? process.env,\n maxBuffer: 16 * 1024 * 1024,\n timeout: options.timeoutMs ?? CURRENT_TARGET_TIMEOUT_MS,\n });\n return parseCurrentCfTarget(stdout);\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n throw new CfDebuggerError(\"CF_TARGET_FAILED\", `cf target failed: ${message}`);\n }\n}\n\nexport function parseCurrentCfTarget(stdout: string): CurrentCfTarget | undefined {\n const fields = parseTargetFields(stdout);\n const apiEndpoint = fields.get(\"api endpoint\");\n const org = fields.get(\"org\");\n const space = fields.get(\"space\");\n if (!isPresent(apiEndpoint) || !isPresent(org) || !isPresent(space)) {\n return undefined;\n }\n\n const region = regionKeyForApiEndpoint(apiEndpoint);\n return {\n apiEndpoint,\n ...(region === undefined ? {} : { region }),\n org,\n space,\n };\n}\n\nexport function requireCurrentCfRegion(\n target: Pick<CurrentCfTarget, \"apiEndpoint\" | \"region\">,\n instruction = \"Pass --region explicitly.\",\n): string {\n if (target.region !== undefined) {\n return target.region;\n }\n throw new CfDebuggerError(\n \"CF_TARGET_FAILED\",\n `Current CF API endpoint \"${target.apiEndpoint}\" does not match a known SAP region. ${instruction}`,\n );\n}\n\nfunction parseTargetFields(stdout: string): Map<string, string> {\n return new Map(\n stdout\n .split(\"\\n\")\n .map((line): readonly [string, string] | undefined => {\n const separator = line.indexOf(\":\");\n if (separator < 0) {\n return undefined;\n }\n return [\n line.slice(0, separator).trim().toLowerCase(),\n line.slice(separator + 1).trim(),\n ];\n })\n .filter((field): field is readonly [string, string] => field !== undefined),\n );\n}\n\nfunction regionKeyForApiEndpoint(apiEndpoint: string): string | undefined {\n const normalized = normalizeApiEndpoint(apiEndpoint);\n const known = listKnownRegionKeys().find((key) => normalizeApiEndpoint(resolveApiEndpoint(key)) === normalized);\n if (known !== undefined) {\n return known;\n }\n return regionKeyFromSapApiEndpoint(normalized);\n}\n\nfunction normalizeApiEndpoint(apiEndpoint: string): string {\n return apiEndpoint.trim().replace(/\\/+$/, \"\").toLowerCase();\n}\n\nfunction regionKeyFromSapApiEndpoint(apiEndpoint: string): string | undefined {\n const match = /^https:\\/\\/api\\.cf\\.([a-z]{2}\\d{2}(?:-\\d{3})?)\\.(?:hana\\.ondemand\\.com|platform\\.sapcloud\\.cn)$/.exec(apiEndpoint);\n return match?.[1];\n}\n\nfunction isPresent(value: string | undefined): value is string {\n return value !== undefined && value.length > 0;\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 cn20: \"https://api.cf.cn20.platform.sapcloud.cn\",\n cn40: \"https://api.cf.cn40.platform.sapcloud.cn\",\n eu01: \"https://api.cf.eu01.hana.ondemand.com\",\n eu02: \"https://api.cf.eu02.hana.ondemand.com\",\n eu10: \"https://api.cf.eu10.hana.ondemand.com\",\n \"eu10-002\": \"https://api.cf.eu10-002.hana.ondemand.com\",\n \"eu10-003\": \"https://api.cf.eu10-003.hana.ondemand.com\",\n \"eu10-004\": \"https://api.cf.eu10-004.hana.ondemand.com\",\n \"eu10-005\": \"https://api.cf.eu10-005.hana.ondemand.com\",\n eu11: \"https://api.cf.eu11.hana.ondemand.com\",\n eu13: \"https://api.cf.eu13.hana.ondemand.com\",\n eu20: \"https://api.cf.eu20.hana.ondemand.com\",\n \"eu20-001\": \"https://api.cf.eu20-001.hana.ondemand.com\",\n \"eu20-002\": \"https://api.cf.eu20-002.hana.ondemand.com\",\n eu22: \"https://api.cf.eu22.hana.ondemand.com\",\n eu30: \"https://api.cf.eu30.hana.ondemand.com\",\n il30: \"https://api.cf.il30.hana.ondemand.com\",\n in30: \"https://api.cf.in30.hana.ondemand.com\",\n jp01: \"https://api.cf.jp01.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 jp31: \"https://api.cf.jp31.hana.ondemand.com\",\n sa30: \"https://api.cf.sa30.hana.ondemand.com\",\n sa31: \"https://api.cf.sa31.hana.ondemand.com\",\n uk20: \"https://api.cf.uk20.hana.ondemand.com\",\n us01: \"https://api.cf.us01.hana.ondemand.com\",\n us02: \"https://api.cf.us02.hana.ondemand.com\",\n us10: \"https://api.cf.us10.hana.ondemand.com\",\n \"us10-001\": \"https://api.cf.us10-001.hana.ondemand.com\",\n \"us10-002\": \"https://api.cf.us10-002.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};\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 { 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 { 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 { readdir, readFile, readlink } from \"node:fs/promises\";\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 findListeningPidsWithProc(port: number): Promise<readonly number[]> {\n const inodes = await findListeningSocketInodesWithProc(port);\n if (inodes.size === 0) {\n return [];\n }\n\n try {\n const entries = await readdir(\"/proc\", { withFileTypes: true });\n const pids = new Set<number>();\n for (const entry of entries) {\n if (!entry.isDirectory() || !/^\\d+$/.test(entry.name)) {\n continue;\n }\n if (await processHasSocketInode(entry.name, inodes)) {\n pids.add(Number.parseInt(entry.name, 10));\n }\n }\n return [...pids];\n } catch {\n return [];\n }\n}\n\nasync function findListeningSocketInodesWithProc(port: number): Promise<ReadonlySet<string>> {\n const inodes = new Set<string>();\n await collectListeningSocketInodes(\"/proc/net/tcp\", port, inodes);\n await collectListeningSocketInodes(\"/proc/net/tcp6\", port, inodes);\n return inodes;\n}\n\nasync function collectListeningSocketInodes(\n path: string,\n port: number,\n inodes: Set<string>,\n): Promise<void> {\n try {\n const content = await readFile(path, \"utf8\");\n for (const line of content.split(\"\\n\").slice(1)) {\n const fields = line.trim().split(/\\s+/);\n const localAddress = fields[1];\n const state = fields[3];\n const inode = fields[9];\n if (localAddress === undefined || state === undefined || inode === undefined) {\n continue;\n }\n const localPort = Number.parseInt(localAddress.split(\":\")[1] ?? \"\", 16);\n if (localPort === port && state === \"0A\") {\n inodes.add(inode);\n }\n }\n } catch {\n // /proc/net/tcp* is Linux-specific; callers fall back to other strategies.\n }\n}\n\nasync function processHasSocketInode(pid: string, inodes: ReadonlySet<string>): Promise<boolean> {\n try {\n const descriptors = await readdir(`/proc/${pid}/fd`);\n for (const descriptor of descriptors) {\n try {\n const link = await readlink(`/proc/${pid}/fd/${descriptor}`);\n const match = /^socket:\\[(\\d+)\\]$/.exec(link);\n if (match?.[1] !== undefined && inodes.has(match[1])) {\n return true;\n }\n } catch {\n // The process may close descriptors while we scan them.\n }\n }\n } catch {\n // The process may exit, or permissions may prevent fd inspection.\n }\n return false;\n}\n\nasync function findListeningPids(port: number): Promise<readonly number[]> {\n if (process.platform === \"win32\") {\n return await findListeningPidsWithNetstat(port);\n }\n const lsofPids = await findListeningPidsWithLsof(port);\n return lsofPids.length > 0 ? lsofPids : await findListeningPidsWithProc(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","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","promisify","execFile","execFileAsync","execFile","promisify","execFileAsync","mkdir","readFile","dirname","process","readFile","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/commands.ts","../src/regions.ts","../src/cloud-foundry/execute.ts","../src/cloud-foundry/ssh.ts","../src/paths.ts","../src/network/ports.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 { listKnownRegionKeys, resolveApiEndpoint } from \"../regions.js\";\nimport { CfDebuggerError } from \"../types.js\";\n\nimport { type CfExecContext, runCf } from \"./execute.js\";\nimport { parseAppNames, parseNameTable } from \"./parsers.js\";\n\nconst execFileAsync = promisify(execFile);\n\nconst CF_AUTH_MAX_ATTEMPTS = 3;\nconst CURRENT_TARGET_TIMEOUT_MS = 30_000;\n\nexport interface CurrentCfTargetReadOptions {\n readonly command?: string;\n readonly env?: NodeJS.ProcessEnv;\n readonly timeoutMs?: number;\n}\n\nexport interface CurrentCfTarget {\n readonly apiEndpoint: string;\n readonly region?: string;\n readonly org: string;\n readonly space: string;\n}\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\nexport async function readCurrentCfTarget(\n options: CurrentCfTargetReadOptions = {},\n): Promise<CurrentCfTarget | undefined> {\n try {\n const { stdout } = await execFileAsync(options.command ?? process.env[\"CF_DEBUGGER_CF_BIN\"] ?? \"cf\", [\"target\"], {\n env: options.env ?? process.env,\n maxBuffer: 16 * 1024 * 1024,\n timeout: options.timeoutMs ?? CURRENT_TARGET_TIMEOUT_MS,\n });\n return parseCurrentCfTarget(stdout);\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n throw new CfDebuggerError(\"CF_TARGET_FAILED\", `cf target failed: ${message}`);\n }\n}\n\nexport function parseCurrentCfTarget(stdout: string): CurrentCfTarget | undefined {\n const fields = parseTargetFields(stdout);\n const apiEndpoint = fields.get(\"api endpoint\");\n const org = fields.get(\"org\");\n const space = fields.get(\"space\");\n if (!isPresent(apiEndpoint) || !isPresent(org) || !isPresent(space)) {\n return undefined;\n }\n\n const region = regionKeyForApiEndpoint(apiEndpoint);\n return {\n apiEndpoint,\n ...(region === undefined ? {} : { region }),\n org,\n space,\n };\n}\n\nexport function requireCurrentCfRegion(\n target: Pick<CurrentCfTarget, \"apiEndpoint\" | \"region\">,\n instruction = \"Pass --region explicitly.\",\n): string {\n if (target.region !== undefined) {\n return target.region;\n }\n throw new CfDebuggerError(\n \"CF_TARGET_FAILED\",\n `Current CF API endpoint \"${target.apiEndpoint}\" does not match a known SAP region. ${instruction}`,\n );\n}\n\nfunction parseTargetFields(stdout: string): Map<string, string> {\n return new Map(\n stdout\n .split(\"\\n\")\n .map((line): readonly [string, string] | undefined => {\n const separator = line.indexOf(\":\");\n if (separator < 0) {\n return undefined;\n }\n return [\n line.slice(0, separator).trim().toLowerCase(),\n line.slice(separator + 1).trim(),\n ];\n })\n .filter((field): field is readonly [string, string] => field !== undefined),\n );\n}\n\nfunction regionKeyForApiEndpoint(apiEndpoint: string): string | undefined {\n const normalized = normalizeApiEndpoint(apiEndpoint);\n const known = listKnownRegionKeys().find((key) => normalizeApiEndpoint(resolveApiEndpoint(key)) === normalized);\n if (known !== undefined) {\n return known;\n }\n return regionKeyFromSapApiEndpoint(normalized);\n}\n\nfunction normalizeApiEndpoint(apiEndpoint: string): string {\n return apiEndpoint.trim().replace(/\\/+$/, \"\").toLowerCase();\n}\n\nfunction regionKeyFromSapApiEndpoint(apiEndpoint: string): string | undefined {\n const match = /^https:\\/\\/api\\.cf\\.([a-z]{2}\\d{2}(?:-\\d{3})?)\\.(?:hana\\.ondemand\\.com|platform\\.sapcloud\\.cn)$/.exec(apiEndpoint);\n return match?.[1];\n}\n\nfunction isPresent(value: string | undefined): value is string {\n return value !== undefined && value.length > 0;\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 cn20: \"https://api.cf.cn20.platform.sapcloud.cn\",\n cn40: \"https://api.cf.cn40.platform.sapcloud.cn\",\n eu01: \"https://api.cf.eu01.hana.ondemand.com\",\n eu02: \"https://api.cf.eu02.hana.ondemand.com\",\n eu10: \"https://api.cf.eu10.hana.ondemand.com\",\n \"eu10-002\": \"https://api.cf.eu10-002.hana.ondemand.com\",\n \"eu10-003\": \"https://api.cf.eu10-003.hana.ondemand.com\",\n \"eu10-004\": \"https://api.cf.eu10-004.hana.ondemand.com\",\n \"eu10-005\": \"https://api.cf.eu10-005.hana.ondemand.com\",\n eu11: \"https://api.cf.eu11.hana.ondemand.com\",\n eu13: \"https://api.cf.eu13.hana.ondemand.com\",\n eu20: \"https://api.cf.eu20.hana.ondemand.com\",\n \"eu20-001\": \"https://api.cf.eu20-001.hana.ondemand.com\",\n \"eu20-002\": \"https://api.cf.eu20-002.hana.ondemand.com\",\n eu22: \"https://api.cf.eu22.hana.ondemand.com\",\n eu30: \"https://api.cf.eu30.hana.ondemand.com\",\n il30: \"https://api.cf.il30.hana.ondemand.com\",\n in30: \"https://api.cf.in30.hana.ondemand.com\",\n jp01: \"https://api.cf.jp01.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 jp31: \"https://api.cf.jp31.hana.ondemand.com\",\n sa30: \"https://api.cf.sa30.hana.ondemand.com\",\n sa31: \"https://api.cf.sa31.hana.ondemand.com\",\n uk20: \"https://api.cf.uk20.hana.ondemand.com\",\n us01: \"https://api.cf.us01.hana.ondemand.com\",\n us02: \"https://api.cf.us02.hana.ondemand.com\",\n us10: \"https://api.cf.us10.hana.ondemand.com\",\n \"us10-001\": \"https://api.cf.us10-001.hana.ondemand.com\",\n \"us10-002\": \"https://api.cf.us10-002.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};\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 { 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 { 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 { readdir, readFile, readlink } from \"node:fs/promises\";\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 findListeningPidsWithProc(port: number): Promise<readonly number[]> {\n const inodes = await findListeningSocketInodesWithProc(port);\n if (inodes.size === 0) {\n return [];\n }\n\n try {\n const entries = await readdir(\"/proc\", { withFileTypes: true });\n const pids = new Set<number>();\n for (const entry of entries) {\n if (!entry.isDirectory() || !/^\\d+$/.test(entry.name)) {\n continue;\n }\n if (await processHasSocketInode(entry.name, inodes)) {\n pids.add(Number.parseInt(entry.name, 10));\n }\n }\n return [...pids];\n } catch {\n return [];\n }\n}\n\nasync function findListeningSocketInodesWithProc(port: number): Promise<ReadonlySet<string>> {\n const inodes = new Set<string>();\n await collectListeningSocketInodes(\"/proc/net/tcp\", port, inodes);\n await collectListeningSocketInodes(\"/proc/net/tcp6\", port, inodes);\n return inodes;\n}\n\nasync function collectListeningSocketInodes(\n path: string,\n port: number,\n inodes: Set<string>,\n): Promise<void> {\n try {\n const content = await readFile(path, \"utf8\");\n for (const line of content.split(\"\\n\").slice(1)) {\n const fields = line.trim().split(/\\s+/);\n const localAddress = fields[1];\n const state = fields[3];\n const inode = fields[9];\n if (localAddress === undefined || state === undefined || inode === undefined) {\n continue;\n }\n const localPort = Number.parseInt(localAddress.split(\":\")[1] ?? \"\", 16);\n if (localPort === port && state === \"0A\") {\n inodes.add(inode);\n }\n }\n } catch {\n // /proc/net/tcp* is Linux-specific; callers fall back to other strategies.\n }\n}\n\nasync function processHasSocketInode(pid: string, inodes: ReadonlySet<string>): Promise<boolean> {\n try {\n const descriptors = await readdir(`/proc/${pid}/fd`);\n for (const descriptor of descriptors) {\n try {\n const link = await readlink(`/proc/${pid}/fd/${descriptor}`);\n const match = /^socket:\\[(\\d+)\\]$/.exec(link);\n if (match?.[1] !== undefined && inodes.has(match[1])) {\n return true;\n }\n } catch {\n // The process may close descriptors while we scan them.\n }\n }\n } catch {\n // The process may exit, or permissions may prevent fd inspection.\n }\n return false;\n}\n\nasync function findListeningPids(port: number): Promise<readonly number[]> {\n if (process.platform === \"win32\") {\n return await findListeningPidsWithNetstat(port);\n }\n const lsofPids = await findListeningPidsWithLsof(port);\n return lsofPids.length > 0 ? lsofPids : await findListeningPidsWithProc(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 isPortListening(port: number, timeoutMs = 200): Promise<boolean> {\n return await new Promise<boolean>((resolve) => {\n const socket = createConnection({ port, host: \"127.0.0.1\" });\n socket.setTimeout(timeoutMs);\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\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 isPortListening(port);\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","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 { findListeningProcessId, isPortListening } from \"../port.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\nasync function isSessionHealthy(session: ActiveSession, host: string): Promise<boolean> {\n if (session.hostname !== host) {\n return true;\n }\n if (!isPidAlive(session.pid)) {\n return false;\n }\n if (session.status !== \"ready\") {\n return true;\n }\n if (!(await isPortListening(session.localPort))) {\n return false;\n }\n const listeningPid = await findListeningProcessId(session.localPort);\n return listeningPid === undefined || listeningPid === session.pid;\n}\n\nasync function filterStaleSessions(\n sessions: readonly ActiveSession[],\n): Promise<readonly ActiveSession[]> {\n const host = getHostname();\n const checks = await Promise.all(\n sessions.map(async (session): Promise<readonly [ActiveSession, boolean]> => [\n session,\n await isSessionHealthy(session, host),\n ]),\n );\n return checks.filter(([, healthy]) => healthy).map(([session]) => session);\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 = await 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 interface PruneCleanupResult {\n readonly sessions: readonly ActiveSession[];\n readonly removed: readonly ActiveSession[];\n}\n\nexport async function pruneAndCleanupOrphans(): Promise<PruneCleanupResult> {\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;\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, readActiveSessions, 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 interface StopDebuggerResult extends ActiveSession {\n readonly stale: boolean;\n}\n\nfunction findMatchingSession(\n sessions: readonly ActiveSession[],\n options: StopOptions,\n): ActiveSession | undefined {\n if (options.sessionId !== undefined) {\n return sessions.find((s) => s.sessionId === options.sessionId);\n }\n if (options.key !== undefined) {\n const key = options.key;\n return sessions.find((s) => matchesKey(s, key));\n }\n return undefined;\n}\n\nasync function cleanupSession(target: ActiveSession, stale: boolean): Promise<StopDebuggerResult> {\n if (!stale && 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 = stale ? undefined : 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), stale };\n}\n\nexport async function stopDebugger(options: StopOptions): Promise<StopDebuggerResult | undefined> {\n const pruneResult = await pruneAndCleanupOrphans();\n const activeTarget = findMatchingSession(pruneResult.sessions, options);\n if (activeTarget !== undefined) {\n return await cleanupSession(activeTarget, false);\n }\n\n const staleTarget = findMatchingSession(pruneResult.removed, options);\n if (staleTarget !== undefined) {\n return await cleanupSession(staleTarget, true);\n }\n\n return undefined;\n}\n\nexport async function stopAllDebuggers(): Promise<number> {\n const pruneResult = await pruneAndCleanupOrphans();\n let stopped = pruneResult.removed.length;\n for (const session of pruneResult.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 readActiveSessions();\n}\n\nexport async function getSession(key: SessionKey): Promise<ActiveSession | undefined> {\n const sessions = await readActiveSessions();\n return sessions.find((s) => matchesKey(s, key));\n}\n"],"mappings":"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","names":["mkdir","process","execFile","promisify","execFileAsync","promisify","execFile","execFileAsync","execFile","promisify","execFileAsync","mkdir","readFile","dirname","process","readFile","process","mkdir","dirname","session","process","getHostname","getHostname","process","process","process","mkdir","rm","process","process","rm"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saptools/cf-debugger",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Open an SSH debug tunnel to a SAP BTP Cloud Foundry app's Node.js inspector — from any terminal, no IDE required.",
5
5
  "type": "module",
6
6
  "publishConfig": {