Add alibaba provider support (OpenAI-compatible MaaS) + fix {file:} token resolution

Jaime Fournier <jaimef@linbsd.org>

6be6eafb4d30a925e739793c98d17e0c041cdec8

diff --git a/src/jcode/core/config.ss b/src/jcode/core/config.ss
index f8b2552..61eed83 100644
--- a/src/jcode/core/config.ss
+++ b/src/jcode/core/config.ss
@@ -213,7 +213,11 @@
            (equal? (substring val 0 6) "{file:")
            (char=? (string-ref val (- (string-length val) 1)) #\}))
     (let* ((path (substring val 6 (- (string-length val) 1)))
-           (expanded (path-expand path)))
+           (expanded (if (and (>= (string-length path) 1)
+                              (char=? (string-ref path 0) #\~))
+                        (path-join (getenv "HOME")
+                                   (substring path 1 (string-length path)))
+                        (path-expand path))))
       (if (file-exists? expanded)
         (let ((content (call-with-input-file expanded
                          (lambda (p) (get-line p)))))
diff --git a/src/jcode/core/models.ss b/src/jcode/core/models.ss
index 5032c1e..318ef4f 100644
--- a/src/jcode/core/models.ss
+++ b/src/jcode/core/models.ss
@@ -55,7 +55,7 @@
   ;; distinct from "xai" (api.x.ai API-key) and "groq" (Groq Inc., separate).
   '("anthropic" "openai" "openrouter" "deepseek" "google" "ollama" "mlx"
     "xai" "grok" "groq" "mistral" "together" "cerebras" "perplexity"
-    "z-ai"))
+    "z-ai" "alibaba"))
 
 (def *configured-providers* (make-parameter '()))
 
@@ -84,6 +84,7 @@
    mlx2 are treated as MLX aliases so multiple local/remote MLX servers can be
    configured independently."
   (cond
+    ((equal? p "alibaba") "openai")   ;; alibaba MaaS is OpenAI-compatible
     ((member p *builtin-providers*) p)
     ((string-prefix? "mlx" p) "mlx")
     ((string-prefix? "ollama" p) "ollama")
@@ -112,8 +113,9 @@
              ((together)    "Together AI")
              ((cerebras)    "Cerebras")
              ((perplexity)  "Perplexity")
-             ((z-ai)        "Z.AI")
-             (else p))))
+((z-ai)        "Z.AI")
+              ((alibaba)     "Alibaba MaaS")
+              (else p))))
     (if (equal? p kind) base (format "~a (~a)" base p))))
 
 (def (provider-default-model provider)
@@ -133,6 +135,7 @@
     ((cerebras)    "llama-3.3-70b")
     ((perplexity)  "sonar-pro")
     ((z-ai)        "glm-5.2")
+    ((alibaba)     "qwen3.8-max-preview")
     (else          "gpt-4o")))
 
 ;; ---- Model lists per provider ----
@@ -181,6 +184,7 @@
     ((cerebras)    cerebras-models)
     ((perplexity)  perplexity-models)
     ((z-ai)        z-ai-models)
+    ((alibaba)     alibaba-models)
     (else '())))
 
 ;; ---- On-disk cache ----
@@ -361,6 +365,11 @@
     ("glm-5.1" . "Z.AI GLM-5.1")
     ("glm-5"   . "Z.AI GLM-5")))
 
+(def alibaba-models
+  '(("qwen3.8-max-preview" . "Qwen3.8 Max Preview (Alibaba MaaS)")
+    ("qwen3.7-max"          . "Qwen3.7 Max (Alibaba MaaS)")
+    ("qwen3.7-plus"         . "Qwen3.7 Plus (Alibaba MaaS)")))
+
 ;; ---- Pricing ($/M-tokens) ----
 ;; Tuple shape: (input output cache-read cache-write). Any field may be #f.
 ;; cache-read defaults to input rate when unknown; cache-write defaults to