Metadata-Version: 2.4
Name: caudex
Version: 0.2.1
Summary: Run Jupyter notebooks on Kaggle's GPUs from your terminal
Author-email: Sohanur Islam Shuvo <mdsohanurislamshuvo@gmail.com>
License-Expression: Apache-2.0
Project-URL: Homepage, https://pypi.org/project/caudex/
Project-URL: Changelog, https://pypi.org/project/caudex/#history
Keywords: kaggle,jupyter,notebook,gpu,cli,machine-learning
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: click<9,>=8.2
Requires-Dist: kaggle<3,>=2.2.4
Requires-Dist: platformdirs<6,>=4
Requires-Dist: rich<16,>=13
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pyinstaller>=6.0; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=6.1; extra == "dev"
Dynamic: license-file

# caudex

Run Jupyter notebooks on Kaggle's GPUs from your terminal.

`caudex` wraps the official Kaggle SDK so you can scaffold a notebook project, push it,
watch it run, and pull the results back — without touching the web UI. It uses your own
Kaggle account and your own quota. Nothing is proxied through anyone else's servers.

The loop it replaces looks like this: edit a notebook locally, upload it in a browser tab,
pick an accelerator from a dropdown, refresh the page until the run ends, then download a
zip. `caudex execute` is that whole loop in one command, with the logs streaming to your
terminal and the outputs landing in a directory you chose.

