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