Metadata-Version: 2.4
Name: jupyterlab_kernel_terminal_workspace_culler_extension
Version: 1.0.3
Summary: Jupyterlab extension to kill unused kernels, terminals and workspaces. User can configure the idle time (minutes) after which the resource will be released automatically. This helps with the locked memory, insane number of terminals opened etc.
Project-URL: Homepage, https://github.com/stellarshenson/jupyterlab_kernel_terminal_workspace_culler_extension
Project-URL: Bug Tracker, https://github.com/stellarshenson/jupyterlab_kernel_terminal_workspace_culler_extension/issues
Project-URL: Repository, https://github.com/stellarshenson/jupyterlab_kernel_terminal_workspace_culler_extension.git
Author-email: Stellars Henson <konrad.jelen@gmail.com>
License: BSD 3-Clause License
        
        Copyright (c) 2026, Stellars Henson
        All rights reserved.
        
        Redistribution and use in source and binary forms, with or without
        modification, are permitted provided that the following conditions are met:
        
        1. Redistributions of source code must retain the above copyright notice, this
           list of conditions and the following disclaimer.
        
        2. Redistributions in binary form must reproduce the above copyright notice,
           this list of conditions and the following disclaimer in the documentation
           and/or other materials provided with the distribution.
        
        3. Neither the name of the copyright holder nor the names of its
           contributors may be used to endorse or promote products derived from
           this software without specific prior written permission.
        
        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
        AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
        DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
        FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
        SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
        CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
        OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
        OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
License-File: LICENSE
Keywords: jupyter,jupyterlab,jupyterlab-extension
Classifier: Framework :: Jupyter
Classifier: Framework :: Jupyter :: JupyterLab
Classifier: Framework :: Jupyter :: JupyterLab :: 4
Classifier: Framework :: Jupyter :: JupyterLab :: Extensions
Classifier: Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt
Classifier: License :: OSI Approved :: BSD License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.10
Requires-Dist: jupyter-server<3,>=2.4.0
Provides-Extra: dev
Requires-Dist: jupyterlab>=4; extra == 'dev'
Provides-Extra: notifications
Requires-Dist: jupyterlab-notifications-extension; extra == 'notifications'
Provides-Extra: test
Requires-Dist: coverage; extra == 'test'
Requires-Dist: pytest; extra == 'test'
Requires-Dist: pytest-asyncio; extra == 'test'
Requires-Dist: pytest-cov; extra == 'test'
Requires-Dist: pytest-jupyter[server]>=0.6.0; extra == 'test'
Description-Content-Type: text/markdown

# jupyterlab_kernel_terminal_workspace_culler_extension

