CLICLItypeA command you run, rather than a dependency you import.See every one →What this type is →independently maintainedindependently maintainedoriginPublished by one person or a small company outside SAP. Says nothing about quality; the readings say that.See every one →

@saptools/cf-function-trace

Records how one JavaScript function's state changes, step by step, in a Node.js process running locally or on SAP BTP Cloud Foundry, and stores the run as a redacted timeline that can be queried later.

Takes one loaded file and one function selector, such as OrderService.create, and answers what that function did to its own state on a running service. record reads the source V8 has loaded through @saptools/cf-inspector, arms one breakpoint, steps through the function and stores a full first state followed by JSON Pointer patches under ~/.saptools/cf-function-trace/, where show, state and diff read it without reconnecting. Sensitive keys, credential-shaped strings and email addresses are redacted before anything is written, and a remote run needs --confirm-impact because the Node.js process is paused while state is captured. On Cloud Foundry it never enables SSH or restarts the app. Version 0.2.x traces one activation in one isolate; asynchronous child calls, several workers at once and mapping through source maps are outside it.

Facts

Latestv0.3.1ReleasedLast pushLicenceMIT

Where it sits

Lives in
Provides
DebuggingCAPDebuggingcapabilitySeeing what running code did between an input arriving and a result coming out: which code ran, what the values were at each point, and why a value ended up as it did.
Runs
Cloud Foundry
Requires
@saptools/cf-inspector@saptools/cf-inspectorartifactConnects to the Node inspector and reads the loaded source through it; the Cloud Foundry tunnel comes from the cf-debugger it resolves in turn.Node.jsNode.jsoutside the atlasRuns on Node.js 20 or newer.

Who is behind it

Đông TrầnĐông TrầnPersonMaintains this
In the atlas: maintains 18, contributes to 18
@dongitran
maintainer

What it declares

Every artifact

Type
CLI
Origin
Independently maintained
Runs on
Cloud Foundry

As a CLI

Distributed through
npm

Connections3

Install

npm add @saptools/cf-function-trace

Derived from the package name. No recipe recorded; the README has the configuration.

Where @saptools/cf-function-trace runsBy its type: a CLI is run from a terminalcallsdocumentedrequiresdocumentedrequiresYour terminal, actorYour terminalNode.js, systemNode.jssystem@saptools/cf-function-trace, module@saptools/cf-functio…@saptools/cf-inspector, module@saptools/cf-inspect…
SAPnot SAP's, or the project's ownstated in the documentdocumented only

README

quoted from dongitran/saptools under MIT, read
dongitran/saptools/packages/cf-function-trace/README.mdOn GitHub

@saptools/cf-function-trace

cf-function-trace turns one loaded JavaScript file plus one function selector into a bounded, redacted runtime timeline. It connects through @saptools/cf-inspector, resolves the exact source currently loaded by V8, arms one exact breakpoint, steps through the selected function, and stores an initial full state followed by replayable state patches.

The default store is private local state under:

~/.saptools/cf-function-trace/data/t<16-hex>/

This package is designed for AI agents and terminal automation: command output is compact JSON, the breakpoint-ready signal is machine readable, and saved runs can be queried without reconnecting to Cloud Foundry.

Installation

Install the CLI from npm:

npm install --global @saptools/cf-function-trace
cf-function-trace --help

For repository development, build and run the workspace package directly:

pnpm install
pnpm --filter @saptools/cf-function-trace... build
node packages/cf-function-trace/dist/cli.js --help

Node.js 20 or newer is required. For a Cloud Foundry target, the cf CLI must already be installed and authenticated, and SSH must be enabled for the app and space.

The tracer uses an ownership-only tunnel and passes allowSshEnableRestart: false to cf-debugger. If SSH is disabled, plan and record fail without enabling SSH or restarting the application and return the stable code SSH_NOT_ENABLED. Enabling SSH and restarting an app remain explicit operator actions outside this CLI.

The published package requires @saptools/cf-inspector@^0.6.0, which in turn resolves a compatible @saptools/cf-debugger. CI installs the function-trace tarball by itself in a clean project so this registry dependency chain is verified without workspace links.

Updates

Every command first checks npm for a newer @saptools/cf-function-trace (at most once an hour, one small request with a 2-second timeout) and, when one exists, installs that exact version with the package manager that owns the running binary and re-runs the command you typed on the new version. Both steps are announced on stderr; nothing is printed when the install is already current:

