From 870ee40c7aa933a907166171bd86bb6fda9a2ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 24 Aug 2026 20:45:47 -0400 Subject: [PATCH] Add AI test, doctor, and interactive setup wizard Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01QKxGW1raAWaqeU8WdHsMsp --- bin/omarchy-cn-ai-doctor | 47 +++++++++++++++++++++++ bin/omarchy-cn-ai-setup | 46 ++++++++++++++++++++++ bin/omarchy-cn-ai-test | 82 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100755 bin/omarchy-cn-ai-doctor create mode 100755 bin/omarchy-cn-ai-setup create mode 100755 bin/omarchy-cn-ai-test diff --git a/bin/omarchy-cn-ai-doctor b/bin/omarchy-cn-ai-doctor new file mode 100755 index 00000000..405b0657 --- /dev/null +++ b/bin/omarchy-cn-ai-doctor @@ -0,0 +1,47 @@ +#!/bin/bash +# omarchy:summary=Check AI harness installation, credentials, and endpoint health +# omarchy:examples=omarchycn ai doctor + +set -euo pipefail + +source "$OMARCHY_PATH/cn/lib/ai.sh" + +failures=0 + +profile=$(cn_ai_current_profile 2>/dev/null || true) +if [[ -z $profile ]]; then + echo "INFO ai: 未设置默认 Profile (omarchycn ai profile use )" + exit 0 +fi +echo "INFO ai: current profile $profile" + +harness=$(cn_ai_profile_field "$profile" harness) +provider=$(cn_ai_profile_field "$profile" provider) +command=$(cn_ai_harness_field "$harness" command) +proto=$(cn_ai_harness_field "$harness" protocol) + +if command -v "$command" > /dev/null 2>&1; then + echo "PASS ai: harness $harness installed ($command)" +else + echo "WARN ai: harness $harness 未安装,首次 launch 时自动安装" +fi + +if omarchy-cn-ai-secret get "$provider" > /dev/null 2>&1; then + echo "PASS ai: $provider API Key stored" +else + echo "FAIL ai: $provider API Key 未存储 (omarchycn ai secret set $provider)" + failures=$((failures + 1)) +fi + +if [[ $proto != "native" ]]; then + base=$(cn_ai_endpoint "$provider" "$proto") + host=$(sed -E 's|https?://([^/]+).*|\1|' <<<"$base") + if getent hosts "$host" > /dev/null 2>&1; then + echo "PASS ai: endpoint DNS resolves ($host)" + else + echo "FAIL ai: endpoint DNS 无法解析 ($host)" + failures=$((failures + 1)) + fi +fi + +exit $((failures > 0 ? 1 : 0)) diff --git a/bin/omarchy-cn-ai-setup b/bin/omarchy-cn-ai-setup new file mode 100755 index 00000000..352add3c --- /dev/null +++ b/bin/omarchy-cn-ai-setup @@ -0,0 +1,46 @@ +#!/bin/bash +# omarchy:summary=Interactive wizard: pick harness, provider, model, store key, test +# omarchy:examples=omarchycn ai setup + +set -euo pipefail + +source "$OMARCHY_PATH/cn/lib/ai.sh" + +if [[ ! -t 0 ]]; then + echo "omarchycn ai setup 需要交互终端;非交互场景用 omarchycn ai profile create" >&2 + exit 1 +fi + +harness=$(cn_ai_harness_ids | gum choose --header "选择 Harness") + +mapfile -t providers < <(jq -r --arg h "$harness" '.combos[$h] | keys[]' "$CN_AI_COMPAT") +if (( ${#providers[@]} == 0 )); then + echo "兼容矩阵中没有 $harness 的 Provider 组合" >&2 + exit 1 +fi +provider=$(printf '%s\n' "${providers[@]}" | gum choose --header "选择 Provider(兼容级别见 docs)") + +allowlist=$(cn_ai_combo_models "$harness" "$provider") +if [[ -n $allowlist ]]; then + model=$(gum choose --header "选择模型" <<<"$allowlist") +else + model=$(cn_ai_models "$provider" | gum choose --header "选择模型") +fi + +default_name="$harness-$provider" +name=$(gum input --header "Profile 名称" --value "$default_name") + +if ! omarchy-cn-ai-secret get "$provider" > /dev/null 2>&1; then + key=$(gum input --password --header "$provider API Key(获取: $(jq -r --arg p "$provider" '.providers[$p].key_url' "$CN_AI_PROVIDERS"))") + printf '%s' "$key" | omarchy-cn-ai-secret set "$provider" +fi + +omarchy-cn-ai-profile-create "$name" "$harness" "$provider" "$model" +omarchy-cn-ai-profile-use "$name" + +proto=$(cn_ai_harness_field "$harness" protocol) +if [[ $proto != "native" ]] && gum confirm "运行连接测试?"; then + omarchy-cn-ai-test "$name" +fi + +echo "完成。启动: omarchycn ai launch" diff --git a/bin/omarchy-cn-ai-test b/bin/omarchy-cn-ai-test new file mode 100755 index 00000000..2c3dfcfb --- /dev/null +++ b/bin/omarchy-cn-ai-test @@ -0,0 +1,82 @@ +#!/bin/bash +# omarchy:summary=Test connectivity, auth, and model availability for an AI profile +# omarchy:args=[profile] +# omarchy:examples=omarchycn ai test | omarchycn ai test work + +set -euo pipefail + +source "$OMARCHY_PATH/cn/lib/ai.sh" + +profile="${1:-$(cn_ai_current_profile)}" + +if [[ ! -f $CN_AI_PROFILE_DIR/$profile.toml ]]; then + echo "No such profile: $profile" >&2 + exit 1 +fi + +harness=$(cn_ai_profile_field "$profile" harness) +provider=$(cn_ai_profile_field "$profile" provider) +model=$(cn_ai_profile_field "$profile" model) +proto=$(cn_ai_harness_field "$harness" protocol) +failures=0 + +if [[ $proto == "native" ]]; then + echo "INFO ai-test: $harness 自管鉴权,本测试仅覆盖 env/config 型 Harness" + exit 0 +fi + +# Endpoint override for mock-server tests +base="${OMARCHYCN_AI_TEST_BASE_URL:-$(cn_ai_endpoint "$provider" "$proto")}" +echo "INFO ai-test: $profile ($harness x $provider x $model) -> $base" + +if ! key=$(omarchy-cn-ai-secret get "$provider" 2>/dev/null); then + echo "FAIL ai-test: 未存储 $provider 的 API Key (omarchycn ai secret set $provider)" + exit 1 +fi + +case "$proto" in +anthropic) + code=$(curl -sS -o /tmp/omarchycn-ai-test-body -w '%{http_code}' -m 30 --connect-timeout 8 \ + -X POST "${base%/}/v1/messages" \ + -H "x-api-key: $key" -H "authorization: Bearer $key" \ + -H "anthropic-version: 2023-06-01" -H "content-type: application/json" \ + -d "{\"model\":\"$model\",\"max_tokens\":8,\"messages\":[{\"role\":\"user\",\"content\":\"ping\"}]}" || echo 000) + ;; +openai) + code=$(curl -sS -o /tmp/omarchycn-ai-test-body -w '%{http_code}' -m 30 --connect-timeout 8 \ + -X POST "${base%/}/chat/completions" \ + -H "Authorization: Bearer $key" -H "content-type: application/json" \ + -d "{\"model\":\"$model\",\"max_tokens\":8,\"messages\":[{\"role\":\"user\",\"content\":\"ping\"}]}" || echo 000) + ;; +*) + echo "FAIL ai-test: unknown protocol $proto" + exit 1 + ;; +esac + +case "$code" in +200) + echo "PASS ai-test: 连接、鉴权与模型调用成功 (HTTP 200)" + ;; +401 | 403) + echo "FAIL ai-test: 鉴权失败 (HTTP $code),检查 API Key" + failures=$((failures + 1)) + ;; +404 | 400 | 422) + echo "FAIL ai-test: 端点可达但请求被拒 (HTTP $code),可能是模型名问题" + head -c 200 /tmp/omarchycn-ai-test-body 2>/dev/null && echo + failures=$((failures + 1)) + ;; +000) + echo "FAIL ai-test: 无法连接 $base (DNS/TLS/网络)" + failures=$((failures + 1)) + ;; +*) + echo "FAIL ai-test: HTTP $code" + head -c 200 /tmp/omarchycn-ai-test-body 2>/dev/null && echo + failures=$((failures + 1)) + ;; +esac + +rm -f /tmp/omarchycn-ai-test-body +exit $((failures > 0 ? 1 : 0))