[![GitHub Actions](https://github.com/stellarshenson/jupyterlab_kernel_terminal_workspace_culler_extension/actions/workflows/build.yml/badge.svg)](https://github.com/stellarshenson/jupyterlab_kernel_terminal_workspace_culler_extension/actions/workflows/build.yml)
[![npm version](https://img.shields.io/npm/v/jupyterlab_kernel_terminal_workspace_culler_extension.svg)](https://www.npmjs.com/package/jupyterlab_kernel_terminal_workspace_culler_extension)
[![PyPI version](https://img.shields.io/pypi/v/jupyterlab-kernel-terminal-workspace-culler-extension.svg)](https://pypi.org/project/jupyterlab-kernel-terminal-workspace-culler-extension/)
[![Total PyPI downloads](https://static.pepy.tech/badge/jupyterlab-kernel-terminal-workspace-culler-extension)](https://pepy.tech/project/jupyterlab-kernel-terminal-workspace-culler-extension)
[![JupyterLab 4](https://img.shields.io/badge/JupyterLab-4-orange.svg)](https://jupyterlab.readthedocs.io/en/stable/)
[![Brought To You By KOLOMOLO](https://img.shields.io/badge/Brought%20To%20You%20By-KOLOMOLO-00ffff?style=flat)](https://kolomolo.com)
[![Donate PayPal](https://img.shields.io/badge/Donate-PayPal-blue?style=flat)](https://www.paypal.com/donate/?hosted_button_id=B4KPBJDLLXTSA)

Automatically cull idle kernels, terminals, and sessions after configurable timeout periods. Helps manage system resources by cleaning up unused sessions that accumulate during long JupyterLab usage.

## Features

- **Idle kernel culling** - Shut down kernels idle beyond timeout (checks `execution_state` and `last_activity`)
- **Idle terminal culling** - Close terminals with no WebSocket activity beyond timeout
- **Session culling** - Remove stale sessions based on associated kernel activity
- **Configurable timeouts** - All timeouts adjustable via JupyterLab Settings
- **Notifications** - Optional toast notifications when resources are culled (requires `jupyterlab-notifications`)
- **Server-side detection** - Uses tornado PeriodicCallback for accurate activity tracking

## Default Settings

| Setting | Default | Description |
|---------|---------|-------------|
| Kernel timeout | 60 min (1 hour) | Idle kernels culled after this period |
| Terminal timeout | 60 min (1 hour) | Inactive terminals culled after this period |
| Session timeout | 10080 min (7 days) | Idle sessions culled after this period |
| Check interval | 5 min | How often the culler checks for idle resources |
| Notifications | enabled | Show notification when resources are culled |

## How Idle Detection Works

**Kernels**: Checked for `execution_state` (busy kernels are never culled) and `last_activity` timestamp. A kernel is idle when it's not executing and hasn't had activity beyond the timeout.

**Terminals**: Based on WebSocket activity. If the browser tab is closed, activity stops being recorded even if the terminal process is running. For long-running jobs, use `nohup`, `screen`, or `tmux`.

**Sessions**: Based on the associated kernel's `last_activity`.

> **Note**: Terminal culling sends SIGHUP to the terminal process. Processes started with `nohup` will survive culling.

## Installation

Requires JupyterLab 4.0.0 or higher.

```bash
pip install jupyterlab-kernel-terminal-workspace-culler-extension
```

## Configuration

Open JupyterLab Settings (`Settings` -> `Settings Editor`) and search for "Resource Culler" to adjust timeouts and enable/disable culling for each resource type.

## Logs

Culling actions are logged at INFO level with `[Culler]` prefix:

```
[Culler] CULLING KERNEL abc123 - idle 65.2 minutes (threshold: 60)
[Culler] Kernel abc123 culled successfully
[Culler] CULLING TERMINAL 1 - idle 62.1 minutes (threshold: 60)
[Culler] Terminal 1 culled successfully
```

Run JupyterLab with `--log-level=INFO` to see culling activity.

## FAQ

**Q: My long-running calculation was killed. How do I prevent this?**

Two options:
1. **Increase timeout**: Go to `Settings` -> `Settings Editor` -> `Resource Culler` and increase the kernel/terminal timeout
2. **Use a terminal multiplexer**: Run calculations inside `screen` or `tmux` - these survive terminal culling

```bash
# Using screen
screen -S mysession
python long_calculation.py
# Detach with Ctrl+A, D

# Using tmux
tmux new -s mysession
python long_calculation.py
# Detach with Ctrl+B, D
```

**Q: Will closing my browser tab kill my running process?**

For terminals: The terminal will be culled after the timeout since WebSocket activity stops when the browser closes. Foreground processes receive SIGHUP. Use `nohup`, `screen`, or `tmux` for processes that must survive.

For kernels: The kernel continues running. Activity is tracked server-side, so a busy kernel won't be culled even if the browser is closed.

**Q: What happens to processes started with `nohup`?**

They survive terminal culling. `nohup` makes processes ignore SIGHUP, which is the signal sent when a terminal closes.

**Q: How do I disable culling entirely?**

Go to `Settings` -> `Settings Editor` -> `Resource Culler` and uncheck "Enable Kernel Culling" and "Enable Terminal Culling".

**Q: Can I see when resources were culled?**

Yes. Run JupyterLab with `--log-level=INFO` to see `[Culler]` log messages. If you have `jupyterlab-notifications` installed, you'll also see toast notifications.

## Uninstall

```bash
pip uninstall jupyterlab-kernel-terminal-workspace-culler-extension
```