cf-function-trace: updating 0.3.0 -> 0.4.0 ...
cf-function-trace: updated to 0.4.0; re-running the command

If the install cannot complete, one stderr line gives the manual command and the command runs on the installed version; that version is not retried for a day. cf-function-trace self-update forces the check and install now; cf-function-trace self-update --check only reports.

ControlEffect
SAPTOOLS_AUTO_UPDATE=on|notify|offon (default) installs and re-runs; notify prints the manual command once per version; off never checks. Applies to every @saptools CLI.
CF_FUNCTION_TRACE_AUTO_UPDATEsame values, this CLI only; wins over the global variable
SAPTOOLS_UPDATE_INTERVAL_MINUTESminutes between checks (default 60; 0 checks on every run)
SAPTOOLS_NPM_REGISTRYregistry to check and install from (default: npm's configured registry, then npmjs)
SAPTOOLS_UPDATE_DEBUG=1explain on stderr why nothing happened

The updater switches itself off in CI (CI set), under NODE_ENV=test or NO_UPDATE_NOTIFIER, when the binary runs from a source checkout, an npm link or an npx cache, and inside the re-run itself. It never writes to stdout, never asks for input, never uses sudo, and never moves onto a prerelease. Its state lives in ~/.saptools/updates/.

Quick start

First start record. It writes {"event":"breakpoint-armed"} to stderr only after the exact breakpoint is active. Trigger the application request that invokes the function after receiving that event; the CLI then writes its final run summary to stdout.

Local inspector:

cf-function-trace plan file:///srv/app/dist/orders.js OrderService.create \
  --port 9229 --call-depth 0

cf-function-trace record file:///srv/app/dist/orders.js OrderService.create \
  --port 9229 --call-depth 0

SAP BTP Cloud Foundry:

cf-function-trace record file:///home/vcap/app/dist/orders.js OrderService.create \
  --region eu10 \
  --org my-org \
  --space dev \
  --app orders-srv \
  --process web \
  --instance 0 \
  --tunnel-port 24321 \
  --confirm-impact

Remote tracing pauses the selected Node.js process while state is captured. The command therefore requires --confirm-impact — plan included, even though plan itself never pauses anything, since opening the remote tunnel at all requires the same confirmation. When an app instance runs more than one Node.js process (for example a npm start launcher alongside the app server), the tunnel auto-selects the process that owns the app's $PORT listening socket — the HTTP server — so --node-pid is usually unnecessary. Pass --node-pid <pid> only to override that choice, or when no single Node process listens on $PORT (in which case selection stays ambiguous and fails closed). --tunnel-port is optional and selects a preferred local CF SSH-forwarding port; omit it to keep cf-debugger's normal automatic allocation.

Non-interactive recording

record writes {"event":"breakpoint-armed"} to stderr the instant the entry breakpoint is live, then blocks on stdout until the traced function actually returns (or a limit ends the run). An agent or script cannot run it in the foreground and then trigger the request afterward — it must arm record in the background, wait for that stderr line, trigger the request, and only then collect the result:

# 1. Start record in the background; redirect stdout (final summary) and
#    stderr (the armed signal) to files.
cf-function-trace record file:///home/vcap/app/dist/orders.js OrderService.create \
  --app orders-srv --region eu10 --org my-org --space dev \
  --confirm-impact --call-depth 0 \
  >/tmp/cf-trace.out 2>/tmp/cf-trace.err &
disown

# 2. Wait for the armed signal. Poll the file itself so this still works even
#    if the next step runs as a separate shell invocation.
until grep -q '"event":"breakpoint-armed"' /tmp/cf-trace.err 2>/dev/null; do
  sleep 0.5
done

# 3. Trigger the real request now that the breakpoint is live.
curl -s -X POST https://orders-srv.example.com/orders -d '{"tempID":"abc"}'

# 4. Wait for record to finish -- its stdout file goes from empty to one JSON line.
until [ -s /tmp/cf-trace.out ]; do
  sleep 0.5
done
RUN_ID=$(node -e 'console.log(JSON.parse(require("fs").readFileSync("/tmp/cf-trace.out","utf8")).runId)')

# 5. Collect.
cf-function-trace show "$RUN_ID" --changes-only

If every step above runs inside one shell invocation, ordinary job control also works (RECORD_PID=$! then wait "$RECORD_PID"). Prefer the file-polling form when steps run as separate tool calls or a fresh shell each time: job control does not survive that, but the redirected files do. plan never needs this recipe — it resolves the same file/selector pair and exits immediately without arming a breakpoint.

Stopping a backgrounded record

Stop a backgrounded record with SIGTERM, never SIGKILL:

kill -TERM "$RECORD_PID"
# never: kill -9 / kill -KILL

record installs a process-wide guard so SIGINT, SIGTERM, an uncaught exception, or an unhandled rejection all resume the paused application and close the owned SSH tunnel before the process exits. SIGTERM gets a graceful window first; a second signal, or a fatal error, escalates straight to that same guarded cleanup. SIGKILL cannot be caught by any process, so none of this runs: a SIGKILL'd record can leave the target app paused at the breakpoint and its cf ssh tunnel orphaned (the child process outlives the killed parent and keeps the local port and SSH session open), freezing the app until that tunnel is cleared.

If a SIGKILL already happened, the tunnel is registered in the same session store cf-debugger itself uses: run cf-debugger list to confirm the orphaned session, then cf-debugger stop <app> to terminate it safely. If ownership cannot be verified, run cf-debugger doctor for the recorded PID, port, and recovery reason. cf-debugger stop <app> --force can then remove the unrecoverable record without signalling an unverified process. A tunnel left behind after that must be handled manually only after independently confirming that the reported PID is the exact cf ssh process owning the reported port; never kill a process merely because it matches a name or occupies the port.

Query a saved run

cf-function-trace show latest --changes-only --from 0 --limit 100
cf-function-trace state latest --at 17 --path /frames/0
cf-function-trace diff latest --from 9 --to 17 --path /frames/0
cf-function-trace runs --limit 20
cf-function-trace purge t0123456789abcdef

show gives a byte-bounded event page with hasMore and nextSeq; pass nextSeq back through --from to continue without loading the full timeline. state requires an exact event sequence, then verifies event kind, artifact kind, replay chain, and state hash. diff compares any two exact reconstructed states. purge accepts only a complete validated run ID.

Output kinds

Event kind: baseline (first pause, full state), pause (a later step), exception (an exception pause), completed (normal terminal event), truncated (terminal event when a limit such as --max-steps cut the run short).

Event artifactKind: full (a complete state snapshot backs this event), patch (a JSON Pointer diff from the previous state backs it), unchanged (state hash identical to the previous event).

Per-node/frame completeness: complete, truncated (a limit cut it off), unavailable (not captured — a getter, or an internal-slot value such as a Map/Promise/Proxy), error (capturing it failed).

Tagged non-JSON values carry their own kind: undefined, bigint, special-number (NaN/Infinity), symbol, accessor (getter/setter, never invoked), unavailable, ref (a back-reference to an already-captured node — cycles and aliases), redacted (a sensitive key or a matched string such as an email address).

Truncated output

show degrades event detail before ever dropping an event outright: it caps each event's changedPaths list first, then drops to a compact per-event form (function name/depth/line/column, no stateHash), then shrinks the page itself — so a run's terminal event is the last thing to disappear under a tight --max-output-bytes, never the first.

state and diff each hold one arbitrary JSON payload, not a list, so neither can shrink piece by piece. An over-budget response instead degrades to the request envelope (runId/seq or runId/from/to, plus path if one was given) with truncated: true, originalBytes, and a hint string describing exactly how to get a response that fits — never a bare, content-free stub. For example:

{"runId":"t0123456789abcdef","seq":11,"truncated":true,"originalBytes":124637,"hint":"Exceeds --max-output-bytes=300; narrow with --path, or raise --max-output-bytes to see the full result."}

To see the data: narrow with --path <json-pointer>, or raise --max-output-bytes (default 24000, maximum 1000000).

Trace semantics

  • --call-depth 0 captures only the selected function.
  • --call-depth 1 or 2 can follow synchronous child calls whose loaded files are inside the runtime app root.
  • Node internals and files below node_modules are stepped out of and never captured.
  • Async functions and functions containing await are traced with V8's async-aware stepping: the step loop stays bound to the one selected activation across each await. Between step boundaries the isolate runs (so the awaited I/O settles) rather than staying frozen, and unrelated pauses that occur during that window are released and skipped. Give a busy target --match to pin the exact activation (see below). --max-paused-ms still bounds only real paused time, so raise --timeout when the traced function awaits slow I/O.
  • The function selector must be unique. Supported selectors include declarations, arrow/function expressions, class methods (including decorator-compiled class expressions such as let X = class X {...}), constructors, accessors, object methods, and simple assigned methods.
  • Exact loaded URLs and absolute paths are preferred. A relative path is accepted only when it has one unique suffix match below the verified app root.
  • record selects the first function entry hit after breakpoint-armed. Pass --match <expr> to arm a conditional entry breakpoint so only an activation whose entry frame satisfies the JavaScript expression is traced — for example --match 'req.data.tempID === "abc"'. V8 evaluates the condition in-process, so concurrent activations that do not match never pause the target. Without --match, production callers should quiesce unrelated traffic because the first hit cannot otherwise be proven to be the intended request.

The implementation reads source with the Chrome DevTools Protocol rather than depending on cf-explorer: inspector source is the exact code V8 is executing. cf-explorer remains useful for human discovery and is a possible future source-map fallback, but it is not required for a trace.

Storage and diff model

The first pause is a full redacted state. Changed pauses use deterministic JSON Pointer patches; arrays are compared by stable prefix/suffix and index so one local-variable change does not replace the whole frame list. New variables and frames use full add values. Unchanged pauses retain a small event referencing the same state hash. A full checkpoint is written periodically and at a normal trace end. The terminal event is named completed: its state is the last confirmed pause, not a newly captured JavaScript return value.

data/t<run-id>/
  manifest.json
  events/000000.json
  events/000001.json
  states/000000.full.json
  states/000001.patch.json

Directories use mode 0700, files use 0600, and writes are atomic. Runs expire after 24 hours by default, each run is capped at 64 MiB, and the store keeps at most 100 runs unless the library API configures retentionMs, maxRunBytes, or maxRuns differently. Expired runs are pruned when a new run or an offline query command starts; there is no background deletion daemon. Event and state sequence files are append-only, and readers cross-check every event/artifact pair before replay. The manifest includes sourceHash, a SHA-256 digest of the exact source returned by V8, so a saved run can be distinguished from the same URL after another deployment.

Safety limits

Important bounds have conservative defaults and strict upper limits:

--timeout <seconds>
--max-steps <count>
--max-paused-ms <milliseconds>
--checkpoint-every <count>
--max-object-depth <depth>
--max-root-vars <count>
--max-properties <count>
--max-nodes <count>
--max-state-bytes <bytes>
--async-stack-depth <count>

--match <expr> scopes the trace to one activation (see Trace semantics). --async-stack-depth sets the async call-stack depth requested for async traces so resumed frames carry async stack context; it is reset when the trace ends. --max-root-vars bounds how many top-level locals/this/return a frame may show at all; --max-properties separately bounds how many properties an individual captured object may show (its fan-out), so raising visibility into a function's own scope does not also fatten every nested object.

Capture reads property descriptors without invoking getters. Cycles and aliases become graph references; accessors, special numbers, bigint, unavailable values, and truncated values use tagged records. Maps, sets, promises, dates, proxies, and other internal-slot values are explicitly marked truncated or unavailable rather than falsely reported as complete. A captured function value's description is hard-capped in length regardless of the remaining byte budget, since V8 reports a function's full source text there. Sensitive keys, common authorization/token/certificate/credential string forms, and email addresses are redacted before hashing, diffing, temporary writes, persistent writes, or process output; set CF_FUNCTION_TRACE_SENSITIVE_KEYS (comma-separated key names) to redact additional project-specific PII fields. There is deliberately no raw-data switch.

Capture, persistence, and stepping are watchdog-bounded by both the overall and cumulative pause deadlines. After the first hit, the one-shot entry breakpoint is removed before tracing continues. On completion or failure, cleanup disables exception pauses before sending the final resume, then closes the inspector and the owned Cloud Foundry tunnel. If both tracing and cleanup fail, the CLI reports CLEANUP_FAILED and explicitly says when resume could not be confirmed. An unreachable inspector cannot provide an absolute real-time resume guarantee, so production tracing still requires explicit impact confirmation and conservative limits. Session disposal preserves the primary operation failure together with inspector and tunnel cleanup failures instead of allowing a later finally error to hide an earlier cause.

Errors

Every command prints one JSON object: a result on stdout, or {"error":{"code":...,"message":...}} on stderr with exit code 2 (usage or data errors, including Commander's own parse errors such as an unknown flag or a missing required option) or 1 (an unexpected failure). A bare invocation with no arguments prints the command list to stdout and still exits 1; nothing is ever silent.

  • INVALID_RUN_ID: the run selector itself is malformed (not latest, not t followed by 16 lowercase hex characters) — checked before any lookup is attempted.
  • RUN_NOT_FOUND: the ID is well-formed but no run exists at that ID (expired, purged, or never created).
  • AMBIGUOUS_FUNCTION: the selector matches more than one function in the file. The error's candidates array lists each match's exact selector (use it verbatim to retry), kind, and startLine/endLine, instead of leaving the caller to guess a more specific name.
  • SESSION_ALREADY_RUNNING: a tunnel already exists for this exact app/process/instance, thrown before any run is created. Run cf-debugger list to see it, then cf-debugger stop <app> before retrying.
  • Partial run on MAX_PAUSED_TIME or TRACE_TIMEOUT: real, recoverable data is still on disk. The thrown error carries runId and directory fields alongside code/message, so the run can still be shown, stated, and diffed without a separate runs lookup. Hitting --max-steps is not an error at all: record exits normally with "status":"partial" in its stdout summary.
  • CLEANUP_FAILED: both the traced operation and its cleanup failed; the message says whether resume could not be confirmed. Treat the target app as possibly still paused until verified otherwise.

See "Stopping a backgrounded record" above for the SIGTERM-not-SIGKILL rule when a record needs to be stopped early instead of failing on its own.

Library API

The package exports the planner, controller ports, inspector adapter, state capture/diff helpers, run reader/store, recorder, and local/Cloud Foundry session lifecycle. Public locations use the Chrome DevTools Protocol's zero-based line and column convention; CLI plan output is one-based for terminal readability.

Current boundary

Version 0.2.x traces one selected activation — synchronous or asynchronous — in one Node.js isolate. Async support uses V8's async-aware stepping and stays bound to the selected activation across await; because settling awaited I/O requires briefly resuming the shared isolate, an async trace is an interleaved sequence of frozen snapshots rather than one atomic frozen window, and unrelated production work runs during each await gap. Following --call-depth into asynchronous child calls, for await ... of loops, tracing multiple workers at once, generating traffic, and mapping bundled code back through source maps are still out of scope. It also cannot cancel every already-running filesystem or CDP promise after a watchdog fires. Choose a target/worker explicitly where needed, prefer --match on busy targets, and trigger the relevant request only after the armed event.

No live Cloud Foundry environment is used by the automated tests. The local E2E suite drives a real Node.js inspector and covers normal, exception, and termination paths. The fake-CF suite verifies remote selector forwarding and cleanup without credentials or SAP network access.

MIT, read

History

Latest

v0.3.1

Other 1

Notes
  • Accept the self-updating releases of @saptools/cf-inspector as dependencies (range widened, no behaviour change).
  1. v0.3.0

    Other 1

    Notes
    • Self-updating. Every command now checks npm for a newer @saptools/cf-function-trace (at most once an hour) and, when one exists, installs that exact version and re-runs the command on it, announcing both steps on stderr. SAPTOOLS_AUTO_UPDATE=on|notify|off controls it (see the README's Updates section); it is off by itself in CI, in tests, from a source checkout and inside the re-run. New cf-function-trace self-update [--check] forces the check and install now. --version now reads package.json at runtime. Both come from the private, build-time-bundled @saptools/core package; the published tarball gains no runtime dependency.
  2. v0.2.7

    Fixes 1

    Notes
    Fix
    • Revert the per-capture getProperties prefetch that shipped in 0.2.6. It could leave a warmed request in flight when its object was released, so the WebSocket handle kept the CLI process alive and a trace could fail to exit (surfaced as a flaky runtime-e2e timeout on the CI runners). The machinery-shallow capture default from 0.2.6 is unchanged and remains the performance fix.
  3. v0.2.6

    Performance 1

    Notes
    Performance
    • Capture "machinery" (framework/behavioral) objects shallowly by default, so a step no longer descends the entire service graph. An object V8 gives a constructed className other than Object/Array — the CAP service instance (this), a cds.Request (req), loggers, services — is now walked only --machinery-depth levels (default 1) instead of the full --max-object-depth, whether it is a root or nested. Plain Object/Array data (the request payload and user locals) is still captured to --max-object-depth. Previously this alone expanded to ~570 nodes per step, the main driver of slow, timeout-prone captures. Raise --machinery-depth (with --max-object-depth) to recover full framework visibility on demand, so nothing is permanently lost. Every step is still captured fresh, so values are never stale.
  4. v0.2.4

    Other 1

    Notes
    Diff granularity
    • Node ids are now derived from each value's path in the captured graph instead of a per-capture discovery-order counter, so the same logical object keeps a stable id across steps. This removes the spurious node churn that made a baseline-anchored diff (from the first captured step to a later one) collapse into a single whole-frame replace; those diffs are now granular per changed leaf, and a variable that never changed no longer appears in the diff.
  5. v0.2.3

    Fixes 4, Docs 1, Other 5

    Notes
    Capture and diff correctness
    • Diffs no longer collapse to a whole-frame replace. The incomplete-node removal safety net in the differ is now scoped to direct-child removals, so a truncated node no longer forces its entire subtree to re-emit. Real captures now produce granular per-step operations instead of one large replace blob.
    • The capture budget is spent on the data that matters. Roots are captured in priority order (return value, own parameters and locals, block locals, this) with a per-root ceiling, so the request payload and user locals are no longer starved to node-limit by framework objects such as loggers and the CAP service graph.
    • Function description values are hard-capped instead of dumping full function source into every step, cutting capture size and the repeated per-step serialization behind mid-trace timeouts.
    • Split the overloaded --max-properties into --max-root-vars (root and local count) and --max-properties (per-object fan-out).
    CLI error contract
    • A mistyped or missing flag now emits a structured JSON error on stderr with a non-zero exit, instead of exiting silently with no output.
    • state and diff responses that exceed --max-output-bytes now preserve the request envelope and include a hint (narrow with --path, or raise --max-output-bytes) instead of returning a content-free stub.
    • Errors are actionable: parse errors, AMBIGUOUS_FUNCTION candidate lists, the operational SESSION_ALREADY_RUNNING guidance, and RUN_NOT_FOUND (with no leaked filesystem paths) are surfaced.
    • A record that times out now attaches the partial run id to the error so the captured partial timeline can still be read.
    Privacy
    • Redaction now covers email and PII patterns and an optional CF_FUNCTION_TRACE_SENSITIVE_KEYS key list, in addition to credential-shaped secrets. Note: because redaction also runs during replay-hash verification, trace runs captured before 0.2.3 can report a state hash mismatch when read; re-record to get a clean run.
    Documentation
    • Added a cf-function-trace skill and expanded the README with the non-interactive record recipe, the output and error reference, and the flag reference. A backgrounded record must be stopped with SIGTERM, never SIGKILL, until the debugger self-heals an orphaned tunnel.
  6. v0.2.2

    Other 1

    Notes
    Reliability
    • Guarantee the target app resumes and the SSH tunnel is torn down on every termination path. An uncaught exception or rejection (for example during a large state capture) previously bypassed cleanup, and a signal-driven stop had only implicit protection — either could leave V8 paused and the cf ssh tunnel orphaned, freezing the app until the tunnel was killed by hand. A process-wide resource guard now releases the tunnel, inspector session, and debugger state on SIGINT, SIGTERM, uncaughtException, and unhandledRejection, with a bounded forced-exit fallback and immediate escalation on a repeated signal.
  7. v0.2.1

    Other 1

    Notes
    • Require @saptools/cf-inspector@^0.6.2 (which pulls @saptools/cf-debugger@^0.1.16) so the async-stack-depth API and app-port Node auto-selection resolve from the published dependency chain.
  8. v0.2.0

    Other 6

    Notes
    Async function tracing
    • Trace async functions and functions containing await: V8's async-aware stepping stays bound to the one selected activation across each await, capturing and diffing the same per-step state timeline as synchronous traces. Unrelated pauses during an await gap are released and skipped.
    • Select methods of decorator-compiled class expressions (let X = class X {...}), so decorated CAP and NestJS handlers resolve.
    • Add --match <expr>, a conditional entry breakpoint that traces only an activation whose entry frame satisfies the expression, correlating one request amid concurrent traffic (sync and async).
    • Add --async-stack-depth <count> and request async call-stack depth for async traces.
    • Auto-select the app $PORT Node process through the tunnel, so --node-pid is usually unnecessary when an instance runs several Node processes.
    • Raise the --max-paused-ms ceiling to 600000 for deeper remote captures.
  9. v0.1.1

    Other 4

    Notes
    • Require the registry-compatible @saptools/cf-inspector@^0.6.0 dependency chain.
    • Add GitHub Actions trusted publishing after quality and Node.js 20/22/24 runtime gates.
    • Verify the publish candidate alone in a clean consumer so dependencies resolve from npm.
    • Replace workspace-only installation documentation with the public npm workflow.
  10. v0.1.0

    Other 13

    Notes
    • Add local and explicitly confirmed SAP BTP Cloud Foundry inspector targets.
    • Resolve exact loaded scripts and uniquely selected synchronous functions.
    • Trace app-owned call depth 0–2 with bounded stepping, one-shot entry breakpoints, and ownership-aware cleanup.
    • Capture side-effect-free tagged object graphs with stable scope identities, strict serialized-state bounds, and redaction before persistence.
    • Store full checkpoints, granular array/object patches, source/state hashes, append-only events, exact replay validation, query-triggered retention, per-run quotas, and private modes.
    • Record exception pauses distinctly and watchdog capture, persistence, stepping, and cleanup.
    • Add compact plan, record, paginated show, exact state, diff, runs, and purge commands.
    • Mark internal-slot values incomplete, expand credential redaction, and name the copied terminal checkpoint completed instead of incorrectly claiming a captured return value.
    • Prevent remote tracing from enabling SSH or restarting an app implicitly.
    • Return a stable, redacted SSH_NOT_ENABLED error and aggregate operation, inspector, and tunnel cleanup failures without masking earlier causes.
    • Add an optional validated --tunnel-port that forwards to the debugger's existing preferred-port API without changing default port allocation.
    • Add unit coverage, normal/failure local-inspector and fake-CF E2E coverage, and Node.js 20/22/24 CI.
    • Verify the package with coordinated debugger, inspector, and function-trace tarballs.
1downloads per week, npm
Weekly downloads for @saptools/cf-function-trace26 complete calendar weeks from 2026-04-27 to 2026-10-25; the latest total is 1.27 Apr – 3 May: 04 May – 10 May: 011 May – 17 May: 018 May – 24 May: 025 May – 31 May: 01 Jun – 7 Jun: 08 Jun – 14 Jun: 015 Jun – 21 Jun: 022 Jun – 28 Jun: 029 Jun – 5 Jul: 06 Jul – 12 Jul: 013 Jul – 19 Jul: 020 Jul – 26 Jul: 1,25027 Jul – 2 Aug: 433 Aug – 9 Aug: 3410 Aug – 16 Aug: 4217 Aug – 23 Aug: 4024 Aug – 30 Aug: 1331 Aug – 6 Sept: 2557 Sept – 13 Sept: 2614 Sept – 20 Sept: 2321 Sept – 27 Sept: 2228 Sept – 4 Oct: 95 Oct – 11 Oct: 312 Oct – 18 Oct: 1319 Oct – 25 Oct: 1
Debugging
  1. @saptools/cf-debuggerNode.js inspector tunnel over cf ssh to Cloud Foundry apps
  2. @saptools/cf-explorerFile search in apps deployed to SAP BTP Cloud Foundry over cf ssh
  3. @saptools/cf-hanaSQL runner against the SAP HANA Cloud database bound to a CF app
  4. @saptools/cf-inspectorShell debugger for remote Node.js over the Chrome DevTools Protocol
  5. @saptools/cf-live-traceHTTP trace hook streaming a running CF Node.js app's traffic

Details

Sources4
Added by handdiscovered herechecked
npm registry, @saptools/cf-function-traceversion, releases, installschecked
github.com/git+https://github.com/dongitran/saptools.gittree, pushes, contributors, CIchecked
packages/cf-function-trace/README.mdquoted, and what the entry declareschecked

Sources are graph entities: the same page may back several entries, and an unreachable source flags every row that rests on it.

Compatibility and provenance
deploymentCloud Foundry
latestv0.3.1
first release
releases and cadence11, ~0d median
discovered viaAdded by hand
first seen in the atlas
ecosystem and domainbtp, developer-experience
History in the atlas1
  1. First seen.
For your agent
Ref
artifact:saptools-cf-function-trace
JSON
/artifacts/saptools-cf-function-trace.json
MCP
Connecting an agent