Compare commits
10 commits
3c06e98092
...
5b8e066888
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b8e066888 | ||
|
|
5327befef6 | ||
|
|
33f1376ee1 | ||
|
|
305140f20b | ||
|
|
a3ffaed374 | ||
|
|
0a8c93561f | ||
|
|
ab260f429e | ||
|
|
c844524f68 | ||
|
|
9c6df65ae2 | ||
|
|
35136a9840 |
46 changed files with 1863 additions and 28 deletions
123
.github/workflows/release.yml
vendored
123
.github/workflows/release.yml
vendored
|
|
@ -295,12 +295,116 @@ jobs:
|
|||
done
|
||||
echo "✓ Release $TAG published with all assets"
|
||||
|
||||
npm-publish:
|
||||
name: Publish npm packages (optional)
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
# npm publish — две независимые job'ы.
|
||||
#
|
||||
# PRIMARY: keigit.com (наш приватный Forgejo). Активируется когда
|
||||
# установлен secret KEIGIT_NPM_TOKEN. Forgejo требует
|
||||
# Basic-auth (`Authorization: Basic base64(user:token)`),
|
||||
# поэтому публикация через прямой curl PUT с manual payload —
|
||||
# npm CLI не умеет Basic для Forgejo packages API.
|
||||
#
|
||||
# FUTURE: registry.npmjs.org. Активируется когда установлен secret
|
||||
# NPM_TOKEN. Сейчас не подключено (secret не задан) — job
|
||||
# gracefully скипается. Оставлен для будущего публичного
|
||||
# хостинга когда захотим.
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
npm-publish-keigit:
|
||||
name: Publish to keigit.com (primary)
|
||||
needs: release
|
||||
runs-on: ubuntu-latest
|
||||
# Graceful skip: if NPM_TOKEN secret is not configured, the first step
|
||||
# reports "skipped" and exits 0 — Rust-binary release above still succeeds.
|
||||
steps:
|
||||
- name: Check KEIGIT_NPM_TOKEN presence
|
||||
id: have_token
|
||||
env:
|
||||
KEIGIT_NPM_TOKEN: ${{ secrets.KEIGIT_NPM_TOKEN }}
|
||||
run: |
|
||||
if [ -n "${KEIGIT_NPM_TOKEN:-}" ]; then
|
||||
echo "present=1" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "present=0" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::KEIGIT_NPM_TOKEN not set — skipping keigit publish gracefully"
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
if: steps.have_token.outputs.present == '1'
|
||||
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
if: steps.have_token.outputs.present == '1'
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install deps
|
||||
if: steps.have_token.outputs.present == '1'
|
||||
working-directory: _ts_packages
|
||||
run: npm ci
|
||||
|
||||
- name: Build workspaces
|
||||
if: steps.have_token.outputs.present == '1'
|
||||
working-directory: _ts_packages
|
||||
run: npm run build --workspaces --if-present
|
||||
|
||||
- name: Publish each package via curl PUT
|
||||
if: steps.have_token.outputs.present == '1'
|
||||
working-directory: _ts_packages
|
||||
env:
|
||||
KEIGIT_NPM_TOKEN: ${{ secrets.KEIGIT_NPM_TOKEN }}
|
||||
KEIGIT_NPM_USER: ${{ secrets.KEIGIT_NPM_USER }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
: "${KEIGIT_NPM_USER:?KEIGIT_NPM_USER secret required (e.g. 'Parfionovich')}"
|
||||
B64_AUTH=$(printf '%s' "${KEIGIT_NPM_USER}:${KEIGIT_NPM_TOKEN}" | base64 -w0)
|
||||
|
||||
for pkg in packages/*/; do
|
||||
[ -f "$pkg/package.json" ] || continue
|
||||
pkgname=$(jq -r '.name' "$pkg/package.json")
|
||||
version=$(jq -r '.version' "$pkg/package.json")
|
||||
short=$(echo "$pkgname" | cut -d/ -f2)
|
||||
echo "::group::publish $pkgname@$version → keigit"
|
||||
(
|
||||
cd "$pkg"
|
||||
npm pack >/dev/null
|
||||
tarball="keisei-${short}-${version}.tgz"
|
||||
[ -f "$tarball" ] || { echo "::warning::tarball $tarball missing"; exit 0; }
|
||||
data=$(base64 -w0 "$tarball")
|
||||
shasum=$(sha1sum "$tarball" | awk '{print $1}')
|
||||
integrity="sha512-$(sha512sum "$tarball" | awk '{print $1}' | xxd -r -p | base64 -w0)"
|
||||
size=$(stat -c '%s' "$tarball")
|
||||
jq -n \
|
||||
--arg name "$pkgname" --arg version "$version" \
|
||||
--arg tarball "https://keigit.com/api/packages/keisei/npm/%40keisei%2F${short}/-/${version}/${short}-${version}.tgz" \
|
||||
--arg shasum "$shasum" --arg integrity "$integrity" \
|
||||
--arg data "$data" --argjson length "$size" \
|
||||
--arg attach "${short}-${version}.tgz" --slurpfile pkg package.json \
|
||||
'{ _id: $name, name: $name, "dist-tags": {latest: $version},
|
||||
versions: { ($version): ($pkg[0] + {_id: ($name + "@" + $version), dist: {tarball: $tarball, shasum: $shasum, integrity: $integrity}}) },
|
||||
_attachments: ({} | .[$attach] = { content_type:"application/octet-stream", data:$data, length:$length }) }' > payload.json
|
||||
http=$(curl -sS -X PUT "https://keigit.com/api/packages/keisei/npm/@keisei%2F${short}" \
|
||||
-H "Authorization: Basic ${B64_AUTH}" -H "Content-Type: application/json" \
|
||||
--data-binary @payload.json -o resp.txt -w "%{http_code}")
|
||||
if [ "$http" = "201" ]; then
|
||||
echo "$pkgname@$version → keigit OK"
|
||||
elif [ "$http" = "409" ] || grep -q "already exists" resp.txt 2>/dev/null; then
|
||||
echo "::warning::$pkgname@$version already published (skipping)"
|
||||
else
|
||||
echo "::error::$pkgname@$version → HTTP $http"
|
||||
cat resp.txt
|
||||
exit 1
|
||||
fi
|
||||
rm -f "$tarball" payload.json resp.txt
|
||||
)
|
||||
echo "::endgroup::"
|
||||
done
|
||||
|
||||
npm-publish-npmjs:
|
||||
name: Publish to registry.npmjs.org (future, gracefully skipped)
|
||||
needs: release
|
||||
runs-on: ubuntu-latest
|
||||
# FUTURE: добавит публичный хостинг через npmjs параллельно keigit.
|
||||
# Сейчас secret NPM_TOKEN не установлен → job просто скипается.
|
||||
# Когда захотим подключить — добавить secret NPM_TOKEN с
|
||||
# https://www.npmjs.com/settings/<user>/tokens, scope=Automation.
|
||||
steps:
|
||||
- name: Check NPM_TOKEN presence
|
||||
id: have_token
|
||||
|
|
@ -311,7 +415,7 @@ jobs:
|
|||
echo "present=1" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "present=0" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::NPM_TOKEN not set — skipping npm publish gracefully"
|
||||
echo "::notice::NPM_TOKEN not set — skipping npmjs publish gracefully (keigit publish is primary)"
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
|
|
@ -333,7 +437,7 @@ jobs:
|
|||
working-directory: _ts_packages
|
||||
run: npm run build --workspaces --if-present
|
||||
|
||||
- name: Publish each package
|
||||
- name: Publish each package via npm CLI (override registry)
|
||||
if: steps.have_token.outputs.present == '1'
|
||||
working-directory: _ts_packages
|
||||
env:
|
||||
|
|
@ -342,9 +446,10 @@ jobs:
|
|||
set -euo pipefail
|
||||
for pkg in packages/*/; do
|
||||
if [ -f "$pkg/package.json" ]; then
|
||||
echo "::group::publish $pkg"
|
||||
( cd "$pkg" && npm publish --access public ) \
|
||||
|| echo "::warning::publish failed for $pkg (continuing)"
|
||||
echo "::group::publish $pkg → npmjs"
|
||||
# --registry overrides publishConfig.registry (keigit) for this run.
|
||||
( cd "$pkg" && npm publish --access public --registry=https://registry.npmjs.org ) \
|
||||
|| echo "::warning::npmjs publish failed for $pkg (continuing)"
|
||||
echo "::endgroup::"
|
||||
fi
|
||||
done
|
||||
|
|
|
|||
149
_blocks/INDEX.md
Normal file
149
_blocks/INDEX.md
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
# Реестр блоков KeiSeiKit
|
||||
|
||||
> SSoT для assembler. Все блоки доступные для `blocks = [...]` в `_manifests/<agent>.toml`.
|
||||
> Авто-генерируется из `_blocks/*.md` через `bash build-index.sh`.
|
||||
> Каждый файл = атомарный кубик (Constructor Pattern).
|
||||
|
||||
Пример:
|
||||
```toml
|
||||
blocks = ["baseline", "rule-pre-dev-gate", "api-anthropic"]
|
||||
```
|
||||
|
||||
## По категориям
|
||||
|
||||
### API
|
||||
|
||||
- `api-anthropic` — API — Anthropic (Claude)
|
||||
- `api-apify` — API — Apify (web scraping platform)
|
||||
- `api-elevenlabs` — API — ElevenLabs (voice)
|
||||
- `api-fal-ai` — API — fal.ai (image / video / 3D)
|
||||
- `api-graphql` — API — GraphQL (schema-first, DataLoader, subscriptions, persisted queries)
|
||||
- `api-openapi-first` — API — OpenAPI-First (3.1 as single source of truth)
|
||||
- `api-rest-conventions` — API — REST Conventions (verbs, status codes, resources, idempotency, ETag)
|
||||
- `api-versioning-pagination-ratelimit` — API — Versioning, Pagination, Rate Limiting
|
||||
|
||||
### AUTH
|
||||
|
||||
- `auth-authorization` — AUTH — Authorization (RBAC / ABAC / ReBAC)
|
||||
- `auth-oauth2-oidc` — AUTH — OAuth2 + OIDC (Authorization Code + PKCE)
|
||||
- `auth-passkeys` — AUTH — Passkeys (WebAuthn / FIDO2)
|
||||
- `auth-sessions` — AUTH — Sessions & Cookies (+JWT tradeoff)
|
||||
|
||||
### CI
|
||||
|
||||
- `ci-forgejo-actions` — CI — Forgejo Actions (self-hosted, Tailscale-only admin)
|
||||
- `ci-github-actions` — CI — GitHub Actions (OIDC, matrix, cache, reusable workflows)
|
||||
- `ci-release-automation` — CI — Release automation (SemVer, changelog, tagging)
|
||||
- `ci-security-gate` — CI — Security gate (secrets, SCA, SBOM, semgrep, licenses)
|
||||
|
||||
### DB
|
||||
|
||||
- `db-drizzle` — DB — Drizzle ORM (TypeScript) patterns
|
||||
- `db-migration-hygiene` — DB — Migration hygiene (universal)
|
||||
- `db-postgres` — DB — PostgreSQL (current major — 17 as of 2026-04) patterns
|
||||
- `db-sqlite` — DB — SQLite (prod-suitable) patterns
|
||||
- `db-sqlx` — DB — SQLx (Rust) patterns
|
||||
|
||||
### DEPLOY
|
||||
|
||||
- `deploy-aws-ec2` — DEPLOY — AWS EC2 (Instance Connect + Elastic IP)
|
||||
- `deploy-cloudflare` — DEPLOY — Cloudflare (Workers / Pages / R2 / KV)
|
||||
- `deploy-docker` — DEPLOY — Docker
|
||||
- `deploy-hetzner-cloud` — DEPLOY — Hetzner Cloud (CX22 / CAX11 + TF + Cloud Firewall)
|
||||
- `deploy-local-only` — DEPLOY — LOCAL ONLY (sensitive / pre-disclosure project)
|
||||
- `deploy-modal` — DEPLOY — Modal (GPU compute)
|
||||
- `deploy-vps-generic` — DEPLOY — Generic VPS (provider-agnostic cloud-init + ssh-first-contact)
|
||||
|
||||
### DOCS
|
||||
|
||||
- `docs-architecture-diagrams` — DOCS — Architecture diagrams (Mermaid)
|
||||
- `docs-claude-md` — DOCS — `CLAUDE.md` (project bootstrap template)
|
||||
- `docs-decisions-adr` — DOCS — `DECISIONS.md` / ADR template (MADR 4.0)
|
||||
- `docs-readme-template` — DOCS — Public `README.md` scaffold
|
||||
- `docs-runbook` — DOCS — Operational runbook template
|
||||
|
||||
### DOMAIN
|
||||
|
||||
- `domain-has-secrets` — DOMAIN — Secrets handling
|
||||
- `domain-ml-training` — DOMAIN — ML Training
|
||||
- `domain-paid-apis` — DOMAIN — Paid APIs (Anthropic / OpenAI / fal.ai / Apify / Modal / AWS / GCP / ElevenLabs)
|
||||
|
||||
### MODE
|
||||
|
||||
- `mode-devils-advocate` — MODE — Devil's Advocate
|
||||
- `mode-first-principles` — MODE — First Principles
|
||||
- `mode-matrix` — MODE — Agent × Cognitive-Mode Matrix
|
||||
- `mode-maximalist` — MODE — Maximalist
|
||||
- `mode-minimalist` — MODE — Minimalist
|
||||
- `mode-skeptic` — MODE — Skeptic
|
||||
|
||||
### OBS
|
||||
|
||||
- `obs-metrics` — OBSERVABILITY — Metrics (Prometheus + OTel + RED/USE)
|
||||
- `obs-structured-logs` — OBSERVABILITY — Structured logs (JSON-lines)
|
||||
- `obs-traces` — OBSERVABILITY — Distributed traces (OpenTelemetry + W3C traceparent)
|
||||
|
||||
### PATH
|
||||
|
||||
- `path-user-hooks` — Path atom — user-hooks
|
||||
- `path-user-memory` — Path atom — user-memory
|
||||
- `path-user-rules` — Path atom — user-rules
|
||||
|
||||
### RULE
|
||||
|
||||
- `rule-double-audit` — DOUBLE AUDIT PROTOCOL (mandatory when 3+ files touched)
|
||||
- `rule-error-budget` — ERROR BUDGET — 3-Level Escalation
|
||||
- `rule-math-first` — MATH FIRST (mandatory for ML / physics / theory work)
|
||||
- `rule-pre-dev-gate` — PRE-DEV GATE — three checks before any new code
|
||||
- `rule-pure-click-contract` — Pure-Click Contract
|
||||
- `rule-test-first` — TEST-FIRST
|
||||
|
||||
### SCRAPER
|
||||
|
||||
- `scraper-free-tier` — DOMAIN — Scrapers Tier 1 (free APIs + open-source)
|
||||
- `scraper-paid-tier` — DOMAIN — Scrapers Tier 3 (Apify / Bright Data paid)
|
||||
- `scraper-unified-output` — DOMAIN — Scraper unified output invariant
|
||||
|
||||
### SECURITY
|
||||
|
||||
- `security-audit-logging` — SECURITY — Audit Logging (auditd + journald forwarding)
|
||||
- `security-firewall-ufw` — SECURITY — Firewall (ufw default-deny + rate limiting + nftables alt)
|
||||
- `security-patching` — SECURITY — Patching (unattended-upgrades + needrestart + reboot window)
|
||||
- `security-ssh-hardening` — SECURITY — SSH Hardening (sshd_config.d/99-kei.conf)
|
||||
- `security-tls-caddy` — SECURITY — TLS via Caddy (automatic ACME, HTTP-01 / DNS-01)
|
||||
|
||||
### STACK
|
||||
|
||||
- `stack-astro` — STACK — Astro 6 (Content + Marketing + Islands)
|
||||
- `stack-embedded-stm32` — STACK — Embedded Rust STM32 (embassy / cortex-m)
|
||||
- `stack-fastapi-postgres` — STACK — FastAPI + async SQLAlchemy 2.0 + PostgreSQL
|
||||
- `stack-flutter` — STACK — Flutter + Riverpod + Clean Architecture
|
||||
- `stack-go-server` — STACK — Go server
|
||||
- `stack-nextjs` — STACK — Next.js 15/16 (App Router + TS + Server Components)
|
||||
- `stack-python-ml` — STACK — Python ML (PyTorch / JAX)
|
||||
- `stack-react-vite` — STACK — Vite + React 19 + TypeScript (SPA)
|
||||
- `stack-rust-axum` — STACK — Rust HTTP server (axum + tokio + sqlx)
|
||||
- `stack-rust-cli` — STACK — Rust CLI / tooling
|
||||
- `stack-sveltekit` — STACK — SvelteKit (Svelte 5 Runes + TS)
|
||||
- `stack-swift-ios` — STACK — Swift iOS (UIKit / SwiftUI hybrid)
|
||||
- `stack-swift-spm` — STACK — Swift SPM executable (macOS)
|
||||
- `stack-tailwind` — STACK — Tailwind CSS 4 (compositional add-on)
|
||||
|
||||
### TEST
|
||||
|
||||
- `test-e2e` — TEST — End-to-end (Playwright browser automation)
|
||||
- `test-fuzz` — TEST — Fuzzing (input-space exploration)
|
||||
- `test-load` — TEST — Load / performance testing (baseline → profile → fix)
|
||||
- `test-property` — TEST — Property-based testing (invariants + shrinking)
|
||||
|
||||
### Прочие (без категорийного префикса)
|
||||
|
||||
- `baseline` — BASELINE — inherit from Main Claude (never violate)
|
||||
- `evidence-grading` — EVIDENCE GRADING
|
||||
- `memory-protocol` — MEMORY PROTOCOL
|
||||
- `pipeline-5phase-template` — Pipeline 5-Phase Wizard Template (shared preamble)
|
||||
|
||||
---
|
||||
|
||||
Всего блоков: 84.
|
||||
Перегенерация: `bash _blocks/build-index.sh`.
|
||||
72
_blocks/build-index.sh
Executable file
72
_blocks/build-index.sh
Executable file
|
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/env bash
|
||||
# build-index.sh — регенерация _blocks/INDEX.md из *.md.
|
||||
#
|
||||
# Использование:
|
||||
# cd ~/Projects/KeiSeiKit-public/_blocks && bash build-index.sh
|
||||
# # или из любого места:
|
||||
# bash $(git rev-parse --show-toplevel)/_blocks/build-index.sh
|
||||
#
|
||||
# Что делает:
|
||||
# 1. Сканит _blocks/*.md (исключая README.md и INDEX.md).
|
||||
# 2. Группирует по префиксу (api-, auth-, ci-, db-, deploy-, ...).
|
||||
# 3. Для каждого блока берёт первую H1-строку как описание.
|
||||
# 4. Пишет INDEX.md с разбиением по 14 категориям + "Прочие".
|
||||
#
|
||||
# Безопасно перезапускать — детерминированный output.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Запускаемся всегда из _blocks/.
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
CATEGORIES=(api auth ci db deploy docs domain mode obs path rule scraper security stack test)
|
||||
|
||||
OUT="INDEX.md"
|
||||
TMP="${OUT}.tmp.$$"
|
||||
trap 'rm -f "$TMP"' EXIT
|
||||
|
||||
{
|
||||
printf '# Реестр блоков KeiSeiKit\n\n'
|
||||
printf '> SSoT для assembler. Все блоки доступные для `blocks = [...]` в `_manifests/<agent>.toml`.\n'
|
||||
printf '> Авто-генерируется из `_blocks/*.md` через `bash build-index.sh`.\n'
|
||||
printf '> Каждый файл = атомарный кубик (Constructor Pattern).\n\n'
|
||||
printf 'Пример:\n```toml\nblocks = ["baseline", "rule-pre-dev-gate", "api-anthropic"]\n```\n\n'
|
||||
printf '## По категориям\n\n'
|
||||
|
||||
for cat in "${CATEGORIES[@]}"; do
|
||||
upper=$(echo "$cat" | tr '[:lower:]' '[:upper:]')
|
||||
files=$(ls 2>/dev/null | grep -E "^${cat}(-|\.).*\.md$" || true)
|
||||
[ -z "$files" ] && continue
|
||||
printf '### %s\n\n' "$upper"
|
||||
while IFS= read -r f; do
|
||||
[ -z "$f" ] && continue
|
||||
name="${f%.md}"
|
||||
desc=$(awk '/^# / { sub(/^# /, ""); print; exit }' "$f" 2>/dev/null || true)
|
||||
[ -z "$desc" ] && desc="(no title)"
|
||||
printf -- '- `%s` — %s\n' "$name" "$desc"
|
||||
done <<< "$files"
|
||||
printf '\n'
|
||||
done
|
||||
|
||||
printf '### Прочие (без категорийного префикса)\n\n'
|
||||
while IFS= read -r f; do
|
||||
name="${f%.md}"
|
||||
case "$name" in
|
||||
api-*|auth-*|ci-*|db-*|deploy-*|docs-*|domain-*|mode-*|obs-*|path-*|rule-*|scraper-*|security-*|stack-*|test-*|README|INDEX) continue ;;
|
||||
esac
|
||||
desc=$(awk '/^# / { sub(/^# /, ""); print; exit }' "$f" 2>/dev/null || true)
|
||||
[ -z "$desc" ] && desc="(no title)"
|
||||
printf -- '- `%s` — %s\n' "$name" "$desc"
|
||||
done < <(ls *.md)
|
||||
|
||||
total=$(ls *.md | grep -vE '^(README|INDEX)\.md$' | wc -l | tr -d ' ')
|
||||
printf '\n---\n\nВсего блоков: %d.\n' "$total"
|
||||
printf 'Перегенерация: `bash _blocks/build-index.sh`.\n'
|
||||
} > "$TMP"
|
||||
|
||||
mv "$TMP" "$OUT"
|
||||
trap - EXIT
|
||||
|
||||
echo "✓ $OUT regenerated"
|
||||
wc -l "$OUT"
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 7aaa6a79b00271d9c08ac4f5c1f0e2d523a49da0
|
||||
Subproject commit afe0c6f1183deaf4d3947bb6a4bf279a6bf9418e
|
||||
|
|
@ -41,7 +41,7 @@ chrono = { workspace = true }
|
|||
# provision-crypto: x25519 ECDH + HKDF-SHA256 + XChaCha20-Poly1305
|
||||
# Mirrors marketplace/src/lib/crypto-box.ts so VPS can decrypt the
|
||||
# bot-token blob emitted by the browser.
|
||||
x25519-dalek = { version = "2", features = ["static_secrets"] }
|
||||
x25519-dalek = { version = "2", features = ["static_secrets", "zeroize"] }
|
||||
chacha20poly1305 = { version = "0.10", features = ["alloc"] }
|
||||
hkdf = "0.12"
|
||||
sha2 = "0.10"
|
||||
|
|
|
|||
|
|
@ -59,6 +59,17 @@ fn b64decode(s: &str) -> Result<Vec<u8>> {
|
|||
.map_err(|e| anyhow!("base64 decode: {e}"))
|
||||
}
|
||||
|
||||
/// Парсит PKCS#8 v1 PEM с приватником X25519 (RFC 8410 §7).
|
||||
///
|
||||
/// Ожидаемый формат — ровно 48 байт DER, последние 32 — raw priv.
|
||||
/// Проверки до взятия хвоста:
|
||||
/// - длина DER ровно 48 байт
|
||||
/// - OID 1.3.101.110 (X25519) по смещению 9..12: 0x2b 0x65 0x6e
|
||||
///
|
||||
/// Без OID-проверки RSA/EC/Ed25519 ключ молча даст 32 неправильных байта.
|
||||
const X25519_OID: [u8; 3] = [0x2b, 0x65, 0x6e]; // RFC 8410 §3
|
||||
const X25519_PKCS8_DER_LEN: usize = 48;
|
||||
|
||||
fn parse_x25519_pkcs8_pem(pem: &str) -> Result<[u8; 32]> {
|
||||
let dash_prefix = "-".repeat(5);
|
||||
let body: String = pem
|
||||
|
|
@ -69,8 +80,18 @@ fn parse_x25519_pkcs8_pem(pem: &str) -> Result<[u8; 32]> {
|
|||
let der = STANDARD
|
||||
.decode(body.trim())
|
||||
.context("PEM body is not valid base64")?;
|
||||
if der.len() < 32 {
|
||||
bail!("PKCS#8 DER too short: {} bytes", der.len());
|
||||
if der.len() != X25519_PKCS8_DER_LEN {
|
||||
bail!(
|
||||
"PKCS#8 DER must be {} bytes for X25519, got {}",
|
||||
X25519_PKCS8_DER_LEN,
|
||||
der.len()
|
||||
);
|
||||
}
|
||||
if der[9..12] != X25519_OID {
|
||||
bail!(
|
||||
"PKCS#8 OID does not match X25519 (1.3.101.110); got {:02x?}",
|
||||
&der[9..12]
|
||||
);
|
||||
}
|
||||
let mut out = [0u8; 32];
|
||||
out.copy_from_slice(&der[der.len() - 32..]);
|
||||
|
|
@ -329,6 +350,37 @@ mod tests {
|
|||
assert_eq!(b64decode(urlsafe).unwrap(), b"Hello world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_rejects_wrong_length_der() {
|
||||
// ровно 32 байта — слишком короткий для PKCS#8 v1 wrapper
|
||||
let bad_pem = format!(
|
||||
"{}\n{}\n{}\n",
|
||||
pem_begin(),
|
||||
STANDARD.encode([0u8; 32]),
|
||||
pem_end()
|
||||
);
|
||||
let err = parse_x25519_pkcs8_pem(&bad_pem).err().unwrap();
|
||||
assert!(err.to_string().contains("48 bytes"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_rejects_wrong_oid() {
|
||||
// 48 байт правильной длины, но OID не X25519 (например Ed25519: 0x2b 0x65 0x70)
|
||||
let mut der = vec![
|
||||
0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22,
|
||||
0x04, 0x20,
|
||||
];
|
||||
der.extend_from_slice(&[0u8; 32]);
|
||||
let bad_pem = format!(
|
||||
"{}\n{}\n{}\n",
|
||||
pem_begin(),
|
||||
STANDARD.encode(&der),
|
||||
pem_end()
|
||||
);
|
||||
let err = parse_x25519_pkcs8_pem(&bad_pem).err().unwrap();
|
||||
assert!(err.to_string().contains("X25519"));
|
||||
}
|
||||
|
||||
fn tempdir_unique() -> std::path::PathBuf {
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
static COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
|
|
|
|||
|
|
@ -6,11 +6,22 @@
|
|||
//! Types in `registry_types.rs` (Constructor Pattern: types separate from loader).
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Deserialize;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub use crate::registry_types::{Model, Profile, Provider};
|
||||
use crate::registry_types::{ModelsFile, ProfilesFile, ProvidersFile};
|
||||
|
||||
/// User-tier override: что пользователь выбрал в onboarding мастере.
|
||||
/// Парсится из `~/.claude/config/user-model-override.toml`.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct UserModelOverride {
|
||||
pub provider: String,
|
||||
pub model: String,
|
||||
#[serde(default)]
|
||||
pub transport: Option<String>,
|
||||
}
|
||||
|
||||
// Embedded compile-time copies. Cargo tracks these as implicit dependencies:
|
||||
// if the TOML changes, the crate is recompiled automatically.
|
||||
const EMBEDDED_PROVIDERS: &str =
|
||||
|
|
@ -65,6 +76,25 @@ impl Registry {
|
|||
})
|
||||
}
|
||||
|
||||
/// Загружает user-tier override из `~/.claude/config/user-model-override.toml`.
|
||||
/// Файл пишется установщиком (install/lib-onboarding.sh::onboarding_write_config)
|
||||
/// при первичной настройке. Содержит выбор юзера: provider/model/transport.
|
||||
///
|
||||
/// Priority в router: `--pinned` flag > этот файл > agent-profiles.toml::default_model_ref.
|
||||
/// Без него выбор провайдера в onboarding декоративен (HIGH аудит-1, 2026-05-17).
|
||||
pub fn load_user_override() -> Option<UserModelOverride> {
|
||||
let home = std::env::var("HOME").ok()?;
|
||||
if home.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let path = PathBuf::from(format!("{home}/.claude/config/user-model-override.toml"));
|
||||
if !path.exists() {
|
||||
return None;
|
||||
}
|
||||
let raw = std::fs::read_to_string(&path).ok()?;
|
||||
toml::from_str::<UserModelOverride>(&raw).ok()
|
||||
}
|
||||
|
||||
pub fn provider_by_id(&self, id: &str) -> Option<&Provider> {
|
||||
self.providers.iter().find(|p| p.id == id)
|
||||
}
|
||||
|
|
@ -137,7 +167,38 @@ mod tests {
|
|||
fn provider_by_id_anthropic() {
|
||||
let r = reg();
|
||||
let p = r.provider_by_id("anthropic").expect("anthropic missing");
|
||||
assert_eq!(p.display_name, "Anthropic");
|
||||
// После RULE 0.26 transport-расширения display_name стало
|
||||
// "Anthropic (Direct API)" чтобы отличать от "anthropic-bedrock".
|
||||
assert!(
|
||||
p.display_name.starts_with("Anthropic"),
|
||||
"got: {}", p.display_name
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_override_parses_minimal_toml() {
|
||||
// Round-trip: TOML → UserModelOverride.
|
||||
let toml_src = r#"
|
||||
provider = "ollama-local"
|
||||
model = "llama-3.3-70b"
|
||||
transport = "local"
|
||||
"#;
|
||||
let ov: UserModelOverride = toml::from_str(toml_src).expect("parse failed");
|
||||
assert_eq!(ov.provider, "ollama-local");
|
||||
assert_eq!(ov.model, "llama-3.3-70b");
|
||||
assert_eq!(ov.transport.as_deref(), Some("local"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_override_transport_optional() {
|
||||
// transport — optional поле.
|
||||
let toml_src = r#"
|
||||
provider = "openai"
|
||||
model = "gpt-5"
|
||||
"#;
|
||||
let ov: UserModelOverride = toml::from_str(toml_src).expect("parse failed");
|
||||
assert_eq!(ov.provider, "openai");
|
||||
assert!(ov.transport.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -30,5 +30,9 @@
|
|||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"author": "Denis Parfionovich <parfionovich@keilab.io>"
|
||||
"author": "Denis Parfionovich <parfionovich@keilab.io>",
|
||||
"publishConfig": {
|
||||
"registry": "https://keigit.com/api/packages/keisei/npm/",
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,5 +29,9 @@
|
|||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"author": "Denis Parfionovich <parfionovich@keilab.io>"
|
||||
"author": "Denis Parfionovich <parfionovich@keilab.io>",
|
||||
"publishConfig": {
|
||||
"registry": "https://keigit.com/api/packages/keisei/npm/",
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
7
_ts_packages/packages/mcp-server/.npmignore
Normal file
7
_ts_packages/packages/mcp-server/.npmignore
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Source maps leak absolute paths of dev machine.
|
||||
# Tested 2026-05-15: dist/*.js.map content includes "/Users/<dev>/Projects/..." strings.
|
||||
**/*.map
|
||||
**/*.tsbuildinfo
|
||||
src/
|
||||
test/
|
||||
tsconfig*.json
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keisei/mcp-server",
|
||||
"version": "0.14.5",
|
||||
"version": "0.14.6",
|
||||
"description": "MCP server exposing KeiSeiKit Rust primitives as Model Context Protocol tools — published to keigit.com (Forgejo npm registry, public DNS)",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"types": ["node"]
|
||||
"types": ["node"],
|
||||
"sourceMap": false,
|
||||
"declarationMap": false
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["dist", "node_modules", "test/**/*"]
|
||||
|
|
|
|||
|
|
@ -29,5 +29,9 @@
|
|||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"author": "Denis Parfionovich <parfionovich@keilab.io>"
|
||||
"author": "Denis Parfionovich <parfionovich@keilab.io>",
|
||||
"publishConfig": {
|
||||
"registry": "https://keigit.com/api/packages/keisei/npm/",
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,5 +30,9 @@
|
|||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"author": "Denis Parfionovich <parfionovich@keilab.io>"
|
||||
"author": "Denis Parfionovich <parfionovich@keilab.io>",
|
||||
"publishConfig": {
|
||||
"registry": "https://keigit.com/api/packages/keisei/npm/",
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,5 +31,9 @@
|
|||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"author": "Denis Parfionovich <parfionovich@keilab.io>"
|
||||
"author": "Denis Parfionovich <parfionovich@keilab.io>",
|
||||
"publishConfig": {
|
||||
"registry": "https://keigit.com/api/packages/keisei/npm/",
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@
|
|||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"declarationMap": false,
|
||||
"sourceMap": false,
|
||||
"composite": true,
|
||||
"incremental": true
|
||||
}
|
||||
|
|
|
|||
17
install.sh
17
install.sh
|
|
@ -41,6 +41,14 @@ source "$LIB_DIR/lib-profile.sh"
|
|||
source "$LIB_DIR/lib-args.sh"
|
||||
# shellcheck source=install/lib-menu.sh
|
||||
source "$LIB_DIR/lib-menu.sh"
|
||||
# shellcheck source=install/lib-i18n.sh
|
||||
source "$LIB_DIR/lib-i18n.sh"
|
||||
# Загружаем английский словарь по умолчанию — welcome banner идёт до выбора языка.
|
||||
i18n_load_default
|
||||
# shellcheck source=install/lib-preflight.sh
|
||||
source "$LIB_DIR/lib-preflight.sh"
|
||||
# shellcheck source=install/lib-onboarding.sh
|
||||
source "$LIB_DIR/lib-onboarding.sh"
|
||||
# shellcheck source=install/lib-plan.sh
|
||||
source "$LIB_DIR/lib-plan.sh"
|
||||
# shellcheck source=install/lib-prereqs.sh
|
||||
|
|
@ -140,6 +148,15 @@ case "$PROFILE" in
|
|||
esac
|
||||
say "profile: $PROFILE"
|
||||
|
||||
# --- welcome banner + onboarding wizard ----------------------------------
|
||||
# Banner всегда EN — пользователь ещё не выбрал язык.
|
||||
# Wizard: TTY + нет ~/.claude/.onboarded + не задан KEISEI_SKIP_ONBOARD.
|
||||
# Skip: KEISEI_SKIP_ONBOARD=1 ./install.sh
|
||||
if onboarding_should_run; then
|
||||
i18n_print_welcome
|
||||
fi
|
||||
onboarding_run
|
||||
|
||||
# --- prerequisites -------------------------------------------------------
|
||||
check_prereqs
|
||||
|
||||
|
|
|
|||
35
install/i18n/ar.sh
Normal file
35
install/i18n/ar.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/ar.sh — العربية.
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
STR_ONBOARDING_INTRO="معالج الإعداد الأولي (5 خطوات)"
|
||||
STR_PICK_LANGUAGE="اختر لغة الواجهة:"
|
||||
STR_PICK_TRANSPORT="اختر طريقة الاتصال:"
|
||||
STR_PICK_PROVIDER="اختر المزود ضمن المجموعة"
|
||||
STR_PICK_MODEL="النموذج الافتراضي:"
|
||||
|
||||
STR_TR_DIRECT_API="واجهة برمجية مباشرة للمزود (مفتاح)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/دور)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (deployment+مفتاح)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="محلي (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="وكيل (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="اشتراك OAuth (ChatGPT)"
|
||||
|
||||
STR_AUTH_INTRO="المصادقة لـ"
|
||||
STR_AUTH_PROMPT="أدخل القيم (Enter — اتركها فارغة، املأها لاحقًا)."
|
||||
STR_AUTH_CURRENT_HINT="(الحالي: <مخفي>)"
|
||||
|
||||
STR_DONE_TITLE="اكتمل الإعداد الأولي"
|
||||
STR_DONE_CONFIG="التكوين:"
|
||||
STR_DONE_SECRETS="الأسرار:"
|
||||
|
||||
STR_MENU_TITLE="مثبّت KeiSeiKit"
|
||||
STR_MENU_SUBSTRATE="القاعدة الأساسية (دائمًا مثبتة):"
|
||||
STR_MENU_PROFILE_PROMPT="اختر ملف التثبيت:"
|
||||
STR_MENU_CONFIRM="تأكيد الاختيار؟"
|
||||
|
||||
STR_PREFLIGHT_FAILED="فشل Preflight — قد لا يعمل المزود."
|
||||
STR_PREFLIGHT_CONTINUE="هل تريد المتابعة على أي حال؟ [y/N]"
|
||||
35
install/i18n/de.sh
Normal file
35
install/i18n/de.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/de.sh — Deutsch.
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
STR_ONBOARDING_INTRO="Einrichtungsassistent (5 Schritte)"
|
||||
STR_PICK_LANGUAGE="Oberflächensprache wählen:"
|
||||
STR_PICK_TRANSPORT="Verbindungsart wählen:"
|
||||
STR_PICK_PROVIDER="Anbieter in der Gruppe wählen"
|
||||
STR_PICK_MODEL="Standardmodell:"
|
||||
|
||||
STR_TR_DIRECT_API="Direkte API des Anbieters (Schlüssel)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/Rolle)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (Deployment+Schlüssel)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="Lokal (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="Proxy (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="OAuth-Abo (ChatGPT)"
|
||||
|
||||
STR_AUTH_INTRO="Authentifizierung für"
|
||||
STR_AUTH_PROMPT="Werte eingeben (Enter — leer lassen, später ausfüllen)."
|
||||
STR_AUTH_CURRENT_HINT="(aktuell: <ausgeblendet>)"
|
||||
|
||||
STR_DONE_TITLE="Einrichtung abgeschlossen"
|
||||
STR_DONE_CONFIG="Konfig:"
|
||||
STR_DONE_SECRETS="Geheimnisse:"
|
||||
|
||||
STR_MENU_TITLE="KeiSeiKit Installer"
|
||||
STR_MENU_SUBSTRATE="Substrat-Basis (immer installiert):"
|
||||
STR_MENU_PROFILE_PROMPT="Installationsprofil wählen:"
|
||||
STR_MENU_CONFIRM="Auswahl bestätigen?"
|
||||
|
||||
STR_PREFLIGHT_FAILED="Preflight fehlgeschlagen — Anbieter funktioniert evtl. nicht."
|
||||
STR_PREFLIGHT_CONTINUE="Trotzdem fortfahren? [j/N]"
|
||||
42
install/i18n/en.sh
Normal file
42
install/i18n/en.sh
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/en.sh — English strings. Default before user picks language.
|
||||
|
||||
# Welcome banner (always EN, shown before language picker).
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
# Onboarding wizard steps
|
||||
STR_ONBOARDING_INTRO="Onboarding wizard (5 steps)"
|
||||
STR_PICK_LANGUAGE="Choose interface language:"
|
||||
STR_PICK_TRANSPORT="Choose connection transport:"
|
||||
STR_PICK_PROVIDER="Choose provider within"
|
||||
STR_PICK_MODEL="Default model:"
|
||||
|
||||
# Transport descriptions
|
||||
STR_TR_DIRECT_API="Direct provider API (key)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/role)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (deployment+key)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="Local (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="Proxy (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="OAuth subscription (ChatGPT)"
|
||||
|
||||
# Auth collection
|
||||
STR_AUTH_INTRO="Auth for"
|
||||
STR_AUTH_PROMPT="Enter values (Enter — leave empty, fill later)."
|
||||
STR_AUTH_CURRENT_HINT="(current: <hidden>)"
|
||||
|
||||
# Completion
|
||||
STR_DONE_TITLE="Onboarding complete"
|
||||
STR_DONE_CONFIG="config:"
|
||||
STR_DONE_SECRETS="secrets:"
|
||||
|
||||
# Profile menu (lib-menu.sh strings)
|
||||
STR_MENU_TITLE="KeiSeiKit Installer"
|
||||
STR_MENU_SUBSTRATE="Substrate baseline (always installed):"
|
||||
STR_MENU_PROFILE_PROMPT="Choose install profile:"
|
||||
STR_MENU_CONFIRM="Confirm selection?"
|
||||
|
||||
# Preflight warnings
|
||||
STR_PREFLIGHT_FAILED="Preflight failed — provider may not work."
|
||||
STR_PREFLIGHT_CONTINUE="Continue anyway? [y/N]"
|
||||
35
install/i18n/es.sh
Normal file
35
install/i18n/es.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/es.sh — Español.
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
STR_ONBOARDING_INTRO="Asistente de configuración inicial (5 pasos)"
|
||||
STR_PICK_LANGUAGE="Elige el idioma de la interfaz:"
|
||||
STR_PICK_TRANSPORT="Elige el método de conexión:"
|
||||
STR_PICK_PROVIDER="Elige el proveedor dentro de"
|
||||
STR_PICK_MODEL="Modelo por defecto:"
|
||||
|
||||
STR_TR_DIRECT_API="API directa del proveedor (clave)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/rol)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (deployment+clave)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="Local (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="Proxy (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="Suscripción OAuth (ChatGPT)"
|
||||
|
||||
STR_AUTH_INTRO="Autenticación para"
|
||||
STR_AUTH_PROMPT="Introduce los valores (Enter — dejar vacío, completar luego)."
|
||||
STR_AUTH_CURRENT_HINT="(actual: <oculto>)"
|
||||
|
||||
STR_DONE_TITLE="Configuración inicial completada"
|
||||
STR_DONE_CONFIG="config:"
|
||||
STR_DONE_SECRETS="secretos:"
|
||||
|
||||
STR_MENU_TITLE="Instalador de KeiSeiKit"
|
||||
STR_MENU_SUBSTRATE="Base del sustrato (siempre instalada):"
|
||||
STR_MENU_PROFILE_PROMPT="Elige el perfil de instalación:"
|
||||
STR_MENU_CONFIRM="¿Confirmar selección?"
|
||||
|
||||
STR_PREFLIGHT_FAILED="Preflight falló — el proveedor puede no funcionar."
|
||||
STR_PREFLIGHT_CONTINUE="¿Continuar de todos modos? [s/N]"
|
||||
35
install/i18n/fr.sh
Normal file
35
install/i18n/fr.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/fr.sh — Français.
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
STR_ONBOARDING_INTRO="Assistant de configuration initiale (5 étapes)"
|
||||
STR_PICK_LANGUAGE="Choisir la langue de l'interface :"
|
||||
STR_PICK_TRANSPORT="Choisir le mode de connexion :"
|
||||
STR_PICK_PROVIDER="Choisir le fournisseur dans le groupe"
|
||||
STR_PICK_MODEL="Modèle par défaut :"
|
||||
|
||||
STR_TR_DIRECT_API="API directe du fournisseur (clé)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/rôle)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (deployment+clé)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="Local (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="Proxy (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="Abonnement OAuth (ChatGPT)"
|
||||
|
||||
STR_AUTH_INTRO="Authentification pour"
|
||||
STR_AUTH_PROMPT="Saisir les valeurs (Entrée — laisser vide, remplir plus tard)."
|
||||
STR_AUTH_CURRENT_HINT="(actuel : <masqué>)"
|
||||
|
||||
STR_DONE_TITLE="Configuration initiale terminée"
|
||||
STR_DONE_CONFIG="config :"
|
||||
STR_DONE_SECRETS="secrets :"
|
||||
|
||||
STR_MENU_TITLE="Installateur KeiSeiKit"
|
||||
STR_MENU_SUBSTRATE="Base du substrat (toujours installée) :"
|
||||
STR_MENU_PROFILE_PROMPT="Choisir le profil d'installation :"
|
||||
STR_MENU_CONFIRM="Confirmer la sélection ?"
|
||||
|
||||
STR_PREFLIGHT_FAILED="Preflight échoué — le fournisseur peut ne pas fonctionner."
|
||||
STR_PREFLIGHT_CONTINUE="Continuer quand même ? [o/N]"
|
||||
35
install/i18n/hi.sh
Normal file
35
install/i18n/hi.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/hi.sh — हिन्दी.
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
STR_ONBOARDING_INTRO="प्रारंभिक सेटअप विज़ार्ड (5 चरण)"
|
||||
STR_PICK_LANGUAGE="इंटरफ़ेस भाषा चुनें:"
|
||||
STR_PICK_TRANSPORT="कनेक्शन विधि चुनें:"
|
||||
STR_PICK_PROVIDER="समूह में प्रदाता चुनें"
|
||||
STR_PICK_MODEL="डिफ़ॉल्ट मॉडल:"
|
||||
|
||||
STR_TR_DIRECT_API="प्रदाता डायरेक्ट API (कुंजी)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/रोल)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (deployment+कुंजी)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="लोकल (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="प्रॉक्सी (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="OAuth सब्सक्रिप्शन (ChatGPT)"
|
||||
|
||||
STR_AUTH_INTRO="के लिए प्रमाणीकरण"
|
||||
STR_AUTH_PROMPT="मान दर्ज करें (Enter — खाली छोड़ें, बाद में भरें)।"
|
||||
STR_AUTH_CURRENT_HINT="(वर्तमान: <छिपा हुआ>)"
|
||||
|
||||
STR_DONE_TITLE="प्रारंभिक सेटअप पूरा हुआ"
|
||||
STR_DONE_CONFIG="कॉन्फ़िग:"
|
||||
STR_DONE_SECRETS="सीक्रेट्स:"
|
||||
|
||||
STR_MENU_TITLE="KeiSeiKit इंस्टॉलर"
|
||||
STR_MENU_SUBSTRATE="सब्सट्रेट बेस (हमेशा स्थापित):"
|
||||
STR_MENU_PROFILE_PROMPT="इंस्टॉल प्रोफ़ाइल चुनें:"
|
||||
STR_MENU_CONFIRM="चयन की पुष्टि करें?"
|
||||
|
||||
STR_PREFLIGHT_FAILED="Preflight विफल — प्रदाता काम नहीं कर सकता।"
|
||||
STR_PREFLIGHT_CONTINUE="फिर भी जारी रखें? [y/N]"
|
||||
35
install/i18n/id.sh
Normal file
35
install/i18n/id.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/id.sh — Bahasa Indonesia.
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
STR_ONBOARDING_INTRO="Wizard pengaturan awal (5 langkah)"
|
||||
STR_PICK_LANGUAGE="Pilih bahasa antarmuka:"
|
||||
STR_PICK_TRANSPORT="Pilih metode koneksi:"
|
||||
STR_PICK_PROVIDER="Pilih penyedia dalam grup"
|
||||
STR_PICK_MODEL="Model default:"
|
||||
|
||||
STR_TR_DIRECT_API="API langsung penyedia (kunci)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/role)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (deployment+kunci)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="Lokal (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="Proxy (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="Langganan OAuth (ChatGPT)"
|
||||
|
||||
STR_AUTH_INTRO="Autentikasi untuk"
|
||||
STR_AUTH_PROMPT="Masukkan nilai (Enter — kosongkan, isi nanti)."
|
||||
STR_AUTH_CURRENT_HINT="(saat ini: <tersembunyi>)"
|
||||
|
||||
STR_DONE_TITLE="Pengaturan awal selesai"
|
||||
STR_DONE_CONFIG="konfig:"
|
||||
STR_DONE_SECRETS="rahasia:"
|
||||
|
||||
STR_MENU_TITLE="Installer KeiSeiKit"
|
||||
STR_MENU_SUBSTRATE="Basis substrat (selalu terpasang):"
|
||||
STR_MENU_PROFILE_PROMPT="Pilih profil instalasi:"
|
||||
STR_MENU_CONFIRM="Konfirmasi pilihan?"
|
||||
|
||||
STR_PREFLIGHT_FAILED="Preflight gagal — penyedia mungkin tidak berfungsi."
|
||||
STR_PREFLIGHT_CONTINUE="Lanjutkan saja? [y/N]"
|
||||
35
install/i18n/it.sh
Normal file
35
install/i18n/it.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/it.sh — Italiano.
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
STR_ONBOARDING_INTRO="Procedura guidata di configurazione iniziale (5 passi)"
|
||||
STR_PICK_LANGUAGE="Scegli la lingua dell'interfaccia:"
|
||||
STR_PICK_TRANSPORT="Scegli il metodo di connessione:"
|
||||
STR_PICK_PROVIDER="Scegli il provider nel gruppo"
|
||||
STR_PICK_MODEL="Modello predefinito:"
|
||||
|
||||
STR_TR_DIRECT_API="API diretta del provider (chiave)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/ruolo)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (deployment+chiave)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="Locale (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="Proxy (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="Abbonamento OAuth (ChatGPT)"
|
||||
|
||||
STR_AUTH_INTRO="Autenticazione per"
|
||||
STR_AUTH_PROMPT="Inserisci i valori (Invio — lascia vuoto, compila dopo)."
|
||||
STR_AUTH_CURRENT_HINT="(attuale: <nascosto>)"
|
||||
|
||||
STR_DONE_TITLE="Configurazione iniziale completata"
|
||||
STR_DONE_CONFIG="config:"
|
||||
STR_DONE_SECRETS="segreti:"
|
||||
|
||||
STR_MENU_TITLE="Installatore KeiSeiKit"
|
||||
STR_MENU_SUBSTRATE="Base del substrato (sempre installata):"
|
||||
STR_MENU_PROFILE_PROMPT="Scegli il profilo di installazione:"
|
||||
STR_MENU_CONFIRM="Confermare la selezione?"
|
||||
|
||||
STR_PREFLIGHT_FAILED="Preflight fallito — il provider potrebbe non funzionare."
|
||||
STR_PREFLIGHT_CONTINUE="Continuare comunque? [s/N]"
|
||||
35
install/i18n/ja.sh
Normal file
35
install/i18n/ja.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/ja.sh — 日本語.
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
STR_ONBOARDING_INTRO="初期設定ウィザード (5 ステップ)"
|
||||
STR_PICK_LANGUAGE="インターフェース言語を選択:"
|
||||
STR_PICK_TRANSPORT="接続方法を選択:"
|
||||
STR_PICK_PROVIDER="グループ内のプロバイダを選択"
|
||||
STR_PICK_MODEL="デフォルトモデル:"
|
||||
|
||||
STR_TR_DIRECT_API="プロバイダ直接 API (キー)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/ロール)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (deployment+キー)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="ローカル (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="プロキシ (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="OAuth サブスクリプション (ChatGPT)"
|
||||
|
||||
STR_AUTH_INTRO="認証 —"
|
||||
STR_AUTH_PROMPT="値を入力 (Enter — 空のまま、後で記入)。"
|
||||
STR_AUTH_CURRENT_HINT="(現在: <非表示>)"
|
||||
|
||||
STR_DONE_TITLE="初期設定が完了しました"
|
||||
STR_DONE_CONFIG="設定:"
|
||||
STR_DONE_SECRETS="シークレット:"
|
||||
|
||||
STR_MENU_TITLE="KeiSeiKit インストーラ"
|
||||
STR_MENU_SUBSTRATE="サブストレートのベース (常にインストール):"
|
||||
STR_MENU_PROFILE_PROMPT="インストールプロファイルを選択:"
|
||||
STR_MENU_CONFIRM="選択を確認しますか?"
|
||||
|
||||
STR_PREFLIGHT_FAILED="Preflight 失敗 — プロバイダが動作しない可能性があります。"
|
||||
STR_PREFLIGHT_CONTINUE="それでも続行しますか? [y/N]"
|
||||
35
install/i18n/ko.sh
Normal file
35
install/i18n/ko.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/ko.sh — 한국어.
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
STR_ONBOARDING_INTRO="초기 설정 마법사 (5 단계)"
|
||||
STR_PICK_LANGUAGE="인터페이스 언어 선택:"
|
||||
STR_PICK_TRANSPORT="연결 방법 선택:"
|
||||
STR_PICK_PROVIDER="그룹 내 제공자 선택"
|
||||
STR_PICK_MODEL="기본 모델:"
|
||||
|
||||
STR_TR_DIRECT_API="제공자 직접 API (키)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/역할)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (deployment+키)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="로컬 (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="프록시 (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="OAuth 구독 (ChatGPT)"
|
||||
|
||||
STR_AUTH_INTRO="인증 —"
|
||||
STR_AUTH_PROMPT="값을 입력하세요 (Enter — 비워두고 나중에 작성)."
|
||||
STR_AUTH_CURRENT_HINT="(현재: <숨김>)"
|
||||
|
||||
STR_DONE_TITLE="초기 설정 완료"
|
||||
STR_DONE_CONFIG="설정:"
|
||||
STR_DONE_SECRETS="시크릿:"
|
||||
|
||||
STR_MENU_TITLE="KeiSeiKit 설치 프로그램"
|
||||
STR_MENU_SUBSTRATE="기본 서브스트레이트 (항상 설치):"
|
||||
STR_MENU_PROFILE_PROMPT="설치 프로필 선택:"
|
||||
STR_MENU_CONFIRM="선택을 확인하시겠습니까?"
|
||||
|
||||
STR_PREFLIGHT_FAILED="Preflight 실패 — 제공자가 작동하지 않을 수 있습니다."
|
||||
STR_PREFLIGHT_CONTINUE="계속 진행하시겠습니까? [y/N]"
|
||||
35
install/i18n/pt.sh
Normal file
35
install/i18n/pt.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/pt.sh — Português.
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
STR_ONBOARDING_INTRO="Assistente de configuração inicial (5 passos)"
|
||||
STR_PICK_LANGUAGE="Escolha o idioma da interface:"
|
||||
STR_PICK_TRANSPORT="Escolha o método de conexão:"
|
||||
STR_PICK_PROVIDER="Escolha o provedor dentro do grupo"
|
||||
STR_PICK_MODEL="Modelo padrão:"
|
||||
|
||||
STR_TR_DIRECT_API="API direta do provedor (chave)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/função)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (deployment+chave)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="Local (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="Proxy (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="Assinatura OAuth (ChatGPT)"
|
||||
|
||||
STR_AUTH_INTRO="Autenticação para"
|
||||
STR_AUTH_PROMPT="Insira valores (Enter — deixar vazio, preencher depois)."
|
||||
STR_AUTH_CURRENT_HINT="(atual: <oculto>)"
|
||||
|
||||
STR_DONE_TITLE="Configuração inicial concluída"
|
||||
STR_DONE_CONFIG="config:"
|
||||
STR_DONE_SECRETS="segredos:"
|
||||
|
||||
STR_MENU_TITLE="Instalador KeiSeiKit"
|
||||
STR_MENU_SUBSTRATE="Base do substrato (sempre instalada):"
|
||||
STR_MENU_PROFILE_PROMPT="Escolha o perfil de instalação:"
|
||||
STR_MENU_CONFIRM="Confirmar seleção?"
|
||||
|
||||
STR_PREFLIGHT_FAILED="Preflight falhou — o provedor pode não funcionar."
|
||||
STR_PREFLIGHT_CONTINUE="Continuar mesmo assim? [s/N]"
|
||||
42
install/i18n/ru.sh
Normal file
42
install/i18n/ru.sh
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/ru.sh — русские строки. Source'ится после выбора языка.
|
||||
# Welcome-баннер всегда EN — на момент его показа выбор ещё не сделан.
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
# Шаги мастера
|
||||
STR_ONBOARDING_INTRO="Мастер первичной настройки (5 шагов)"
|
||||
STR_PICK_LANGUAGE="Выберите язык интерфейса:"
|
||||
STR_PICK_TRANSPORT="Выберите способ подключения:"
|
||||
STR_PICK_PROVIDER="Выберите провайдера в группе"
|
||||
STR_PICK_MODEL="Модель по умолчанию:"
|
||||
|
||||
# Описание транспортов
|
||||
STR_TR_DIRECT_API="Прямой API провайдера (ключ)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/role)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (deployment+ключ)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="Локально (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="Прокси (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="OAuth-подписка (ChatGPT)"
|
||||
|
||||
# Сбор ключей
|
||||
STR_AUTH_INTRO="Аутентификация для"
|
||||
STR_AUTH_PROMPT="Введите значения (Enter — оставить пустым, заполните позже)."
|
||||
STR_AUTH_CURRENT_HINT="(текущее: <скрыто>)"
|
||||
|
||||
# Завершение
|
||||
STR_DONE_TITLE="Первичная настройка завершена"
|
||||
STR_DONE_CONFIG="конфиг:"
|
||||
STR_DONE_SECRETS="секреты:"
|
||||
|
||||
# Меню профилей (lib-menu.sh)
|
||||
STR_MENU_TITLE="Установщик KeiSeiKit"
|
||||
STR_MENU_SUBSTRATE="Базовая часть (ставится всегда):"
|
||||
STR_MENU_PROFILE_PROMPT="Выберите профиль установки:"
|
||||
STR_MENU_CONFIRM="Подтвердить выбор?"
|
||||
|
||||
# Preflight-предупреждения
|
||||
STR_PREFLIGHT_FAILED="Preflight упал — провайдер может не работать."
|
||||
STR_PREFLIGHT_CONTINUE="Продолжить всё равно? [y/N]"
|
||||
35
install/i18n/tr.sh
Normal file
35
install/i18n/tr.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/tr.sh — Türkçe.
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
STR_ONBOARDING_INTRO="İlk kurulum sihirbazı (5 adım)"
|
||||
STR_PICK_LANGUAGE="Arayüz dilini seçin:"
|
||||
STR_PICK_TRANSPORT="Bağlantı yöntemini seçin:"
|
||||
STR_PICK_PROVIDER="Gruptaki sağlayıcıyı seçin"
|
||||
STR_PICK_MODEL="Varsayılan model:"
|
||||
|
||||
STR_TR_DIRECT_API="Sağlayıcının doğrudan API'si (anahtar)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/rol)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (deployment+anahtar)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="Yerel (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="Proxy (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="OAuth aboneliği (ChatGPT)"
|
||||
|
||||
STR_AUTH_INTRO="Kimlik doğrulama —"
|
||||
STR_AUTH_PROMPT="Değerleri girin (Enter — boş bırak, sonra doldur)."
|
||||
STR_AUTH_CURRENT_HINT="(geçerli: <gizli>)"
|
||||
|
||||
STR_DONE_TITLE="İlk kurulum tamamlandı"
|
||||
STR_DONE_CONFIG="yapılandırma:"
|
||||
STR_DONE_SECRETS="gizli anahtarlar:"
|
||||
|
||||
STR_MENU_TITLE="KeiSeiKit Kurulum Aracı"
|
||||
STR_MENU_SUBSTRATE="Substrate tabanı (her zaman kurulu):"
|
||||
STR_MENU_PROFILE_PROMPT="Kurulum profilini seçin:"
|
||||
STR_MENU_CONFIRM="Seçimi onayla?"
|
||||
|
||||
STR_PREFLIGHT_FAILED="Preflight başarısız — sağlayıcı çalışmayabilir."
|
||||
STR_PREFLIGHT_CONTINUE="Yine de devam edilsin mi? [e/H]"
|
||||
35
install/i18n/uk.sh
Normal file
35
install/i18n/uk.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/uk.sh — Українська.
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
STR_ONBOARDING_INTRO="Майстер первинного налаштування (5 кроків)"
|
||||
STR_PICK_LANGUAGE="Виберіть мову інтерфейсу:"
|
||||
STR_PICK_TRANSPORT="Виберіть спосіб підключення:"
|
||||
STR_PICK_PROVIDER="Виберіть провайдера у групі"
|
||||
STR_PICK_MODEL="Модель за замовчуванням:"
|
||||
|
||||
STR_TR_DIRECT_API="Прямий API провайдера (ключ)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/роль)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (deployment+ключ)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="Локально (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="Проксі (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="OAuth-підписка (ChatGPT)"
|
||||
|
||||
STR_AUTH_INTRO="Автентифікація для"
|
||||
STR_AUTH_PROMPT="Введіть значення (Enter — залишити порожнім, заповнити пізніше)."
|
||||
STR_AUTH_CURRENT_HINT="(поточне: <приховано>)"
|
||||
|
||||
STR_DONE_TITLE="Первинне налаштування завершено"
|
||||
STR_DONE_CONFIG="конфіг:"
|
||||
STR_DONE_SECRETS="секрети:"
|
||||
|
||||
STR_MENU_TITLE="Інсталятор KeiSeiKit"
|
||||
STR_MENU_SUBSTRATE="База підкладки (завжди встановлюється):"
|
||||
STR_MENU_PROFILE_PROMPT="Виберіть профіль встановлення:"
|
||||
STR_MENU_CONFIRM="Підтвердити вибір?"
|
||||
|
||||
STR_PREFLIGHT_FAILED="Preflight упав — провайдер може не працювати."
|
||||
STR_PREFLIGHT_CONTINUE="Продовжити все одно? [y/N]"
|
||||
35
install/i18n/vi.sh
Normal file
35
install/i18n/vi.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/vi.sh — Tiếng Việt.
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
STR_ONBOARDING_INTRO="Trình hướng dẫn thiết lập ban đầu (5 bước)"
|
||||
STR_PICK_LANGUAGE="Chọn ngôn ngữ giao diện:"
|
||||
STR_PICK_TRANSPORT="Chọn phương thức kết nối:"
|
||||
STR_PICK_PROVIDER="Chọn nhà cung cấp trong nhóm"
|
||||
STR_PICK_MODEL="Mô hình mặc định:"
|
||||
|
||||
STR_TR_DIRECT_API="API trực tiếp của nhà cung cấp (khóa)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/vai trò)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (deployment+khóa)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="Cục bộ (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="Proxy (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="Đăng ký OAuth (ChatGPT)"
|
||||
|
||||
STR_AUTH_INTRO="Xác thực cho"
|
||||
STR_AUTH_PROMPT="Nhập giá trị (Enter — để trống, điền sau)."
|
||||
STR_AUTH_CURRENT_HINT="(hiện tại: <đã ẩn>)"
|
||||
|
||||
STR_DONE_TITLE="Thiết lập ban đầu hoàn tất"
|
||||
STR_DONE_CONFIG="cấu hình:"
|
||||
STR_DONE_SECRETS="bí mật:"
|
||||
|
||||
STR_MENU_TITLE="Trình cài đặt KeiSeiKit"
|
||||
STR_MENU_SUBSTRATE="Nền tảng substrate (luôn cài đặt):"
|
||||
STR_MENU_PROFILE_PROMPT="Chọn hồ sơ cài đặt:"
|
||||
STR_MENU_CONFIRM="Xác nhận lựa chọn?"
|
||||
|
||||
STR_PREFLIGHT_FAILED="Preflight thất bại — nhà cung cấp có thể không hoạt động."
|
||||
STR_PREFLIGHT_CONTINUE="Vẫn tiếp tục? [y/N]"
|
||||
35
install/i18n/zh.sh
Normal file
35
install/i18n/zh.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# shellcheck shell=bash
|
||||
# i18n/zh.sh — 简体中文 (Simplified Chinese).
|
||||
|
||||
STR_WELCOME_TITLE="KeiSeiKit · Exobrain installer"
|
||||
STR_WELCOME_TAGLINE="Portable Rust agent substrate for AI coding tools"
|
||||
|
||||
STR_ONBOARDING_INTRO="初始化向导 (5 步)"
|
||||
STR_PICK_LANGUAGE="选择界面语言:"
|
||||
STR_PICK_TRANSPORT="选择连接方式:"
|
||||
STR_PICK_PROVIDER="选择此分组中的提供者"
|
||||
STR_PICK_MODEL="默认模型:"
|
||||
|
||||
STR_TR_DIRECT_API="提供者直连 API (密钥)"
|
||||
STR_TR_AWS_BEDROCK="AWS Bedrock (IAM/角色)"
|
||||
STR_TR_AZURE_OPENAI="Azure OpenAI (部署 + 密钥)"
|
||||
STR_TR_GOOGLE_VERTEX="Google Vertex AI (GCP)"
|
||||
STR_TR_LOCAL="本地 (Ollama/MLX/LMStudio)"
|
||||
STR_TR_PROXY="代理 (LiteLLM/OpenRouter)"
|
||||
STR_TR_SUBSCRIPTION="OAuth 订阅 (ChatGPT)"
|
||||
|
||||
STR_AUTH_INTRO="认证"
|
||||
STR_AUTH_PROMPT="输入值 (回车 — 保留为空,稍后填写)。"
|
||||
STR_AUTH_CURRENT_HINT="(当前: <已隐藏>)"
|
||||
|
||||
STR_DONE_TITLE="初始化完成"
|
||||
STR_DONE_CONFIG="配置:"
|
||||
STR_DONE_SECRETS="密钥:"
|
||||
|
||||
STR_MENU_TITLE="KeiSeiKit 安装器"
|
||||
STR_MENU_SUBSTRATE="基础组件 (始终安装):"
|
||||
STR_MENU_PROFILE_PROMPT="选择安装配置:"
|
||||
STR_MENU_CONFIRM="确认选择?"
|
||||
|
||||
STR_PREFLIGHT_FAILED="Preflight 失败 — 提供者可能无法工作。"
|
||||
STR_PREFLIGHT_CONTINUE="仍然继续? [y/N]"
|
||||
65
install/lib-i18n.sh
Normal file
65
install/lib-i18n.sh
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# shellcheck shell=bash
|
||||
# lib-i18n.sh — лоадер локализаций.
|
||||
#
|
||||
# Контракт:
|
||||
# 1. На старте всегда source install/i18n/en.sh — экран приветствия
|
||||
# показывается ДО выбора языка пользователем.
|
||||
# 2. После onboarding_pick_language вызывается i18n_load_lang "$lang" —
|
||||
# перегружает строки выбранного словаря.
|
||||
# 3. Любая строка отсутствующая в словаре — fallback на en.sh уже в
|
||||
# памяти (мы не unset'им переменные, ru перезаписывает поверх).
|
||||
#
|
||||
# Используется install.sh и install/lib-onboarding.sh.
|
||||
|
||||
# Корень i18n относительно LIB_DIR.
|
||||
I18N_DIR="${LIB_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}/i18n"
|
||||
|
||||
i18n_load_default() {
|
||||
# shellcheck source=install/i18n/en.sh
|
||||
source "$I18N_DIR/en.sh"
|
||||
}
|
||||
|
||||
i18n_load_lang() {
|
||||
local lang="$1"
|
||||
# Сначала всегда грузим английский — это база fallback.
|
||||
i18n_load_default
|
||||
# Если выбран не-английский — поверх кладём словарь языка.
|
||||
# Любой STR_*, отсутствующий в файле, остаётся с английским значением.
|
||||
if [ "$lang" != "en" ] && [ -f "$I18N_DIR/${lang}.sh" ]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$I18N_DIR/${lang}.sh"
|
||||
fi
|
||||
}
|
||||
|
||||
# Список доступных языков — для onboarding_pick_language.
|
||||
# Формат: <code>\t<display_name>
|
||||
i18n_available_languages() {
|
||||
cat <<'EOF'
|
||||
en English
|
||||
ru Русский
|
||||
uk Українська
|
||||
de Deutsch
|
||||
fr Français
|
||||
es Español
|
||||
pt Português
|
||||
it Italiano
|
||||
tr Türkçe
|
||||
ar العربية
|
||||
hi हिन्दी
|
||||
zh 简体中文
|
||||
ja 日本語
|
||||
ko 한국어
|
||||
id Bahasa Indonesia
|
||||
vi Tiếng Việt
|
||||
EOF
|
||||
}
|
||||
|
||||
# Welcome banner. Всегда EN. Запускается из install.sh до мастера.
|
||||
i18n_print_welcome() {
|
||||
echo ""
|
||||
echo " ╔═══════════════════════════════════════════════════════╗"
|
||||
echo " ║ ${STR_WELCOME_TITLE} ║"
|
||||
echo " ║ ${STR_WELCOME_TAGLINE} ║"
|
||||
echo " ╚═══════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
}
|
||||
|
|
@ -30,8 +30,8 @@ menu_should_skip() {
|
|||
# Profile choice = how many ADDITIONAL primitive binaries to add on top.
|
||||
menu_whiptail_profile() {
|
||||
local tool="$1"
|
||||
"$tool" --title "KeiSeiKit Installer — substrate always installed; profile = primitives ADDED on top" --radiolist \
|
||||
"Choose install profile (SPACE to select, ENTER to confirm):" 28 86 12 \
|
||||
"$tool" --title "${STR_MENU_TITLE:-KeiSeiKit Installer} — ${STR_MENU_SUBSTRATE:-substrate always installed; profile = primitives ADDED on top}" --radiolist \
|
||||
"${STR_MENU_PROFILE_PROMPT:-Choose install profile (SPACE to select, ENTER to confirm):}" 28 86 12 \
|
||||
"minimal" "substrate only — 0 primitives (~5s)" ON \
|
||||
"core" "+ 2 primitives (tomd, kei-doctor) (~5s)" OFF \
|
||||
"frontend" "+ 8 site tools — mock-render, visual-diff, figma-tokens" OFF \
|
||||
|
|
@ -69,15 +69,15 @@ menu_whiptail_custom() {
|
|||
# plain-text profile picker → profile name. Exits 1 on cancel.
|
||||
menu_plain_profile() {
|
||||
echo "============================================================" >&2
|
||||
echo " KeiSeiKit Installer" >&2
|
||||
echo " ${STR_MENU_TITLE:-KeiSeiKit Installer}" >&2
|
||||
echo "============================================================" >&2
|
||||
echo >&2
|
||||
echo " Substrate baseline (ALWAYS installed, regardless of profile):" >&2
|
||||
echo " ${STR_MENU_SUBSTRATE:-Substrate baseline (ALWAYS installed):}" >&2
|
||||
echo " • 37 agent manifests • 67 skills • 39 hooks" >&2
|
||||
echo " • 82 blocks • 16 caps • 7 roles" >&2
|
||||
echo " • 11 cross-tool bridges (Cursor / Copilot / Codex / Aider / …)" >&2
|
||||
echo >&2
|
||||
echo " Profile = primitive binaries ADDED on top of substrate." >&2
|
||||
echo " ${STR_MENU_PROFILE_PROMPT:-Profile = primitive binaries ADDED on top of substrate.}" >&2
|
||||
echo "------------------------------------------------------------" >&2
|
||||
echo >&2
|
||||
echo " Standard:" >&2
|
||||
|
|
|
|||
79
install/lib-onboarding-registry.sh
Normal file
79
install/lib-onboarding-registry.sh
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# shellcheck shell=bash
|
||||
# lib-onboarding-registry.sh — парсеры реестров providers.toml + models.toml.
|
||||
#
|
||||
# Constructor Pattern: 1 файл = парсинг реестров. UI и state — в соседних кубах.
|
||||
#
|
||||
# Источник: $KIT_DIR/_blocks/registries/{providers,models}.toml (submodule
|
||||
# kei-registries). Если файла нет — fallback на захардкоженный набор
|
||||
# покрывающий все 7 транспортов.
|
||||
#
|
||||
# Глобалы (общие с lib-onboarding-*):
|
||||
# REGISTRY_PROVIDERS — путь к providers.toml
|
||||
# REGISTRY_MODELS — путь к models.toml
|
||||
|
||||
REGISTRY_PROVIDERS="${REGISTRY_PROVIDERS:-$KIT_DIR/_blocks/registries/providers.toml}"
|
||||
REGISTRY_MODELS="${REGISTRY_MODELS:-$KIT_DIR/_blocks/registries/models.toml}"
|
||||
|
||||
# Парсер providers.toml. Простой awk-граббер по [[provider]] секциям.
|
||||
# Печатает: <id>\t<transport>\t<display_name>\t<auth_env>
|
||||
onboarding_list_providers() {
|
||||
[ -f "$REGISTRY_PROVIDERS" ] || { onboarding_fallback_providers; return; }
|
||||
awk '
|
||||
/^\[\[provider\]\]/ { id=""; tr=""; dn=""; ae=""; next }
|
||||
/^id[[:space:]]*=/ { gsub(/^id[[:space:]]*=[[:space:]]*"/, ""); gsub(/".*$/, ""); id=$0 }
|
||||
/^transport[[:space:]]*=/ { gsub(/^transport[[:space:]]*=[[:space:]]*"/, ""); gsub(/".*$/, ""); tr=$0 }
|
||||
/^display_name[[:space:]]*=/ { gsub(/^display_name[[:space:]]*=[[:space:]]*"/, ""); gsub(/".*$/, ""); dn=$0 }
|
||||
/^auth_env[[:space:]]*=/ { gsub(/^auth_env[[:space:]]*=[[:space:]]*"/, ""); gsub(/".*$/, ""); ae=$0;
|
||||
if (id && tr) print id "\t" tr "\t" dn "\t" ae }
|
||||
' "$REGISTRY_PROVIDERS"
|
||||
}
|
||||
|
||||
# Fallback если submodule не подтянут.
|
||||
# Покрывает 7 транспортов минимальными представителями. Синхронизировать
|
||||
# вручную если в реестре появится новый транспорт-тип.
|
||||
onboarding_fallback_providers() {
|
||||
printf "anthropic\tdirect-api\tAnthropic (Direct API)\tANTHROPIC_API_KEY\n"
|
||||
printf "anthropic-bedrock\taws-bedrock\tAnthropic (AWS Bedrock)\tAWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_REGION\n"
|
||||
printf "openai\tdirect-api\tOpenAI (Direct API)\tOPENAI_API_KEY\n"
|
||||
printf "openai-azure\tazure-openai\tOpenAI (Azure)\tAZURE_OPENAI_API_KEY,AZURE_OPENAI_ENDPOINT,AZURE_OPENAI_DEPLOYMENT\n"
|
||||
printf "xai\tdirect-api\txAI\tXAI_API_KEY\n"
|
||||
printf "deepseek\tdirect-api\tDeepSeek\tDEEPSEEK_API_KEY\n"
|
||||
printf "google\tdirect-api\tGoogle Gemini (Direct API)\tGEMINI_API_KEY\n"
|
||||
printf "google-vertex\tgoogle-vertex\tGoogle Gemini (Vertex AI)\tGOOGLE_APPLICATION_CREDENTIALS,GCP_PROJECT_ID,GCP_REGION\n"
|
||||
printf "ollama-local\tlocal\tOllama (local)\t_\n"
|
||||
printf "mlx-local\tlocal\tMLX (Apple silicon local)\t_\n"
|
||||
printf "lmstudio-local\tlocal\tLM Studio (local)\t_\n"
|
||||
printf "litellm-proxy\tproxy\tLiteLLM proxy (keisei.app)\tKEI_LITELLM_KEY\n"
|
||||
printf "openrouter\tproxy\tOpenRouter\tOPENROUTER_API_KEY\n"
|
||||
printf "codex\tsubscription\tOpenAI Codex (ChatGPT OAuth)\t_\n"
|
||||
}
|
||||
|
||||
# Уникальные транспорты — для первого экрана выбора.
|
||||
onboarding_list_transports() {
|
||||
onboarding_list_providers | awk -F'\t' '{print $2}' | sort -u
|
||||
}
|
||||
|
||||
# Провайдеры внутри транспорта.
|
||||
onboarding_providers_in_transport() {
|
||||
local tr="$1"
|
||||
onboarding_list_providers | awk -F'\t' -v t="$tr" '$2==t {print $1 "\t" $3 "\t" $4}'
|
||||
}
|
||||
|
||||
# Модели по provider_ref.
|
||||
onboarding_models_for_provider() {
|
||||
local pr="$1"
|
||||
[ -f "$REGISTRY_MODELS" ] || { printf "claude-sonnet-4-6\tClaude Sonnet 4.6\n"; return; }
|
||||
awk -v pr="$pr" '
|
||||
/^\[\[model\]\]/ { id=""; pref=""; dn=""; next }
|
||||
/^id[[:space:]]*=/ { gsub(/^id[[:space:]]*=[[:space:]]*"/, ""); gsub(/".*$/, ""); id=$0 }
|
||||
/^provider_ref[[:space:]]*=/ { gsub(/^provider_ref[[:space:]]*=[[:space:]]*"/, ""); gsub(/".*$/, ""); pref=$0 }
|
||||
/^display_name[[:space:]]*=/ { gsub(/^display_name[[:space:]]*=[[:space:]]*"/, ""); gsub(/".*$/, ""); dn=$0;
|
||||
if (pref==pr) print id "\t" dn }
|
||||
' "$REGISTRY_MODELS"
|
||||
}
|
||||
|
||||
# auth_env для одного провайдера (для onboarding_collect_auth).
|
||||
onboarding_auth_env_for_provider() {
|
||||
local p="$1"
|
||||
onboarding_list_providers | awk -F'\t' -v p="$p" '$1==p {print $4}'
|
||||
}
|
||||
57
install/lib-onboarding-state.sh
Normal file
57
install/lib-onboarding-state.sh
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# shellcheck shell=bash
|
||||
# lib-onboarding-state.sh — запись результата мастера на диск.
|
||||
#
|
||||
# Constructor Pattern: 1 файл = state-запись. UI — в ui.sh, парсеры — в registry.sh.
|
||||
#
|
||||
# Пишет:
|
||||
# ~/.claude/.onboarded — флаг прохождения
|
||||
# ~/.claude/config/onboarding.toml — выбор lang/transport/provider/model
|
||||
# ~/.claude/config/user-model-override.toml — для kei-model-router (HIGH аудит-1)
|
||||
# ~/.claude/secrets/.env — добавляет ключи провайдера (chmod 600)
|
||||
|
||||
ONBOARDED_FLAG="${ONBOARDED_FLAG:-$HOME/.claude/.onboarded}"
|
||||
ONBOARDING_CONFIG="${ONBOARDING_CONFIG:-$HOME/.claude/config/onboarding.toml}"
|
||||
SECRETS_ENV="${SECRETS_ENV:-$HOME/.claude/secrets/.env}"
|
||||
|
||||
onboarding_write_secrets() {
|
||||
[ "${#ONBOARDING_AUTH_ENV_KEYS[@]}" = "0" ] && return
|
||||
mkdir -p "$(dirname "$SECRETS_ENV")"
|
||||
touch "$SECRETS_ENV"; chmod 600 "$SECRETS_ENV"
|
||||
local i
|
||||
for i in "${!ONBOARDING_AUTH_ENV_KEYS[@]}"; do
|
||||
local k="${ONBOARDING_AUTH_ENV_KEYS[$i]}"
|
||||
local v="${ONBOARDING_AUTH_ENV_VALUES[$i]}"
|
||||
if grep -q "^${k}=" "$SECRETS_ENV" 2>/dev/null; then
|
||||
grep -v "^${k}=" "$SECRETS_ENV" > "$SECRETS_ENV.tmp"
|
||||
mv "$SECRETS_ENV.tmp" "$SECRETS_ENV"
|
||||
fi
|
||||
printf '%s=%s\n' "$k" "$v" >> "$SECRETS_ENV"
|
||||
done
|
||||
chmod 600 "$SECRETS_ENV"
|
||||
}
|
||||
|
||||
onboarding_write_config() {
|
||||
mkdir -p "$(dirname "$ONBOARDING_CONFIG")"
|
||||
cat > "$ONBOARDING_CONFIG" <<EOF
|
||||
# KeiSeiKit onboarding choices. Auto-generated by lib-onboarding.sh.
|
||||
# Re-run wizard: rm ~/.claude/.onboarded && ./install.sh
|
||||
language = "$ONBOARDING_LANG"
|
||||
transport = "$ONBOARDING_TRANSPORT"
|
||||
provider = "$ONBOARDING_PROVIDER"
|
||||
default_model = "$ONBOARDING_MODEL"
|
||||
EOF
|
||||
|
||||
# Override для kei-model-router (HIGH аудит-1).
|
||||
# Приоритет: --pinned flag > этот файл > agent-profiles.toml default_model_ref.
|
||||
local override_path="$HOME/.claude/config/user-model-override.toml"
|
||||
cat > "$override_path" <<EOF
|
||||
# User-tier model override. Auto-generated by onboarding wizard.
|
||||
# Format: kei-model-router::Registry::load_user_override().
|
||||
# Priority: --pinned flag > этот файл > agent-profiles.toml default_model_ref.
|
||||
provider = "$ONBOARDING_PROVIDER"
|
||||
model = "$ONBOARDING_MODEL"
|
||||
transport = "$ONBOARDING_TRANSPORT"
|
||||
EOF
|
||||
|
||||
: > "$ONBOARDED_FLAG"
|
||||
}
|
||||
189
install/lib-onboarding-ui.sh
Normal file
189
install/lib-onboarding-ui.sh
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
# shellcheck shell=bash
|
||||
# lib-onboarding-ui.sh — pick_* функции мастера (whiptail / bash select).
|
||||
#
|
||||
# Constructor Pattern: 1 файл = UI слой. Парсеры реестров — в registry.sh,
|
||||
# state-запись — в state.sh.
|
||||
#
|
||||
# Заполняет глобалы:
|
||||
# ONBOARDING_LANG, ONBOARDING_TRANSPORT, ONBOARDING_PROVIDER, ONBOARDING_MODEL
|
||||
# ONBOARDING_AUTH_ENV_KEYS[] + ONBOARDING_AUTH_ENV_VALUES[]
|
||||
#
|
||||
# Использует:
|
||||
# - lib-i18n.sh: STR_* словарь + i18n_available_languages + i18n_load_lang
|
||||
# - lib-onboarding-registry.sh: списки провайдеров/моделей
|
||||
|
||||
onboarding_pick_language() {
|
||||
local langs
|
||||
langs="$(i18n_available_languages 2>/dev/null)"
|
||||
if [ -z "$langs" ]; then
|
||||
langs="$(printf 'en\tEnglish\nru\tРусский\n')"
|
||||
fi
|
||||
|
||||
if command -v whiptail >/dev/null 2>&1; then
|
||||
local args=() first=1
|
||||
while IFS=$'\t' read -r code name; do
|
||||
[ -z "$code" ] && continue
|
||||
if [ "$first" = "1" ]; then
|
||||
args+=("$code" "$name" "ON"); first=0
|
||||
else
|
||||
args+=("$code" "$name" "OFF")
|
||||
fi
|
||||
done <<< "$langs"
|
||||
ONBOARDING_LANG=$(whiptail --title "KeiSei · Language / Язык / 语言 / 言語 / ..." --radiolist \
|
||||
"Choose interface language / Выберите язык:" 22 70 16 \
|
||||
"${args[@]}" 3>&1 1>&2 2>&3) || ONBOARDING_LANG="en"
|
||||
else
|
||||
echo "" >&2
|
||||
echo "Choose language / Выберите язык / 选择语言 / 言語選択:" >&2
|
||||
declare -a codes=()
|
||||
local i=1
|
||||
while IFS=$'\t' read -r code name; do
|
||||
[ -z "$code" ] && continue
|
||||
codes+=("$code")
|
||||
printf " %2d) %s — %s\n" "$i" "$code" "$name" >&2
|
||||
i=$((i+1))
|
||||
done <<< "$langs"
|
||||
read -r -p "[1-${#codes[@]}, default 1=en]: " ans
|
||||
ans="${ans:-1}"
|
||||
ONBOARDING_LANG="${codes[$((ans-1))]:-en}"
|
||||
fi
|
||||
command -v i18n_load_lang >/dev/null 2>&1 && i18n_load_lang "$ONBOARDING_LANG"
|
||||
}
|
||||
|
||||
onboarding_pick_transport() {
|
||||
local transports
|
||||
transports=$(onboarding_list_transports)
|
||||
local prompt="${STR_PICK_TRANSPORT:-Choose connection transport:}"
|
||||
|
||||
if command -v whiptail >/dev/null 2>&1; then
|
||||
local args=()
|
||||
while IFS= read -r tr; do
|
||||
local desc
|
||||
case "$tr" in
|
||||
direct-api) desc="${STR_TR_DIRECT_API:-Direct provider API}" ;;
|
||||
aws-bedrock) desc="${STR_TR_AWS_BEDROCK:-AWS Bedrock}" ;;
|
||||
azure-openai) desc="${STR_TR_AZURE_OPENAI:-Azure OpenAI}" ;;
|
||||
google-vertex) desc="${STR_TR_GOOGLE_VERTEX:-Google Vertex AI}" ;;
|
||||
local) desc="${STR_TR_LOCAL:-Local}" ;;
|
||||
proxy) desc="${STR_TR_PROXY:-Proxy}" ;;
|
||||
subscription) desc="${STR_TR_SUBSCRIPTION:-OAuth subscription}" ;;
|
||||
*) desc="$tr" ;;
|
||||
esac
|
||||
args+=("$tr" "$desc" "OFF")
|
||||
done <<< "$transports"
|
||||
ONBOARDING_TRANSPORT=$(whiptail --title "KeiSei · Transport" --radiolist \
|
||||
"$prompt" 18 70 7 "${args[@]}" 3>&1 1>&2 2>&3) || ONBOARDING_TRANSPORT="direct-api"
|
||||
else
|
||||
echo "" >&2
|
||||
echo "$prompt" >&2
|
||||
local i=1
|
||||
declare -a opts=()
|
||||
while IFS= read -r tr; do
|
||||
opts+=("$tr")
|
||||
echo " $i) $tr" >&2
|
||||
i=$((i+1))
|
||||
done <<< "$transports"
|
||||
read -r -p "[1-${#opts[@]}, default 1]: " ans
|
||||
ans="${ans:-1}"
|
||||
ONBOARDING_TRANSPORT="${opts[$((ans-1))]:-direct-api}"
|
||||
fi
|
||||
}
|
||||
|
||||
onboarding_pick_provider() {
|
||||
local rows; rows=$(onboarding_providers_in_transport "$ONBOARDING_TRANSPORT")
|
||||
local count; count=$(echo "$rows" | wc -l | tr -d ' ')
|
||||
|
||||
# Если провайдер один на транспорт — авто-выбор.
|
||||
if [ "$count" = "1" ]; then
|
||||
ONBOARDING_PROVIDER=$(echo "$rows" | awk -F'\t' '{print $1}')
|
||||
return
|
||||
fi
|
||||
|
||||
if command -v whiptail >/dev/null 2>&1; then
|
||||
local args=()
|
||||
while IFS=$'\t' read -r id dn ae; do
|
||||
args+=("$id" "$dn" "OFF")
|
||||
done <<< "$rows"
|
||||
local prompt="${STR_PICK_PROVIDER:-Provider within} $ONBOARDING_TRANSPORT:"
|
||||
ONBOARDING_PROVIDER=$(whiptail --title "KeiSei · Provider" --radiolist \
|
||||
"$prompt" 16 70 8 "${args[@]}" 3>&1 1>&2 2>&3) \
|
||||
|| ONBOARDING_PROVIDER=$(echo "$rows" | head -1 | awk -F'\t' '{print $1}')
|
||||
else
|
||||
echo "" >&2
|
||||
echo "${STR_PICK_PROVIDER:-Provider within} $ONBOARDING_TRANSPORT:" >&2
|
||||
declare -a ids=()
|
||||
local i=1
|
||||
while IFS=$'\t' read -r id dn ae; do
|
||||
ids+=("$id")
|
||||
echo " $i) $id — $dn" >&2
|
||||
i=$((i+1))
|
||||
done <<< "$rows"
|
||||
read -r -p "[1-${#ids[@]}, default 1]: " ans
|
||||
ans="${ans:-1}"
|
||||
ONBOARDING_PROVIDER="${ids[$((ans-1))]:-${ids[0]}}"
|
||||
fi
|
||||
}
|
||||
|
||||
onboarding_pick_model() {
|
||||
# Для AWS/Azure/Vertex модели идут под parent-провайдером — мапим.
|
||||
local lookup="$ONBOARDING_PROVIDER"
|
||||
case "$ONBOARDING_PROVIDER" in
|
||||
anthropic-bedrock) lookup="anthropic" ;;
|
||||
openai-azure) lookup="openai" ;;
|
||||
google-vertex) lookup="google" ;;
|
||||
esac
|
||||
local rows; rows=$(onboarding_models_for_provider "$lookup")
|
||||
[ -z "$rows" ] && rows=$(printf "claude-sonnet-4-6\tClaude Sonnet 4.6 (fallback)\n")
|
||||
|
||||
if command -v whiptail >/dev/null 2>&1; then
|
||||
local args=()
|
||||
while IFS=$'\t' read -r id dn; do
|
||||
args+=("$id" "$dn" "OFF")
|
||||
done <<< "$rows"
|
||||
ONBOARDING_MODEL=$(whiptail --title "KeiSei · Model" --radiolist \
|
||||
"${STR_PICK_MODEL:-Default model:}" 16 70 8 "${args[@]}" 3>&1 1>&2 2>&3) \
|
||||
|| ONBOARDING_MODEL=$(echo "$rows" | head -1 | awk -F'\t' '{print $1}')
|
||||
else
|
||||
echo "" >&2
|
||||
echo "${STR_PICK_MODEL:-Models for} $lookup:" >&2
|
||||
declare -a ids=()
|
||||
local i=1
|
||||
while IFS=$'\t' read -r id dn; do
|
||||
ids+=("$id")
|
||||
echo " $i) $id — $dn" >&2
|
||||
i=$((i+1))
|
||||
done <<< "$rows"
|
||||
read -r -p "[1-${#ids[@]}, default 1]: " ans
|
||||
ans="${ans:-1}"
|
||||
ONBOARDING_MODEL="${ids[$((ans-1))]:-${ids[0]}}"
|
||||
fi
|
||||
}
|
||||
|
||||
onboarding_collect_auth() {
|
||||
ONBOARDING_AUTH_ENV_KEYS=()
|
||||
ONBOARDING_AUTH_ENV_VALUES=()
|
||||
local ae; ae=$(onboarding_auth_env_for_provider "$ONBOARDING_PROVIDER")
|
||||
[ -z "$ae" ] || [ "$ae" = "_" ] && return # local / subscription — нет ключей
|
||||
|
||||
echo "" >&2
|
||||
echo "${STR_AUTH_INTRO:-Auth for} $ONBOARDING_PROVIDER ($ae):" >&2
|
||||
echo "${STR_AUTH_PROMPT:-Enter values (Enter — leave empty, fill later).}" >&2
|
||||
|
||||
local IFS_old="$IFS"; IFS=','
|
||||
for key in $ae; do
|
||||
IFS="$IFS_old"
|
||||
local cur="${!key:-}"
|
||||
local prompt_msg="$key"
|
||||
[ -n "$cur" ] && prompt_msg="$key ${STR_AUTH_CURRENT_HINT:-(current: <hidden>)}"
|
||||
read -r -s -p " $prompt_msg = " val
|
||||
echo "" >&2
|
||||
if [ -n "$val" ]; then
|
||||
ONBOARDING_AUTH_ENV_KEYS+=("$key")
|
||||
ONBOARDING_AUTH_ENV_VALUES+=("$val")
|
||||
elif [ -n "$cur" ]; then
|
||||
ONBOARDING_AUTH_ENV_KEYS+=("$key")
|
||||
ONBOARDING_AUTH_ENV_VALUES+=("$cur")
|
||||
fi
|
||||
done
|
||||
IFS="$IFS_old"
|
||||
}
|
||||
95
install/lib-onboarding.sh
Normal file
95
install/lib-onboarding.sh
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# shellcheck shell=bash
|
||||
# lib-onboarding.sh — мастер первичной настройки (тонкий оркестратор).
|
||||
#
|
||||
# Иерархия: язык → транспорт → провайдер → модель → preflight → ключи.
|
||||
#
|
||||
# Constructor Pattern: этот файл — только координация. Логика по слоям:
|
||||
# lib-onboarding-registry.sh — парсеры providers/models.toml + fallback
|
||||
# lib-onboarding-ui.sh — pick_* функции (whiptail/bash select)
|
||||
# lib-onboarding-state.sh — запись secrets/.env + onboarding.toml + флаг
|
||||
# lib-preflight.sh — провайдер-специфичные CLI-проверки
|
||||
# lib-i18n.sh — STR_* словарь + load_lang
|
||||
#
|
||||
# Источник: $KIT_DIR/_blocks/registries/{providers,models}.toml (submodule
|
||||
# kei-registries). Если submodule не подтянут — fallback (см. registry.sh).
|
||||
#
|
||||
# Skip: $ONBOARDED_FLAG, env KEISEI_SKIP_ONBOARD=1, non-TTY.
|
||||
|
||||
# Глобалы заполняемые мастером.
|
||||
ONBOARDING_LANG=""
|
||||
ONBOARDING_TRANSPORT=""
|
||||
ONBOARDING_PROVIDER=""
|
||||
ONBOARDING_MODEL=""
|
||||
declare -a ONBOARDING_AUTH_ENV_KEYS=()
|
||||
declare -a ONBOARDING_AUTH_ENV_VALUES=()
|
||||
|
||||
ONBOARDED_FLAG="$HOME/.claude/.onboarded"
|
||||
ONBOARDING_CONFIG="$HOME/.claude/config/onboarding.toml"
|
||||
SECRETS_ENV="$HOME/.claude/secrets/.env"
|
||||
REGISTRY_PROVIDERS="$KIT_DIR/_blocks/registries/providers.toml"
|
||||
REGISTRY_MODELS="$KIT_DIR/_blocks/registries/models.toml"
|
||||
|
||||
# Подкубы (sourced параллельно — функции расходятся по namespace без коллизий).
|
||||
# shellcheck source=install/lib-onboarding-registry.sh
|
||||
[ -f "$LIB_DIR/lib-onboarding-registry.sh" ] && source "$LIB_DIR/lib-onboarding-registry.sh"
|
||||
# shellcheck source=install/lib-onboarding-ui.sh
|
||||
[ -f "$LIB_DIR/lib-onboarding-ui.sh" ] && source "$LIB_DIR/lib-onboarding-ui.sh"
|
||||
# shellcheck source=install/lib-onboarding-state.sh
|
||||
[ -f "$LIB_DIR/lib-onboarding-state.sh" ] && source "$LIB_DIR/lib-onboarding-state.sh"
|
||||
|
||||
# Skip-логика.
|
||||
onboarding_should_run() {
|
||||
[ -f "$ONBOARDED_FLAG" ] && return 1
|
||||
[ "${KEISEI_SKIP_ONBOARD:-}" = "1" ] && return 1
|
||||
[ ! -t 0 ] && return 1
|
||||
[ ! -t 1 ] && return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
# Оркестратор: 5 шагов + preflight + запись.
|
||||
onboarding_run() {
|
||||
onboarding_should_run || return 0
|
||||
|
||||
if command -v say >/dev/null 2>&1; then
|
||||
say "${STR_ONBOARDING_INTRO:-Onboarding wizard (5 steps)}"
|
||||
else
|
||||
echo "── KeiSei: ${STR_ONBOARDING_INTRO:-onboarding (5 steps)} ──" >&2
|
||||
fi
|
||||
|
||||
onboarding_pick_language
|
||||
onboarding_pick_transport
|
||||
onboarding_pick_provider
|
||||
onboarding_pick_model
|
||||
|
||||
# Preflight — провайдер-специфичная проверка CLI/daemon до сбора ключей.
|
||||
if command -v preflight_run >/dev/null 2>&1; then
|
||||
if ! preflight_run "$ONBOARDING_PROVIDER"; then
|
||||
echo "" >&2
|
||||
echo " ⚠ ${STR_PREFLIGHT_FAILED:-Preflight failed — provider may not work.}" >&2
|
||||
if [ -t 0 ] && [ -t 1 ]; then
|
||||
read -r -p " ${STR_PREFLIGHT_CONTINUE:-Continue anyway? [y/N]} " _ans
|
||||
case "$_ans" in
|
||||
y|Y|yes|да|Да)
|
||||
echo " → продолжаю; ключи запишутся но runtime может упасть." >&2
|
||||
;;
|
||||
*)
|
||||
echo " → прервано; флаг .onboarded НЕ выставляется, перезапустите." >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
else
|
||||
echo " → non-TTY, продолжаю — настройте CLI вручную потом." >&2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
onboarding_collect_auth
|
||||
onboarding_write_secrets
|
||||
onboarding_write_config
|
||||
|
||||
if command -v say >/dev/null 2>&1; then
|
||||
say "✓ ${STR_DONE_TITLE:-onboarding complete}: $ONBOARDING_TRANSPORT / $ONBOARDING_PROVIDER / $ONBOARDING_MODEL"
|
||||
say " ${STR_DONE_CONFIG:-config:} $ONBOARDING_CONFIG"
|
||||
[ "${#ONBOARDING_AUTH_ENV_KEYS[@]}" -gt 0 ] && say " ${STR_DONE_SECRETS:-secrets:} $SECRETS_ENV (chmod 600)"
|
||||
fi
|
||||
}
|
||||
102
install/lib-preflight.sh
Normal file
102
install/lib-preflight.sh
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# shellcheck shell=bash
|
||||
# lib-preflight.sh — диспетчер preflight-проверок CLI.
|
||||
#
|
||||
# Контракт:
|
||||
# preflight_run <provider-id>
|
||||
# 1. Ищет файл install/preflight/<provider-id>.sh
|
||||
# 2. Если есть — source'ит и вызывает `preflight_check_<sanitized-id>`
|
||||
# 3. Функция возвращает 0 (ok) / 1 (missing, инструкция напечатана)
|
||||
# 4. Если файла нет — провайдеру CLI не нужен, тихо exit 0
|
||||
#
|
||||
# Файл per-provider должен экспортировать ОДНУ функцию:
|
||||
# preflight_check_<id>() — печатает инструкцию в stderr, exit 0/1
|
||||
#
|
||||
# Sanitize: dashes в id заменяются на underscores для имени функции
|
||||
# (bash не любит dashes в идентификаторах).
|
||||
|
||||
PREFLIGHT_DIR="${LIB_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)}/preflight"
|
||||
|
||||
# Печатает инструкцию по установке, спрашивает действие.
|
||||
# Аргументы: $1 — имя CLI, $2 — команда установки.
|
||||
preflight_offer_install() {
|
||||
local cli="$1"
|
||||
local install_cmd="$2"
|
||||
echo "" >&2
|
||||
echo " ⚠ $cli не найден." >&2
|
||||
echo " Установить: $install_cmd" >&2
|
||||
echo "" >&2
|
||||
if [ -t 0 ] && [ -t 1 ]; then
|
||||
echo " ⓘ команда: $install_cmd" >&2
|
||||
read -r -p " Поставить сейчас? [y/N/skip] " ans
|
||||
case "$ans" in
|
||||
y|Y|yes)
|
||||
# bash -c вместо eval — explicit subshell, не word-splitting'тся
|
||||
# лишний раз в текущем процессе.
|
||||
bash -c "$install_cmd"
|
||||
return $?
|
||||
;;
|
||||
skip|s|S)
|
||||
echo " пропускаю — поставите вручную позже." >&2
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
echo " пропуск (по умолчанию)." >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
else
|
||||
# non-TTY: только печатаем инструкцию.
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Универсальный helper для типового CLI-чека (command -v + offer-install + version).
|
||||
# Используется per-provider preflight файлами чтобы убрать boilerplate.
|
||||
#
|
||||
# Аргументы:
|
||||
# $1 — имя CLI (для сообщений), например "aws CLI"
|
||||
# $2 — бинарь (для command -v), например "aws"
|
||||
# $3 — install_cmd (для preflight_offer_install)
|
||||
# $4 — version_cmd (для печати при success), например "aws --version"
|
||||
#
|
||||
# Возврат: 0 если CLI есть, 1 если нет и юзер не поставил.
|
||||
preflight_check_cli() {
|
||||
local label="$1"
|
||||
local bin="$2"
|
||||
local install_cmd="$3"
|
||||
local version_cmd="$4"
|
||||
if ! command -v "$bin" >/dev/null 2>&1; then
|
||||
preflight_offer_install "$label" "$install_cmd" || return 1
|
||||
# После install проверяем что бинарь появился в PATH.
|
||||
command -v "$bin" >/dev/null 2>&1 || return 1
|
||||
fi
|
||||
echo " ✓ $label: $(eval "$version_cmd" 2>&1 | head -1)" >&2
|
||||
return 0
|
||||
}
|
||||
|
||||
# Главный диспетчер. Вызывается из onboarding между pick_model и collect_auth.
|
||||
preflight_run() {
|
||||
local provider="$1"
|
||||
[ -z "$provider" ] && return 0
|
||||
# Whitelist символов в provider-id: только [a-z0-9_-], длина 1..64.
|
||||
# Защищает от path-traversal (../) и shell-инъекций через имя файла.
|
||||
if ! [[ "$provider" =~ ^[a-z0-9][a-z0-9_-]{0,63}$ ]]; then
|
||||
echo " ⚠ preflight: provider id '$provider' содержит недопустимые символы — пропуск" >&2
|
||||
return 0
|
||||
fi
|
||||
local script="$PREFLIGHT_DIR/${provider}.sh"
|
||||
# Проверяем что resolved путь не вышел за PREFLIGHT_DIR (на случай symlink'ов).
|
||||
local resolved
|
||||
resolved="$(cd "$PREFLIGHT_DIR" 2>/dev/null && pwd -P)/${provider}.sh"
|
||||
if [ ! -f "$script" ] || [ ! -f "$resolved" ]; then
|
||||
return 0 # CLI не нужен — direct-api, ключ собирается ниже
|
||||
fi
|
||||
# shellcheck disable=SC1090
|
||||
source "$script"
|
||||
local fn="preflight_check_${provider//-/_}"
|
||||
if command -v "$fn" >/dev/null 2>&1; then
|
||||
"$fn"
|
||||
return $?
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
40
install/preflight/anthropic-bedrock.sh
Normal file
40
install/preflight/anthropic-bedrock.sh
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# shellcheck shell=bash
|
||||
# preflight/anthropic-bedrock.sh — AWS CLI + Bedrock региональный доступ.
|
||||
|
||||
preflight_check_anthropic_bedrock() {
|
||||
if ! command -v aws >/dev/null 2>&1; then
|
||||
local cmd
|
||||
case "$(uname -s)" in
|
||||
Darwin) cmd="brew install awscli" ;;
|
||||
Linux) cmd="curl 'https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip' -o /tmp/awscliv2.zip && unzip -q /tmp/awscliv2.zip -d /tmp && sudo /tmp/aws/install" ;;
|
||||
*) cmd="см. https://aws.amazon.com/cli/" ;;
|
||||
esac
|
||||
preflight_offer_install "aws CLI" "$cmd" || return 1
|
||||
fi
|
||||
# Один вызов вместо двух — экономит ~1-3с при success-path.
|
||||
local identity_out identity_rc
|
||||
identity_out="$(aws sts get-caller-identity --output json 2>&1)"
|
||||
identity_rc=$?
|
||||
if [ $identity_rc -ne 0 ]; then
|
||||
echo "" >&2
|
||||
# Различаем cred-ошибку от сетевой/прочей по тексту.
|
||||
if echo "$identity_out" | grep -qiE "UnauthorizedAccess|InvalidClientTokenId|ExpiredToken|signature|credential"; then
|
||||
echo " ⚠ AWS credentials невалидны." >&2
|
||||
echo " Запустите: aws configure" >&2
|
||||
echo " Или экспортируйте AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY + AWS_REGION." >&2
|
||||
else
|
||||
echo " ⚠ aws sts get-caller-identity упал (не credentials)." >&2
|
||||
echo " raw: $identity_out" >&2
|
||||
fi
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
echo " ✓ aws CLI: $(aws --version 2>&1 | head -1)" >&2
|
||||
# ARN не печатаем полностью — может содержать account-id (sensitive enum target).
|
||||
# Показываем только тип identity (user/role/assumed-role) и user-name.
|
||||
local arn_short
|
||||
arn_short="$(echo "$identity_out" | sed -n 's/.*"Arn": *"\(arn:aws:[^:]*::\)[0-9]*\(:[^"]*\)".*/\1***\2/p')"
|
||||
[ -z "$arn_short" ] && arn_short="<masked>"
|
||||
echo " ✓ identity: $arn_short" >&2
|
||||
return 0
|
||||
}
|
||||
41
install/preflight/codex.sh
Normal file
41
install/preflight/codex.sh
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# shellcheck shell=bash
|
||||
# preflight/codex.sh — OpenAI Codex CLI через ChatGPT OAuth.
|
||||
|
||||
preflight_check_codex() {
|
||||
if ! command -v codex >/dev/null 2>&1; then
|
||||
if ! command -v npm >/dev/null 2>&1; then
|
||||
echo "" >&2
|
||||
echo " ⚠ npm требуется для установки codex." >&2
|
||||
echo " Сначала: brew install node (macOS) или apt install nodejs npm (Linux)" >&2
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
preflight_offer_install "codex CLI" "npm install -g @openai/codex" || return 1
|
||||
fi
|
||||
# Проверяем что OAuth активен.
|
||||
# Regex'ы: позитивные паттерны (logged-in / signed-in / active) И
|
||||
# негативные (not logged in / logged out / sign in required) — иначе
|
||||
# фраза "you are not logged in" проходит через `logged.in` regex
|
||||
# как false-pass.
|
||||
local status
|
||||
status="$(codex login status 2>&1 || true)"
|
||||
if echo "$status" | grep -qiE 'not (logged|signed) (in|on)|logged out|signed out|please.*log\s*in|sign[[:space:]]*in[[:space:]]*required'; then
|
||||
echo "" >&2
|
||||
echo " ⚠ codex не залогинен в ChatGPT." >&2
|
||||
echo " Запустите: codex login" >&2
|
||||
echo " (требуется ChatGPT Plus/Pro/Team подписка)" >&2
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
if ! echo "$status" | grep -qiE '\b(logged|signed) in\b|status:[[:space:]]*active|auth(enticated)?[[:space:]]*:[[:space:]]*(yes|true|ok)'; then
|
||||
echo "" >&2
|
||||
echo " ⚠ codex auth-статус неопределён:" >&2
|
||||
echo " $status" >&2
|
||||
echo " Запустите: codex login" >&2
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
echo " ✓ codex CLI: $(codex --version 2>&1 | head -1)" >&2
|
||||
echo " ✓ OAuth: active" >&2
|
||||
return 0
|
||||
}
|
||||
34
install/preflight/google-vertex.sh
Normal file
34
install/preflight/google-vertex.sh
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# shellcheck shell=bash
|
||||
# preflight/google-vertex.sh — gcloud CLI + service-account JSON.
|
||||
|
||||
preflight_check_google_vertex() {
|
||||
if ! command -v gcloud >/dev/null 2>&1; then
|
||||
local cmd
|
||||
case "$(uname -s)" in
|
||||
Darwin) cmd="brew install --cask google-cloud-sdk" ;;
|
||||
Linux)
|
||||
echo " ⚠ Linux установка gcloud тянет скрипт с sdk.cloud.google.com" >&2
|
||||
echo " и выполняет его как bash — без проверки хэша." >&2
|
||||
echo " Альтернатива: пакет из репов apt/dnf, либо ручная установка по" >&2
|
||||
echo " https://cloud.google.com/sdk/docs/install#linux" >&2
|
||||
cmd="curl https://sdk.cloud.google.com | bash"
|
||||
;;
|
||||
*) cmd="см. https://cloud.google.com/sdk/docs/install" ;;
|
||||
esac
|
||||
preflight_offer_install "gcloud CLI" "$cmd" || return 1
|
||||
fi
|
||||
# Проверяем что выбран project.
|
||||
local project
|
||||
project="$(gcloud config get-value project 2>/dev/null)"
|
||||
if [ -z "$project" ] || [ "$project" = "(unset)" ]; then
|
||||
echo "" >&2
|
||||
echo " ⚠ GCP project не выбран." >&2
|
||||
echo " Запустите: gcloud auth login && gcloud config set project YOUR_PROJECT_ID" >&2
|
||||
echo " Также установите GOOGLE_APPLICATION_CREDENTIALS на путь к service-account JSON." >&2
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
echo " ✓ gcloud CLI: $(gcloud --version 2>&1 | head -1)" >&2
|
||||
echo " ✓ project: $project" >&2
|
||||
return 0
|
||||
}
|
||||
16
install/preflight/lmstudio-local.sh
Normal file
16
install/preflight/lmstudio-local.sh
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# shellcheck shell=bash
|
||||
# preflight/lmstudio-local.sh — LM Studio desktop GUI на 127.0.0.1:1234.
|
||||
|
||||
preflight_check_lmstudio_local() {
|
||||
# LM Studio это desktop-приложение, не CLI — проверяем только порт.
|
||||
if ! curl -fsS --max-time 3 http://127.0.0.1:1234/v1/models >/dev/null 2>&1; then
|
||||
echo "" >&2
|
||||
echo " ⚠ LM Studio сервер не запущен на 1234." >&2
|
||||
echo " Скачайте: https://lmstudio.ai/" >&2
|
||||
echo " В GUI: Local Server → Start Server (порт 1234 по умолчанию)" >&2
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
echo " ✓ LM Studio: 127.0.0.1:1234 отвечает" >&2
|
||||
return 0
|
||||
}
|
||||
24
install/preflight/mlx-local.sh
Normal file
24
install/preflight/mlx-local.sh
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# shellcheck shell=bash
|
||||
# preflight/mlx-local.sh — MLX inference server (Apple silicon).
|
||||
|
||||
preflight_check_mlx_local() {
|
||||
if [ "$(uname -s)" != "Darwin" ] || [ "$(uname -m)" != "arm64" ]; then
|
||||
echo "" >&2
|
||||
echo " ⚠ MLX доступен только на Apple silicon (arm64 macOS)." >&2
|
||||
echo " Текущая платформа: $(uname -s) $(uname -m)" >&2
|
||||
return 1
|
||||
fi
|
||||
if ! command -v mlx_lm.server >/dev/null 2>&1; then
|
||||
# --user избегает sudo и не загрязняет system Python.
|
||||
preflight_offer_install "mlx_lm" "pip install --user mlx-lm" || return 1
|
||||
fi
|
||||
if ! curl -fsS --max-time 3 http://127.0.0.1:8080/v1/models >/dev/null 2>&1; then
|
||||
echo "" >&2
|
||||
echo " ⚠ MLX server не запущен на 8080." >&2
|
||||
echo " Запустите: mlx_lm.server --model mlx-community/Qwen2.5-Coder-32B-Instruct-4bit" >&2
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
echo " ✓ mlx_lm.server: 127.0.0.1:8080 отвечает" >&2
|
||||
return 0
|
||||
}
|
||||
34
install/preflight/ollama-local.sh
Normal file
34
install/preflight/ollama-local.sh
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# shellcheck shell=bash
|
||||
# preflight/ollama-local.sh — Ollama daemon на 127.0.0.1:11434.
|
||||
|
||||
preflight_check_ollama_local() {
|
||||
if ! command -v ollama >/dev/null 2>&1; then
|
||||
local cmd
|
||||
case "$(uname -s)" in
|
||||
Darwin) cmd="brew install ollama" ;;
|
||||
Linux)
|
||||
# WARNING: curl|sh без verification — supply-chain риск.
|
||||
# Если есть apt/dnf — лучше через них, но ollama не в репах
|
||||
# большинства дистров. Предупреждаем юзера явно.
|
||||
echo " ⚠ Linux установка ollama тянет скрипт с https://ollama.com и" >&2
|
||||
echo " выполняет его как shell — без проверки хэша/подписи." >&2
|
||||
echo " Альтернатива: скачать вручную с https://ollama.com/download/linux" >&2
|
||||
echo " и проверить SHA256 перед запуском." >&2
|
||||
cmd="curl -fsSL https://ollama.com/install.sh | sh"
|
||||
;;
|
||||
*) cmd="см. https://ollama.com/download" ;;
|
||||
esac
|
||||
preflight_offer_install "ollama" "$cmd" || return 1
|
||||
fi
|
||||
# Проверяем что daemon запущен.
|
||||
if ! curl -fsS --max-time 3 http://127.0.0.1:11434/api/tags >/dev/null 2>&1; then
|
||||
echo "" >&2
|
||||
echo " ⚠ ollama daemon не запущен." >&2
|
||||
echo " Запустите: ollama serve (или brew services start ollama на macOS)" >&2
|
||||
echo "" >&2
|
||||
return 1
|
||||
fi
|
||||
echo " ✓ ollama: $(ollama --version 2>&1 | head -1)" >&2
|
||||
echo " ✓ daemon: 127.0.0.1:11434 отвечает" >&2
|
||||
return 0
|
||||
}
|
||||
Loading…
Reference in a new issue