main
1provider add cline \
2 --type openai-compat \
3 --base-url "https://api.cline.bot/api/v1/" \
4 --discover-models false
5
6_cline_pass_ttl=86400
7_cline_pass_file="${XDG_DATA_HOME:-$HOME/.local/share}/crush/cline.json"
8
9# The feed lists ids without capabilities, so join it with OpenRouter for
10# capabilities and reasoning levels.
11_emit_cline_pass_models() {
12 jq -r '
13 .models[]
14 | [ .id, .name, .context_window, .default_max_tokens, .can_reason,
15 .supports_attachments,
16 ((.reasoning_levels // []) | join(",")),
17 (.default_reasoning_effort // "")
18 ]
19 | @tsv
20 ' "$_cline_pass_file" |
21 while IFS=$'\t' read -r id name ctx maxtokens canreason images levels effort; do
22 args=("cline/$id" --name "$name" \
23 --context-window "$ctx" \
24 --default-max-tokens "$maxtokens" \
25 --can-reason "$canreason" \
26 --supports-images "$images")
27 if [ -n "$levels" ]; then args+=(--reasoning-levels "$levels"); fi
28 if [ -n "$effort" ]; then args+=(--reasoning-effort "$effort"); fi
29 model add "${args[@]}" </dev/null
30 done
31}
32
33_cline_pass_usable() {
34 [ -f "$_cline_pass_file" ] &&
35 jq -e '.models | length > 0' "$_cline_pass_file" >/dev/null 2>&1
36}
37
38_cline_pass_fresh=false
39if _cline_pass_usable; then
40 if [ $(( $(date +%s) - $(stat -c %Y "$_cline_pass_file") )) -lt "$_cline_pass_ttl" ]; then
41 _cline_pass_fresh=true
42 fi
43fi
44
45if [ "$_cline_pass_fresh" = false ]; then
46 _cline_pass_models="$(curl -fsSL --max-time 8 \
47 "https://api.cline.bot/api/v1/ai/cline/recommended-models" || true)"
48 if [ -n "$_cline_pass_models" ]; then
49 _cline_pass_json="$(curl -fsSL --max-time 12 \
50 "https://openrouter.ai/api/v1/models" |
51 jq -c --argjson cp "$_cline_pass_models" '
52 (.data // [] | map({key: (.id | split("/") | last), value: .}) | from_entries) as $or
53 | {
54 name: "Cline", id: "cline", type: "openai-compat",
55 api_endpoint: "https://api.cline.bot/api/v1/",
56 models: [
57 $cp.clinePass[]?
58 | (.id | sub("^cline-pass/"; "")) as $slug
59 | ($or[$slug] // {}) as $m
60 | {
61 id: ("cline-pass/" + $slug),
62 name: (($m.name // $slug) | sub("^[^:]+: "; "")),
63 context_window: ($m.context_length // 128000),
64 default_max_tokens: ($m.top_provider.max_completion_tokens // 8192),
65 can_reason: (($m | length) == 0
66 or (($m.supported_parameters // []) | index("reasoning")) != null
67 or $m.reasoning != null),
68 reasoning_levels: ($m.reasoning.supported_efforts // []),
69 default_reasoning_effort: ($m.reasoning.default_effort // ""),
70 supports_attachments:
71 ((($m.architecture.input_modalities // []) | index("image")) != null)
72 }
73 ]
74 }')"
75 if [ -n "$_cline_pass_json" ] &&
76 [ "$(printf '%s' "$_cline_pass_json" | jq '.models | length')" -gt 0 ]; then
77 mkdir -p "$(dirname "$_cline_pass_file")"
78 printf '%s\n' "$_cline_pass_json" > "$_cline_pass_file.tmp" &&
79 mv "$_cline_pass_file.tmp" "$_cline_pass_file"
80 fi
81 fi
82fi
83
84# Falls back to a stale cache when the fetch above did not refresh it.
85if _cline_pass_usable; then
86 _emit_cline_pass_models
87fi