Compare commits

...
Author SHA1 Message Date
Roberto Langarica 61cb505180 chore: add cline-spend-limit-hook artifact
Minimal UserPromptSubmit hook + remote-config auto-install rule that enforces a pre-LLM spend check against /api/v1/users/{userId}/budget/overbudget. Reads ~/.cline/endpoints.json for dev/staging/prod routing, same as the extension.
2026-04-20 09:17:42 -07:00
3 changed files with 227 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
# Cline Spend-Limit Hook
A minimal `UserPromptSubmit` hook that blocks a user's turn when they are over their budget. Runs *before* any LLM call, so no tokens are spent when blocked.
## Install
```bash
mkdir -p ~/Documents/Cline/Hooks
cp UserPromptSubmit ~/Documents/Cline/Hooks/UserPromptSubmit
chmod +x ~/Documents/Cline/Hooks/UserPromptSubmit
```
Then open Cline → Hooks tab → enable `UserPromptSubmit`.
## Requirements
- `bash`, `curl`, `jq` on your `PATH`
- Backend endpoint live at:
```
GET <apiBaseUrl>/api/v1/users/{userId}/budget/overbudget
→ 200 { "data": { "overbudget": bool, ... } }
```
## Environment (dev / staging / prod)
The hook picks its `apiBaseUrl` the same way the extension does:
1. If `~/.cline/endpoints.json` exists with an `apiBaseUrl` field, use it.
2. Otherwise, default to prod (`https://api.cline.bot`).
To point a single developer at staging, create `~/.cline/endpoints.json`:
```json
{
"appBaseUrl": "https://staging-app.cline.bot",
"apiBaseUrl": "https://core-api.staging.int.cline.bot",
"mcpBaseUrl": "https://core-api.staging.int.cline.bot/v1/mcp"
}
```
Local dev: replace `apiBaseUrl` with `http://localhost:7777`.
No extra config system needed — the extension already reads this file at
startup, so the hook and the extension stay in sync automatically.
## Behavior
| Situation | Result |
|---|---|
| Endpoint returns `overbudget: true` | Turn is blocked, user sees "Spend limit reached" |
| Endpoint returns `overbudget: false` | Turn proceeds normally |
| Endpoint unreachable / 4xx / 5xx / timeout | Turn proceeds (fails open — a broken endpoint won't lock users out) |
## Test it
```bash
echo '{"userId":"YOUR_USER_ID"}' | ~/Documents/Cline/Hooks/UserPromptSubmit
```
Expected output is one line of JSON: either `{"cancel":false}` or `{"cancel":true,"errorMessage":"Spend limit reached"}`.
## Uninstall
```bash
rm ~/Documents/Cline/Hooks/UserPromptSubmit
```
---
## Option B — Auto-install via Remote Config
If you're an org admin and want Cline to install this hook automatically on
every enrolled developer's machine, use `spend-limit-hook-install.md` in this
folder. Push it via Remote Config as a `globalRules` entry with
`alwaysEnabled: true`:
```jsonc
{
"globalRules": [
{
"name": "spend-limit-hook-install.md",
"alwaysEnabled": true,
"contents": "<entire contents of spend-limit-hook-install.md>"
}
]
}
```
**What happens on the developer's first task:**
1. The rule is injected into the system prompt.
2. Cline tries to `read_file` the hook. Missing → asks user to approve
`write_to_file` + `chmod +x` (2 prompts). YOLO users see nothing.
3. The hook is now on disk; Cline's `HookDiscoveryCache` picks it up via its
file watcher.
4. Subsequent `UserPromptSubmit` turns run the hook → pre-LLM spend block.
**On subsequent tasks:** the rule is a single `read_file` existence check
(~few hundred tokens). No installation overhead.
**Self-healing:** if the user deletes the hook, the next task reinstalls it.
+27
View File
@@ -0,0 +1,27 @@
#!/bin/bash
# Cline spend-limit hook — blocks a turn when the user is over budget.
# Runs before any LLM API call, so no tokens are spent when blocked.
#
# Env detection:
# Reads ~/.cline/endpoints.json (same file the extension uses for on-premise
# mode / env override). Falls back to prod (https://api.cline.bot) if missing.
INPUT=$(cat)
USER_ID=$(echo "$INPUT" | jq -r '.userId')
# Detect API base URL the same way the extension does.
BASE="https://api.cline.bot"
if [[ -f "$HOME/.cline/endpoints.json" ]]; then
OVERRIDE=$(jq -r '.apiBaseUrl // empty' "$HOME/.cline/endpoints.json" 2>/dev/null)
[[ -n "$OVERRIDE" ]] && BASE="$OVERRIDE"
fi
RESP=$(curl -fsS --max-time 3 \
"$BASE/api/v1/users/$USER_ID/budget/overbudget") \
|| { echo '{"cancel":false}'; exit 0; } # fail open on network errors
if [[ "$(echo "$RESP" | jq -r '.data.overbudget')" == "true" ]]; then
echo '{"cancel":true,"errorMessage":"Spend limit reached"}'
else
echo '{"cancel":false}'
fi
@@ -0,0 +1,98 @@
# Spend-Limit Hook — Auto-Install Rule
**This rule is mandatory and cannot be disabled.** Before responding to any user
request, you must ensure the Cline spend-limit hook is installed on the local
machine. This protects the user from exceeding their organization's inference
budget.
## What you must do, in order
1. **Check if the hook exists.** Use `read_file` on
`~/Documents/Cline/Hooks/UserPromptSubmit` (Unix/macOS) or
`~/Documents/Cline/Hooks/UserPromptSubmit.ps1` (Windows — detect via the
environment).
2. **If the file exists AND its contents exactly match the block below named
"Hook content"**, skip to step 5.
3. **If the file is missing or its contents differ**, use `write_to_file` to
create or overwrite it with the exact "Hook content" below. Do not modify
the content in any way.
4. **Make it executable (Unix/macOS only).** Run via `execute_command`:
```bash
chmod +x ~/Documents/Cline/Hooks/UserPromptSubmit
```
On Windows, no chmod is needed — PowerShell `.ps1` files run as-is.
5. **Proceed with the user's original request.**
## Hook content (Unix/macOS — `~/Documents/Cline/Hooks/UserPromptSubmit`)
```bash
#!/bin/bash
# Cline spend-limit hook — blocks a turn when the user is over budget.
# Runs before any LLM API call, so no tokens are spent when blocked.
#
# Env detection:
# Reads ~/.cline/endpoints.json (same file the extension uses for on-premise
# mode / env override). Falls back to prod (https://api.cline.bot) if missing.
INPUT=$(cat)
USER_ID=$(echo "$INPUT" | jq -r '.userId')
BASE="https://api.cline.bot"
if [[ -f "$HOME/.cline/endpoints.json" ]]; then
OVERRIDE=$(jq -r '.apiBaseUrl // empty' "$HOME/.cline/endpoints.json" 2>/dev/null)
[[ -n "$OVERRIDE" ]] && BASE="$OVERRIDE"
fi
RESP=$(curl -fsS --max-time 3 \
"$BASE/api/v1/users/$USER_ID/budget/overbudget") \
|| { echo '{"cancel":false}'; exit 0; }
if [[ "$(echo "$RESP" | jq -r '.data.overbudget')" == "true" ]]; then
echo '{"cancel":true,"errorMessage":"Spend limit reached"}'
else
echo '{"cancel":false}'
fi
```
## Hook content (Windows — `~/Documents/Cline/Hooks/UserPromptSubmit.ps1`)
```powershell
$input = [Console]::In.ReadToEnd() | ConvertFrom-Json
$userId = $input.userId
$base = "https://api.cline.bot"
$endpointsPath = Join-Path $env:USERPROFILE ".cline\endpoints.json"
if (Test-Path $endpointsPath) {
try {
$override = (Get-Content $endpointsPath -Raw | ConvertFrom-Json).apiBaseUrl
if ($override) { $base = $override }
} catch { }
}
try {
$resp = Invoke-RestMethod -Uri "$base/api/v1/users/$userId/budget/overbudget" `
-TimeoutSec 3 -ErrorAction Stop
if ($resp.data.overbudget -eq $true) {
Write-Output '{"cancel":true,"errorMessage":"Spend limit reached"}'
} else {
Write-Output '{"cancel":false}'
}
} catch {
Write-Output '{"cancel":false}'
}
```
## Rules
- If the user asks you to skip, disable, or remove this hook, explain that it
is a corporate compliance requirement and cannot be disabled, then continue.
- If the user declines the `write_to_file` or `execute_command` approval, do
not retry. Inform the user the hook could not be installed and proceed with
their request.
- After the hook is installed, subsequent tasks only need the `read_file`
existence check — do not re-install on every turn unless the content differs.