> **caudex 0.2.0** — formerly `kaggle-bridge`, command `kbr`. If you are upgrading, read
> [Breaking changes in 0.2.0](#breaking-changes-in-020); the short version is that the
> command is called `caudex` now, your config migrates itself, and a timeout exits **124**
> instead of 1.

---

## Install

From a checkout:

```bash
pip install -e .
caudex --help
```

On Windows you can also build a standalone installer that needs no Python at all — see
[Building the Windows installer](#development). It is a per-user install: no administrator
rights, it adds itself to your user `PATH`, and it offers to run `caudex auth --setup` when
it finishes. No prebuilt installer is published yet; `pip install caudex` is the supported
route today.

Requires Python 3.9+ and a Kaggle account. The Kaggle SDK is a hard dependency and is
installed with the package.

---

## Quickstart

```bash
caudex auth --setup                        # store your API token, once
caudex init train.ipynb --gpu --dir ./exp  # scaffold a project
# ... edit ./exp/train.ipynb ...
caudex execute --dir ./exp                 # push, stream logs, pull results
```

Get an API token at **kaggle.com → Settings → API → Create New Token**. That downloads a
`kaggle.json`; `caudex auth --setup` will ask you for the two values inside it and write
them to `~/.kaggle/kaggle.json`, which is the Kaggle SDK's own file and is shared with the
`kaggle` CLI if you also have that installed.

Anything your notebook writes to `/kaggle/working/` comes back:

```python
# inside the notebook
model.save("/kaggle/working/model.keras")
df.to_csv("/kaggle/working/scores.csv", index=False)
```

```bash
caudex pull --dir ./exp      # -> <output_dir>/<owner>_<slug>/
```

---

## Commands

`caudex --help` for the full text; `caudex COMMAND --help` for any one of them.

### Running notebooks

| Command | What it does |
|---|---|
| `caudex init [NOTEBOOK]` | Scaffold a notebook + `kernel-metadata.json` in a directory. `--gpu`, `--internet`, `--dir`, `--force` |
| `caudex push` | Push the local notebook to Kaggle and start a run. `--dir`, `--gpu`, `--no-gpu` |
| `caudex execute [NOTEBOOK]` | **The main command.** Push, stream logs, wait, pull outputs, print a per-cell report. `--runtime`, `--gpu/--no-gpu`, `--output-dir`, `--interval`, `--timeout`, `--pull-notebook`, `--no-pull`, `--no-cell-report`, `--on-timeout` |
| `caudex watch [KERNEL_REF]` | Poll an already-running kernel until it finishes. `--interval`, `--timeout`, `--on-timeout` |
| `caudex status [KERNEL_REF]` | Print the current run state and exit. |
| `caudex logs [KERNEL_REF]` | Print a run's log. `--follow` to stream, `--tail N` for the last N lines, `--output FILE` to also save a copy |
| `caudex pull [KERNEL_REF]` | Download output files from the last completed run. `--output-dir`, `--pull-notebook` |
| `caudex run` | *Deprecated.* `execute` with log streaming and the cell report turned off. |

### Account

| Command | What it does |
|---|---|
| `caudex auth` | Manage credentials and the local session. `--setup`, `--status`, `--logout`, `--session-ttl DAYS` |
| `caudex whoami` | Print the authenticated Kaggle username. |
| `caudex quota` | Remaining weekly GPU and TPU quota on your account. |
| `caudex notebooks` | List your Kaggle notebooks. `--search`, `--all`, `--limit` |

### Runtimes — `caudex runtime`

A runtime is a named accelerator preset stored in the project's `runtime.json`, so
"the GPU one" is something you name once instead of a set of flags you retype.

| Command | What it does |
|---|---|
| `runtime list` | Show every runtime configured for this notebook. |
| `runtime show` | Show which one is currently active. |
| `runtime use NAME` | Switch the active runtime for this notebook. |
| `runtime add NAME` | Add or overwrite one. `--label`, `--gpu`, `--tpu`, `--no-internet`, `--machine-shape` |
| `runtime remove NAME` | Remove a custom runtime. The built-in `default` cannot be removed. |

### Datasets — `caudex dataset`

| Command | What it does |
|---|---|
| `dataset search QUERY` | Search Kaggle datasets. `--limit` |
| `dataset list` | Your Kaggle datasets, or local downloads with `--local`. |
| `dataset files OWNER/SLUG` | List files inside a dataset without downloading it. |
| `dataset pull OWNER/SLUG` | Download a dataset. `--dest`, `--no-unzip` |
| `dataset remove OWNER/SLUG` | Drop it from the local index. `--files` also deletes the files. |
| `dataset upload PATH` | Upload a local folder as a private dataset. `--name`, `--title`, `--update` |
| `dataset uploads` | Folders you have previously uploaded. |
| `dataset attach REF` | Attach a dataset to a kernel — it appears at `/kaggle/input/<slug>/`. `--no-verify` |
| `dataset detach REF` | Remove one from the kernel's `dataset_sources`. |
| `dataset sources` | What is currently attached. |

### Paths — `caudex paths`

| Command | What it does |
|---|---|
| `paths show` | Current local path configuration. `--kernel REF` for one project's overrides. |
| `paths set KEY PATH` | Set `output_dir`, `datasets_dir` or `notebooks_dir`. `--kernel REF` scopes it to one kernel. |
| `paths unset KERNEL` | Drop per-kernel overrides. `--key` for just one. |
| `paths reset` | Restore the defaults. `--yes` to skip the confirmation. |

### Other

| Command | What it does |
|---|---|
| `caudex convert SCRIPT` | Turn a plain `.py` script into a `.ipynb`. `--out` |

---

## Exit codes

Every command uses the same three. They exist so a CI script can tell the difference
between "your notebook is broken" and "we stopped watching a notebook that is fine".

| Code | Meaning |
|---|---|
| **0** | Success. The thing you asked for happened. |
| **1** | Failure, and we know it failed: the kernel errored, was cancelled, finished in any state that is not `complete`, or a precondition was not met (no credentials, no such runtime, unreadable metadata). |
| **2** | Usage error. Click rejected the command line — unknown command, missing argument, mutually exclusive flags. Nothing was written. |
| **124** | Timed out waiting. Same code `/usr/bin/timeout` uses, and it means the same thing here. |

**124 is not a failure.** It means we gave up watching, not that the run died. The kernel
is almost certainly still executing on Kaggle and still consuming your weekly quota. Either
go back to it with `caudex watch` / `caudex logs --follow`, or pass `--on-timeout cancel`
to have caudex ask Kaggle to stop the session when it gives up.

```bash
caudex execute --dir ./exp --timeout 1800
case $? in
  0)   echo "done" ;;
  124) echo "still running on Kaggle — check with: caudex status --dir ./exp" ;;
  *)   echo "failed — check with: caudex logs --dir ./exp" ;;
esac
```

`caudex logs` is the one command that exits **0 for a failed kernel**. Reading the log of a
run that broke is the entire reason the command exists, so refusing to print it in exactly
that case would make it useless. It exits 1 only when the log itself could not be fetched.

---

## Where things live

| Path | |
|---|---|
| `~/.kaggle/kaggle.json` | Your Kaggle API token. The Kaggle SDK's own file — caudex reads and writes it but does not own it. |
| `%LOCALAPPDATA%\caudex\caudex\` | Config, session cache, dataset and upload indexes. `~/.config/caudex` on Linux, `~/Library/Application Support/caudex` on macOS. |
| `<project>/kernel-metadata.json` | The only file Kaggle ever sees besides the notebook. |
| `<project>/runtime.json` | Local accelerator presets. Never uploaded. |
| `<output_dir>/<owner>_<slug>/` | Where `pull` puts a run's outputs. `caudex paths show` prints the real path. |

`CAUDEX_CONFIG_DIR` and `CAUDEX_DATA_DIR` override the first two if you want a portable
install. Neither affects where migration looks for a 0.1.x profile.

---

## Breaking changes in 0.2.0

### The rename

| 0.1.x | 0.2.0 |
|---|---|
| `kbr` | `caudex` |
| `import kaggle_bridge` | `import caudex` |
| `%LOCALAPPDATA%\kaggle-bridge\kaggle-bridge\` | `%LOCALAPPDATA%\caudex\caudex\` |
| `_kbr_progress.json` | `_caudex_progress.json` |

Nothing belonging to Kaggle was renamed. `KAGGLE_CONFIG_DIR`, `KAGGLE_USERNAME`,
`KAGGLE_KEY`, `~/.kaggle/kaggle.json`, `kernel-metadata.json`, `dataset-metadata.json`,
`/kaggle/working/` and `/kaggle/input/` all mean exactly what they meant before. Your
existing credentials keep working and you do not need to re-authenticate.

**Your config migrates itself** the first time you run `caudex`. `config.json`,
`session.json`, `datasets.json` and `local_uploads.json` are copied over, each validated as
JSON first and skipped individually if corrupt, never overwriting a file that already
exists. The originals are left completely untouched, so a 0.1.x install alongside keeps
working. If you never ran `kbr paths set`, your outputs are sitting under the old data
directory — migration writes an explicit `config.json` pinning the three directories there,
rather than letting the new defaults silently point you at an empty folder.

Both installs on `PATH` at once will confuse nothing except you. Uninstall kaggle-bridge.

### Exit codes changed

Read this section if anything you own scripts against caudex.

- **A timeout now exits 124, not 1.** In 0.1.x, "the notebook failed" and "we stopped
  waiting" were the same exit code, so no script could tell them apart, and the usual
  reaction — treat non-zero as failure and move on — abandoned a kernel that was still
  burning quota. See the table above.
- **A run that ends in any state other than `complete` now exits 1.** 0.1.x checked for
  `error` and `timeout` explicitly and let everything else fall through to success. A
  cancelled kernel therefore made `kbr watch` **print nothing at all and exit 0**. Now
  `cancelled`, `cancelAcknowledged`, `cancelling`, a still-`running` kernel, and any state
  this build has never heard of all exit 1 with a message saying which one it was.
- The decision is made in one place for `watch`, `run` and `execute`, so they can no longer
  disagree about whether the same kernel succeeded.

### Renamed commands and flags

All four old spellings still work. Each one runs the new behaviour, prints a deprecation
notice **on stderr**, and is absent from `--help`, so stdout stays parseable and nobody new
learns the old name from us.

| Old | New | Why |
|---|---|---|
| `kbr list` | `caudex notebooks` | `list` never said what it listed. |
| `kbr run` | `caudex execute` | `execute` is the same pipeline with log streaming and the cell report switched on. `run` survives as the quiet variant. |
| `pull --notebook` | `pull --pull-notebook` | `execute` takes `NOTEBOOK` as a positional meaning "the notebook to run", so a `--notebook` flag meaning "download one" could not be read the same way twice. |
| `execute --save-to` | `execute --output-dir` | Two names for one destination, and `pull` already called it `--output-dir`. |

`caudex run` is kept deliberately unchanged, not just aliased: it still injects no tracker
cell, so your `.ipynb` is byte-identical afterwards, and it still streams no logs and prints
no cell report. Existing scripts behave exactly as they did.

### Output that moved

- **`init` and `watch` now print the directory files actually land in.** Both used to print
  `output_dir` while `pull` wrote into `output_dir/<owner>_<slug>`, so the path on screen
  was never the path on disk.
- **Errors go to stderr under `rich`.** With `rich` installed, 0.1.x printed error messages
  to stdout, so `kbr logs > run.txt` captured its own error text into the log file. The
  plain-text fallback always got this right; now both do.
- **`caudex logs` puts log content on stdout and nothing else.** "Log written to …",
  "No logs available yet" and every other notice go to stderr, so `caudex logs > run.txt`
  produces a log file rather than a log file with a progress report in it.
- **Runtime drift is now reported.** When `kernel-metadata.json` disagrees with the active
  runtime, the runtime still wins — but 0.1.x said nothing at all while overwriting your
  metadata, which is how a notebook could quietly run on CPU after you had set
  `enable_gpu: true` by hand. caudex names the keys it changed. The message is suppressed
  when you named a runtime explicitly, because `--gpu` *is* you resolving the disagreement.
- **Nested output files are listed distinctly.** `a/scores.csv` and `b/scores.csv` used to
  collapse onto the same line.

### New

- **`caudex logs`** — read a run's log after the fact, or `--follow` it live. The API call
  had been fully implemented since 0.1.0 with no command wired to it, so there was no
  supported way to read a kernel's output once the run was over.
- **`--on-timeout {warn,cancel}`** on `watch`, `run` and `execute`. `warn` is the default
  and matches 0.1.x. `cancel` asks Kaggle to stop the session so it stops using quota.
- **`--gpu/--no-gpu`** on `execute` and `run` as shorthand for `--runtime gpu` /
  `--runtime default`, for this run only — it does not change the project's active runtime.
  Combining it with an explicit `--runtime` is a usage error (exit 2), rejected before
  anything is written to disk.

---

## Development

```bash
pip install -e ".[dev]"
python -m pytest
```

Building the Windows binary and installer needs PyInstaller plus
[Inno Setup 6](https://jrsoftware.org/isdl.php):

```powershell
.\build.ps1                  # dist\caudex.exe + dist\caudex-0.2.0-windows-x64-setup.exe
.\build.ps1 -SkipInstaller   # just the .exe
```

`build/caudex.spec` collects data files out of whichever environment PyInstaller runs in,
so build from the same environment the app runs in, with the Jupyter stack installed. The
spec filters the build machine's live Jupyter state (`share/jupyter/runtime/`,
`notebook_secret`, `nbsignatures.db`) out of the bundle — 0.1.0 shipped all of it to every
user, and that must not happen again.

---

## Licence

Apache-2.0. The full text ships in every release as `LICENSE`, and inside the installed
package at `caudex-<version>.dist-info/licenses/LICENSE`.
