Check manual-full UI text in smoke test
This commit is contained in:
27
.understand-anything/.understandignore
Normal file
27
.understand-anything/.understandignore
Normal file
@@ -0,0 +1,27 @@
|
||||
# .understandignore - patterns for files/dirs to exclude from analysis
|
||||
# Syntax: same as .gitignore (globs, # comments, ! negation, trailing / for dirs)
|
||||
# Lines below are suggestions - uncomment to activate.
|
||||
# Use ! prefix to force-include something excluded by defaults.
|
||||
#
|
||||
# Built-in defaults (always excluded unless negated):
|
||||
# node_modules/, .git/, dist/, build/, obj/, *.lock, *.min.js, etc.
|
||||
#
|
||||
.understand-anything/
|
||||
|
||||
# --- From .gitignore (uncomment to exclude) ---
|
||||
|
||||
# .DS_Store
|
||||
# *.tmp
|
||||
# .build/
|
||||
# app/.build/
|
||||
|
||||
# --- Detected directories (uncomment to exclude) ---
|
||||
|
||||
# tests/
|
||||
# docs/
|
||||
|
||||
# --- Test file patterns (uncomment to exclude) ---
|
||||
|
||||
# *.test.*
|
||||
# *.spec.*
|
||||
# *.snap
|
||||
3
.understand-anything/config.json
Normal file
3
.understand-anything/config.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"outputLanguage": "ru"
|
||||
}
|
||||
11
.understand-anything/intermediate/assemble-review.json
Normal file
11
.understand-anything/intermediate/assemble-review.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"issues": [],
|
||||
"warnings": [],
|
||||
"stats": {
|
||||
"nodes": 73,
|
||||
"edges": 56,
|
||||
"duplicates": 0,
|
||||
"dangling": 0,
|
||||
"missingFiles": 0
|
||||
}
|
||||
}
|
||||
1571
.understand-anything/intermediate/assembled-graph.json
Normal file
1571
.understand-anything/intermediate/assembled-graph.json
Normal file
File diff suppressed because it is too large
Load Diff
634
.understand-anything/intermediate/batch-1.json
Normal file
634
.understand-anything/intermediate/batch-1.json
Normal file
@@ -0,0 +1,634 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "document:AGENTS.md",
|
||||
"type": "document",
|
||||
"name": "AGENTS.md",
|
||||
"filePath": "AGENTS.md",
|
||||
"summary": "Репозиторные правила для агентов: модель системы, live VPN safety, тестовая лестница и контракт финального ответа.",
|
||||
"tags": [
|
||||
"agent-rules",
|
||||
"documentation",
|
||||
"safety"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "document:README.md",
|
||||
"type": "document",
|
||||
"name": "README.md",
|
||||
"filePath": "README.md",
|
||||
"summary": "Основная пользовательская документация Lemana VPN: установка, credential sources, модули, использование, диагностика и удаление.",
|
||||
"tags": [
|
||||
"documentation",
|
||||
"overview",
|
||||
"user-guide"
|
||||
],
|
||||
"complexity": "complex"
|
||||
},
|
||||
{
|
||||
"id": "file:install.sh",
|
||||
"type": "file",
|
||||
"name": "install.sh",
|
||||
"filePath": "install.sh",
|
||||
"summary": "Installer wizard для Homebrew/pipx/openconnect-lite, CLI, config, DNS wrapper, sudoers, Touch ID helper, Swift app, launch agent и shell aliases.",
|
||||
"tags": [
|
||||
"installer",
|
||||
"wizard",
|
||||
"macos"
|
||||
],
|
||||
"complexity": "complex"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:usage",
|
||||
"type": "function",
|
||||
"name": "usage",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
72,
|
||||
106
|
||||
],
|
||||
"summary": "Значимая функция usage в install.sh.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"usage"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:yes_no",
|
||||
"type": "function",
|
||||
"name": "yes_no",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
304,
|
||||
330
|
||||
],
|
||||
"summary": "Значимая функция yes_no в install.sh.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"yes-no"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:choose_credential_source_interactive",
|
||||
"type": "function",
|
||||
"name": "choose_credential_source_interactive",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
332,
|
||||
382
|
||||
],
|
||||
"summary": "Показывает интерактивный выбор credential source и нормализует ответ пользователя.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"choose-credential-source-interactive"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:print_detected_state",
|
||||
"type": "function",
|
||||
"name": "print_detected_state",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
400,
|
||||
417
|
||||
],
|
||||
"summary": "Значимая функция print_detected_state в install.sh.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"print-detected-state"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:choose_modules",
|
||||
"type": "function",
|
||||
"name": "choose_modules",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
419,
|
||||
514
|
||||
],
|
||||
"summary": "Определяет набор устанавливаемых модулей с учетом интерактивного режима и флагов installer.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"choose-modules"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:download_file",
|
||||
"type": "function",
|
||||
"name": "download_file",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
523,
|
||||
540
|
||||
],
|
||||
"summary": "Значимая функция download_file в install.sh.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"download-file"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:install_homebrew_packages",
|
||||
"type": "function",
|
||||
"name": "install_homebrew_packages",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
552,
|
||||
578
|
||||
],
|
||||
"summary": "Значимая функция install_homebrew_packages в install.sh.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"install-homebrew-packages"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:install_openconnect_lite",
|
||||
"type": "function",
|
||||
"name": "install_openconnect_lite",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
580,
|
||||
597
|
||||
],
|
||||
"summary": "Устанавливает или проверяет openconnect-lite runtime, необходимый для SAML SSO.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"install-openconnect-lite"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:install_cli",
|
||||
"type": "function",
|
||||
"name": "install_cli",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
599,
|
||||
613
|
||||
],
|
||||
"summary": "Копирует основной CLI и uninstall helper в пользовательский bin.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"install-cli"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:install_config",
|
||||
"type": "function",
|
||||
"name": "install_config",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
615,
|
||||
639
|
||||
],
|
||||
"summary": "Устанавливает шаблон openconnect-lite config и сохраняет локальную конфигурацию модулей.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"install-config"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:install_dns_cleanup",
|
||||
"type": "function",
|
||||
"name": "install_dns_cleanup",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
641,
|
||||
654
|
||||
],
|
||||
"summary": "Устанавливает root-owned DNS cleanup wrapper для безопасного сброса корпоративных DNS.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"install-dns-cleanup"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:install_sudoers",
|
||||
"type": "function",
|
||||
"name": "install_sudoers",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
656,
|
||||
683
|
||||
],
|
||||
"summary": "Пишет ограниченные sudoers правила для openconnect и DNS cleanup wrapper.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"install-sudoers"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:install_touchid_helper",
|
||||
"type": "function",
|
||||
"name": "install_touchid_helper",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
685,
|
||||
708
|
||||
],
|
||||
"summary": "Собирает и устанавливает Touch ID helper для Bitwarden master password.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"install-touchid-helper"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:install_menu_bar_app",
|
||||
"type": "function",
|
||||
"name": "install_menu_bar_app",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
710,
|
||||
769
|
||||
],
|
||||
"summary": "Собирает release-версию Swift menu bar app и устанавливает LemanaVPN.app.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"install-menu-bar-app"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:install_launch_agent",
|
||||
"type": "function",
|
||||
"name": "install_launch_agent",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
771,
|
||||
811
|
||||
],
|
||||
"summary": "Создает LaunchAgent для автозапуска menu bar app при логине.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"install-launch-agent"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:restart_running_menu_bar_app",
|
||||
"type": "function",
|
||||
"name": "restart_running_menu_bar_app",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
813,
|
||||
831
|
||||
],
|
||||
"summary": "Перезапускает уже открытое menu bar приложение после обновления bundle.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"restart-running-menu-bar-app"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:install_shell_aliases",
|
||||
"type": "function",
|
||||
"name": "install_shell_aliases",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
833,
|
||||
877
|
||||
],
|
||||
"summary": "Добавляет idempotent zsh aliases для CLI режимов подключения и диагностики.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"install-shell-aliases"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:maybe_login_bitwarden",
|
||||
"type": "function",
|
||||
"name": "maybe_login_bitwarden",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
879,
|
||||
900
|
||||
],
|
||||
"summary": "Проверяет Bitwarden session и при необходимости запускает login/unlock flow.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"maybe-login-bitwarden"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:install.sh:main",
|
||||
"type": "function",
|
||||
"name": "main",
|
||||
"filePath": "install.sh",
|
||||
"lineRange": [
|
||||
902,
|
||||
941
|
||||
],
|
||||
"summary": "Значимая функция main в install.sh.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"main"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "file:uninstall.sh",
|
||||
"type": "file",
|
||||
"name": "uninstall.sh",
|
||||
"filePath": "uninstall.sh",
|
||||
"summary": "Uninstaller для восстановления runtime patch backups, удаления CLI/app/config/sudoers/aliases и опциональной очистки Keychain/Touch ID state.",
|
||||
"tags": [
|
||||
"uninstaller",
|
||||
"cleanup",
|
||||
"macos"
|
||||
],
|
||||
"complexity": "complex"
|
||||
},
|
||||
{
|
||||
"id": "function:uninstall.sh:usage",
|
||||
"type": "function",
|
||||
"name": "usage",
|
||||
"filePath": "uninstall.sh",
|
||||
"lineRange": [
|
||||
62,
|
||||
79
|
||||
],
|
||||
"summary": "Значимая функция usage в uninstall.sh.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"usage"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:uninstall.sh:restore_openconnect_lite_patch",
|
||||
"type": "function",
|
||||
"name": "restore_openconnect_lite_patch",
|
||||
"filePath": "uninstall.sh",
|
||||
"lineRange": [
|
||||
161,
|
||||
181
|
||||
],
|
||||
"summary": "Возвращает backup исходника openconnect-lite, если runtime patch был установлен.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"restore-openconnect-lite-patch"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:uninstall.sh:remove_zshrc_block",
|
||||
"type": "function",
|
||||
"name": "remove_zshrc_block",
|
||||
"filePath": "uninstall.sh",
|
||||
"lineRange": [
|
||||
183,
|
||||
204
|
||||
],
|
||||
"summary": "Удаляет idempotent блок Lemana VPN aliases из zshrc.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"remove-zshrc-block"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:uninstall.sh:remove_keychain_entries",
|
||||
"type": "function",
|
||||
"name": "remove_keychain_entries",
|
||||
"filePath": "uninstall.sh",
|
||||
"lineRange": [
|
||||
206,
|
||||
234
|
||||
],
|
||||
"summary": "Опционально удаляет password, TOTP seed и username из macOS Keychain.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"remove-keychain-entries"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:uninstall.sh:main",
|
||||
"type": "function",
|
||||
"name": "main",
|
||||
"filePath": "uninstall.sh",
|
||||
"lineRange": [
|
||||
236,
|
||||
318
|
||||
],
|
||||
"summary": "Значимая функция main в uninstall.sh.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"main"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:usage",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:yes_no",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:choose_credential_source_interactive",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:print_detected_state",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:choose_modules",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:download_file",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:install_homebrew_packages",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:install_openconnect_lite",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:install_cli",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:install_config",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:install_dns_cleanup",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:install_sudoers",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:install_touchid_helper",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:install_menu_bar_app",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:install_launch_agent",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:restart_running_menu_bar_app",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:install_shell_aliases",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:maybe_login_bitwarden",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:install.sh",
|
||||
"target": "function:install.sh:main",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:uninstall.sh",
|
||||
"target": "function:uninstall.sh:usage",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:uninstall.sh",
|
||||
"target": "function:uninstall.sh:restore_openconnect_lite_patch",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:uninstall.sh",
|
||||
"target": "function:uninstall.sh:remove_zshrc_block",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:uninstall.sh",
|
||||
"target": "function:uninstall.sh:remove_keychain_entries",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:uninstall.sh",
|
||||
"target": "function:uninstall.sh:main",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
943
.understand-anything/intermediate/batch-2.json
Normal file
943
.understand-anything/intermediate/batch-2.json
Normal file
@@ -0,0 +1,943 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "document:.agents/skills/lemana-vpn-operations/SKILL.md",
|
||||
"type": "document",
|
||||
"name": "Lemana VPN Operations skill",
|
||||
"filePath": ".agents/skills/lemana-vpn-operations/SKILL.md",
|
||||
"summary": "Инструкции для операционных задач: установка, обновление, статусы, безопасные ответы пользователю и live-connection правила.",
|
||||
"tags": [
|
||||
"skill",
|
||||
"operations",
|
||||
"documentation"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "document:.agents/skills/lemana-vpn-sso-autofill/SKILL.md",
|
||||
"type": "document",
|
||||
"name": "Lemana VPN SSO skill",
|
||||
"filePath": ".agents/skills/lemana-vpn-sso-autofill/SKILL.md",
|
||||
"summary": "Инструкции по Bitwarden, Keychain, TOTP, Keycloak SSO, autofill и runtime-патчам openconnect-lite.",
|
||||
"tags": [
|
||||
"skill",
|
||||
"sso",
|
||||
"credentials"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "document:.agents/skills/lemana-vpn-testing/SKILL.md",
|
||||
"type": "document",
|
||||
"name": "Lemana VPN Testing skill",
|
||||
"filePath": ".agents/skills/lemana-vpn-testing/SKILL.md",
|
||||
"summary": "Инструкции по non-live проверкам, status checks, live VPN safety и финальному отчету для изменений в репозитории.",
|
||||
"tags": [
|
||||
"skill",
|
||||
"testing",
|
||||
"verification"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "file:app/Package.swift",
|
||||
"type": "file",
|
||||
"name": "Package.swift",
|
||||
"filePath": "app/Package.swift",
|
||||
"summary": "Swift Package manifest для сборки executable target LemanaVPN под macOS 13+.",
|
||||
"tags": [
|
||||
"swift",
|
||||
"build-system",
|
||||
"menu-bar-app"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "file:app/Sources/LemanaVPN/LemanaVPNApp.swift",
|
||||
"type": "file",
|
||||
"name": "LemanaVPNApp.swift",
|
||||
"filePath": "app/Sources/LemanaVPN/LemanaVPNApp.swift",
|
||||
"summary": "SwiftUI/AppKit menu bar surface: отображает состояние VPN, кнопки подключения, логи и действия пользователя.",
|
||||
"tags": [
|
||||
"swiftui",
|
||||
"menu-bar",
|
||||
"ui"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "class:app/Sources/LemanaVPN/LemanaVPNApp.swift:LemanaVPNApp",
|
||||
"type": "class",
|
||||
"name": "LemanaVPNApp",
|
||||
"filePath": "app/Sources/LemanaVPN/LemanaVPNApp.swift",
|
||||
"lineRange": [
|
||||
5,
|
||||
16
|
||||
],
|
||||
"summary": "Точка входа SwiftUI menu bar app, связывает VPNManager с MenuBarExtra.",
|
||||
"tags": [
|
||||
"swift",
|
||||
"type",
|
||||
"menu-bar-app"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "class:app/Sources/LemanaVPN/LemanaVPNApp.swift:StatusBarLabel",
|
||||
"type": "class",
|
||||
"name": "StatusBarLabel",
|
||||
"filePath": "app/Sources/LemanaVPN/LemanaVPNApp.swift",
|
||||
"lineRange": [
|
||||
17,
|
||||
51
|
||||
],
|
||||
"summary": "Компактный status bar label с иконкой и оставшимся временем сессии.",
|
||||
"tags": [
|
||||
"swift",
|
||||
"type",
|
||||
"menu-bar-app"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "class:app/Sources/LemanaVPN/LemanaVPNApp.swift:VPNMenuView",
|
||||
"type": "class",
|
||||
"name": "VPNMenuView",
|
||||
"filePath": "app/Sources/LemanaVPN/LemanaVPNApp.swift",
|
||||
"lineRange": [
|
||||
52,
|
||||
191
|
||||
],
|
||||
"summary": "Основной menu UI с командами auto/manual/manual-full connect, disconnect, logs и quit.",
|
||||
"tags": [
|
||||
"swift",
|
||||
"type",
|
||||
"menu-bar-app"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "file:app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"type": "file",
|
||||
"name": "VPNManager.swift",
|
||||
"filePath": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"summary": "Основной state manager Swift-приложения: запускает CLI в JSON mode, декодирует события, отслеживает health и управляет reconnect/disconnect.",
|
||||
"tags": [
|
||||
"swift",
|
||||
"state-manager",
|
||||
"cli-bridge"
|
||||
],
|
||||
"complexity": "complex"
|
||||
},
|
||||
{
|
||||
"id": "class:app/Sources/LemanaVPN/VPNManager.swift:VPNEvent",
|
||||
"type": "class",
|
||||
"name": "VPNEvent",
|
||||
"filePath": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"lineRange": [
|
||||
4,
|
||||
17
|
||||
],
|
||||
"summary": "Decodable-модель JSON events, которые runtime CLI отправляет в Swift app.",
|
||||
"tags": [
|
||||
"swift",
|
||||
"type",
|
||||
"menu-bar-app"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "class:app/Sources/LemanaVPN/VPNManager.swift:VPNStatusResponse",
|
||||
"type": "class",
|
||||
"name": "VPNStatusResponse",
|
||||
"filePath": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"lineRange": [
|
||||
18,
|
||||
21
|
||||
],
|
||||
"summary": "Decodable envelope для ответа `vpn --status --json`.",
|
||||
"tags": [
|
||||
"swift",
|
||||
"type",
|
||||
"menu-bar-app"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "class:app/Sources/LemanaVPN/VPNManager.swift:ModuleStatus",
|
||||
"type": "class",
|
||||
"name": "ModuleStatus",
|
||||
"filePath": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"lineRange": [
|
||||
22,
|
||||
101
|
||||
],
|
||||
"summary": "Модель статуса модулей и вычисление summary/systemImage для menu bar app.",
|
||||
"tags": [
|
||||
"swift",
|
||||
"type",
|
||||
"menu-bar-app"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "class:app/Sources/LemanaVPN/VPNManager.swift:VPNState",
|
||||
"type": "class",
|
||||
"name": "VPNState",
|
||||
"filePath": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"lineRange": [
|
||||
102,
|
||||
121
|
||||
],
|
||||
"summary": "Finite state model для disconnected/connecting/connected/reconnecting/error состояний.",
|
||||
"tags": [
|
||||
"swift",
|
||||
"type",
|
||||
"menu-bar-app"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "class:app/Sources/LemanaVPN/VPNManager.swift:VPNLaunchMode",
|
||||
"type": "class",
|
||||
"name": "VPNLaunchMode",
|
||||
"filePath": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"lineRange": [
|
||||
122,
|
||||
136
|
||||
],
|
||||
"summary": "Маппинг app launch modes на CLI flags `--auto`, `--manual`, `--manual-full`.",
|
||||
"tags": [
|
||||
"swift",
|
||||
"type",
|
||||
"menu-bar-app"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "class:app/Sources/LemanaVPN/VPNManager.swift:VPNManager",
|
||||
"type": "class",
|
||||
"name": "VPNManager",
|
||||
"filePath": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"lineRange": [
|
||||
137,
|
||||
698
|
||||
],
|
||||
"summary": "ObservableObject, который мостит Swift menu bar app с shell CLI, JSON events и health checks.",
|
||||
"tags": [
|
||||
"swift",
|
||||
"type",
|
||||
"state-manager"
|
||||
],
|
||||
"complexity": "complex"
|
||||
},
|
||||
{
|
||||
"id": "function:app/Sources/LemanaVPN/VPNManager.swift:refreshStatus",
|
||||
"type": "function",
|
||||
"name": "refreshStatus",
|
||||
"filePath": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"lineRange": [
|
||||
194,
|
||||
248
|
||||
],
|
||||
"summary": "Запускает CLI status JSON и обновляет module summary в menu bar app.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"refreshStatus"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "function:app/Sources/LemanaVPN/VPNManager.swift:connect",
|
||||
"type": "function",
|
||||
"name": "connect",
|
||||
"filePath": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"lineRange": [
|
||||
249,
|
||||
326
|
||||
],
|
||||
"summary": "Запускает CLI в JSON mode с выбранным режимом подключения и подписывается на stdout/stderr.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"connect"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "function:app/Sources/LemanaVPN/VPNManager.swift:disconnect",
|
||||
"type": "function",
|
||||
"name": "disconnect",
|
||||
"filePath": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"lineRange": [
|
||||
327,
|
||||
349
|
||||
],
|
||||
"summary": "Отправляет SIGINT текущему CLI process и останавливает reconnect state.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"disconnect"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:app/Sources/LemanaVPN/VPNManager.swift:parseEvent",
|
||||
"type": "function",
|
||||
"name": "parseEvent",
|
||||
"filePath": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"lineRange": [
|
||||
350,
|
||||
448
|
||||
],
|
||||
"summary": "Декодирует JSON events из CLI и переводит их в Swift state updates.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"parseEvent"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "function:app/Sources/LemanaVPN/VPNManager.swift:handleTermination",
|
||||
"type": "function",
|
||||
"name": "handleTermination",
|
||||
"filePath": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"lineRange": [
|
||||
449,
|
||||
576
|
||||
],
|
||||
"summary": "Обрабатывает завершение CLI process и решает, нужен ли автоматический reconnect.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"handleTermination"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "function:app/Sources/LemanaVPN/VPNManager.swift:startHealthCheck",
|
||||
"type": "function",
|
||||
"name": "startHealthCheck",
|
||||
"filePath": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"lineRange": [
|
||||
577,
|
||||
666
|
||||
],
|
||||
"summary": "Периодически проверяет tunnel health и запускает reconnect при повторных failures.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"startHealthCheck"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "file:bin/vpn-lemanapro.sh",
|
||||
"type": "file",
|
||||
"name": "vpn-lemanapro.sh",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"summary": "Главный runtime CLI: status JSON, sync credentials, Keychain/TOTP, runtime patches, SSO запуск, reconnect loop, DNS cleanup и фильтрация openconnect-lite output.",
|
||||
"tags": [
|
||||
"cli",
|
||||
"runtime",
|
||||
"sso"
|
||||
],
|
||||
"complexity": "complex"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_module_bool",
|
||||
"type": "function",
|
||||
"name": "_module_bool",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
197,
|
||||
203
|
||||
],
|
||||
"summary": "Значимая функция _module_bool в bin/vpn-lemanapro.sh.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"module-bool"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_patches_active",
|
||||
"type": "function",
|
||||
"name": "_patches_active",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
205,
|
||||
219
|
||||
],
|
||||
"summary": "Значимая функция _patches_active в bin/vpn-lemanapro.sh.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"patches-active"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_module_status_json",
|
||||
"type": "function",
|
||||
"name": "_module_status_json",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
225,
|
||||
264
|
||||
],
|
||||
"summary": "Собирает машинно-читаемый JSON со статусом модулей, credentials, app, DNS и patches.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"module-status-json"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_module_human_part",
|
||||
"type": "function",
|
||||
"name": "_module_human_part",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
266,
|
||||
275
|
||||
],
|
||||
"summary": "Значимая функция _module_human_part в bin/vpn-lemanapro.sh.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"module-human-part"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_module_status_human",
|
||||
"type": "function",
|
||||
"name": "_module_status_human",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
277,
|
||||
311
|
||||
],
|
||||
"summary": "Печатает человекочитаемую строку статуса модулей перед подключением.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"module-status-human"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_check_status",
|
||||
"type": "function",
|
||||
"name": "_check_status",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
313,
|
||||
375
|
||||
],
|
||||
"summary": "Проверяет PID/status file, running process, DNS target и возвращает disconnected/connected JSON.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"check-status"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_patch_oc",
|
||||
"type": "function",
|
||||
"name": "_patch_oc",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
377,
|
||||
604
|
||||
],
|
||||
"summary": "Применяет runtime patches к openconnect-lite для Keycloak autofill/manual/debug сценариев.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"patch-oc"
|
||||
],
|
||||
"complexity": "complex"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_normalize_totp_secret",
|
||||
"type": "function",
|
||||
"name": "_normalize_totp_secret",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
623,
|
||||
649
|
||||
],
|
||||
"summary": "Нормализует TOTP secret из raw secret или otpauth URI перед сохранением.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"normalize-totp-secret"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_persist_username",
|
||||
"type": "function",
|
||||
"name": "_persist_username",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
675,
|
||||
718
|
||||
],
|
||||
"summary": "Значимая функция _persist_username в bin/vpn-lemanapro.sh.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"persist-username"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_ensure_username",
|
||||
"type": "function",
|
||||
"name": "_ensure_username",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
720,
|
||||
745
|
||||
],
|
||||
"summary": "Значимая функция _ensure_username в bin/vpn-lemanapro.sh.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"ensure-username"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_configure_keychain",
|
||||
"type": "function",
|
||||
"name": "_configure_keychain",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
747,
|
||||
785
|
||||
],
|
||||
"summary": "Интерактивно сохраняет LDAP password и TOTP seed в macOS Keychain.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"configure-keychain"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_ensure_keychain_credentials",
|
||||
"type": "function",
|
||||
"name": "_ensure_keychain_credentials",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
787,
|
||||
814
|
||||
],
|
||||
"summary": "Проверяет наличие обязательных Keychain credentials перед auto/manual подключением.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"ensure-keychain-credentials"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_bw_unlock",
|
||||
"type": "function",
|
||||
"name": "_bw_unlock",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
823,
|
||||
889
|
||||
],
|
||||
"summary": "Открывает Bitwarden session с учетом Touch ID helper и fallback prompts.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"bw-unlock"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_sync_bitwarden",
|
||||
"type": "function",
|
||||
"name": "_sync_bitwarden",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
891,
|
||||
925
|
||||
],
|
||||
"summary": "Синхронизирует LDAP password и TOTP seed из Bitwarden в macOS Keychain.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"sync-bitwarden"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_sync_credentials",
|
||||
"type": "function",
|
||||
"name": "_sync_credentials",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
927,
|
||||
936
|
||||
],
|
||||
"summary": "Выбирает credential source и запускает соответствующий sync/preflight путь.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"sync-credentials"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_dns_cleanup",
|
||||
"type": "function",
|
||||
"name": "_dns_cleanup",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
938,
|
||||
954
|
||||
],
|
||||
"summary": "Вызывает narrow sudo wrapper для очистки VPN DNS при завершении сессии.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"dns-cleanup"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "function:bin/vpn-lemanapro.sh:_filter_output",
|
||||
"type": "function",
|
||||
"name": "_filter_output",
|
||||
"filePath": "bin/vpn-lemanapro.sh",
|
||||
"lineRange": [
|
||||
956,
|
||||
1006
|
||||
],
|
||||
"summary": "Фильтрует поток openconnect-lite, извлекает IP/expiry, пишет status JSON и события для app.",
|
||||
"tags": [
|
||||
"function",
|
||||
"runtime",
|
||||
"filter-output"
|
||||
],
|
||||
"complexity": "moderate"
|
||||
},
|
||||
{
|
||||
"id": "document:docs/goals/manual-first-auth-flow/GOAL.md",
|
||||
"type": "document",
|
||||
"name": "Manual-first auth goal",
|
||||
"filePath": "docs/goals/manual-first-auth-flow/GOAL.md",
|
||||
"summary": "Краткое описание цели manual-first authentication flow и ожидаемого пользовательского результата.",
|
||||
"tags": [
|
||||
"goal",
|
||||
"auth-flow",
|
||||
"planning"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "document:docs/goals/manual-first-auth-flow/PLAN.md",
|
||||
"type": "document",
|
||||
"name": "Manual-first auth plan",
|
||||
"filePath": "docs/goals/manual-first-auth-flow/PLAN.md",
|
||||
"summary": "Подробный план изменения installer UX, CLI auth profiles, Swift menu-bar surface и smoke coverage для manual-first сценария.",
|
||||
"tags": [
|
||||
"plan",
|
||||
"auth-flow",
|
||||
"implementation"
|
||||
],
|
||||
"complexity": "complex"
|
||||
},
|
||||
{
|
||||
"id": "document:docs/superpowers/plans/2026-05-24-keychain-totp-provider.md",
|
||||
"type": "document",
|
||||
"name": "Keychain TOTP provider plan",
|
||||
"filePath": "docs/superpowers/plans/2026-05-24-keychain-totp-provider.md",
|
||||
"summary": "Исторический implementation plan для разделения credential sources, нормализации TOTP и расширения smoke-проверок.",
|
||||
"tags": [
|
||||
"plan",
|
||||
"keychain",
|
||||
"totp"
|
||||
],
|
||||
"complexity": "complex"
|
||||
},
|
||||
{
|
||||
"id": "file:libexec/lemana-vpn-dns-cleanup",
|
||||
"type": "file",
|
||||
"name": "lemana-vpn-dns-cleanup",
|
||||
"filePath": "libexec/lemana-vpn-dns-cleanup",
|
||||
"summary": "Узкий root-owned wrapper, который сбрасывает только корпоративные DNS и обновляет macOS resolver cache.",
|
||||
"tags": [
|
||||
"dns",
|
||||
"sudo-wrapper",
|
||||
"cleanup"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "config:templates/openconnect-lite-config.toml",
|
||||
"type": "config",
|
||||
"name": "openconnect-lite-config.toml",
|
||||
"filePath": "templates/openconnect-lite-config.toml",
|
||||
"summary": "Шаблон профиля openconnect-lite: gateway, credentials mode и Keycloak auto-fill selectors для username, password и TOTP.",
|
||||
"tags": [
|
||||
"configuration",
|
||||
"openconnect-lite",
|
||||
"autofill"
|
||||
],
|
||||
"complexity": "simple"
|
||||
},
|
||||
{
|
||||
"id": "file:tests/smoke.sh",
|
||||
"type": "file",
|
||||
"name": "smoke.sh",
|
||||
"filePath": "tests/smoke.sh",
|
||||
"summary": "Non-live smoke suite: shell syntax, installer/uninstaller checks, Swift wiring, SSO patch snippets, config selectors и regression assertions.",
|
||||
"tags": [
|
||||
"test",
|
||||
"smoke",
|
||||
"verification"
|
||||
],
|
||||
"complexity": "complex"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/LemanaVPNApp.swift",
|
||||
"target": "class:app/Sources/LemanaVPN/LemanaVPNApp.swift:LemanaVPNApp",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/LemanaVPNApp.swift",
|
||||
"target": "class:app/Sources/LemanaVPN/LemanaVPNApp.swift:StatusBarLabel",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/LemanaVPNApp.swift",
|
||||
"target": "class:app/Sources/LemanaVPN/LemanaVPNApp.swift:VPNMenuView",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"target": "class:app/Sources/LemanaVPN/VPNManager.swift:VPNEvent",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"target": "class:app/Sources/LemanaVPN/VPNManager.swift:VPNStatusResponse",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"target": "class:app/Sources/LemanaVPN/VPNManager.swift:ModuleStatus",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"target": "class:app/Sources/LemanaVPN/VPNManager.swift:VPNState",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"target": "class:app/Sources/LemanaVPN/VPNManager.swift:VPNLaunchMode",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"target": "class:app/Sources/LemanaVPN/VPNManager.swift:VPNManager",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"target": "function:app/Sources/LemanaVPN/VPNManager.swift:refreshStatus",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"target": "function:app/Sources/LemanaVPN/VPNManager.swift:connect",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"target": "function:app/Sources/LemanaVPN/VPNManager.swift:disconnect",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"target": "function:app/Sources/LemanaVPN/VPNManager.swift:parseEvent",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"target": "function:app/Sources/LemanaVPN/VPNManager.swift:handleTermination",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"target": "function:app/Sources/LemanaVPN/VPNManager.swift:startHealthCheck",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_module_bool",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_patches_active",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_module_status_json",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_module_human_part",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_module_status_human",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_check_status",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_patch_oc",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_normalize_totp_secret",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_persist_username",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_ensure_username",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_configure_keychain",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_ensure_keychain_credentials",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_bw_unlock",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_sync_bitwarden",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_sync_credentials",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_dns_cleanup",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"source": "file:bin/vpn-lemanapro.sh",
|
||||
"target": "function:bin/vpn-lemanapro.sh:_filter_output",
|
||||
"type": "contains",
|
||||
"direction": "forward",
|
||||
"weight": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
149
.understand-anything/intermediate/batches.json
Normal file
149
.understand-anything/intermediate/batches.json
Normal file
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"algorithm": "louvain",
|
||||
"totalFiles": 17,
|
||||
"totalBatches": 2,
|
||||
"exportsByPath": {
|
||||
"app/Package.swift": [],
|
||||
"app/Sources/LemanaVPN/LemanaVPNApp.swift": [],
|
||||
"app/Sources/LemanaVPN/VPNManager.swift": [],
|
||||
"libexec/lemana-vpn-dns-cleanup": []
|
||||
},
|
||||
"batches": [
|
||||
{
|
||||
"batchIndex": 1,
|
||||
"files": [
|
||||
{
|
||||
"path": "AGENTS.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 88,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "README.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 579,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "install.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 943,
|
||||
"fileCategory": "script"
|
||||
},
|
||||
{
|
||||
"path": "uninstall.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 320,
|
||||
"fileCategory": "script"
|
||||
}
|
||||
],
|
||||
"batchImportData": {
|
||||
"AGENTS.md": [],
|
||||
"README.md": [],
|
||||
"install.sh": [],
|
||||
"uninstall.sh": []
|
||||
},
|
||||
"neighborMap": {}
|
||||
},
|
||||
{
|
||||
"batchIndex": 2,
|
||||
"files": [
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-operations/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 70,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-sso-autofill/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 80,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-testing/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 101,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "app/Package.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 14,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "app/Sources/LemanaVPN/LemanaVPNApp.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 190,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 697,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "bin/vpn-lemanapro.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 1090,
|
||||
"fileCategory": "script"
|
||||
},
|
||||
{
|
||||
"path": "docs/goals/manual-first-auth-flow/GOAL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 12,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "docs/goals/manual-first-auth-flow/PLAN.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 251,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "docs/superpowers/plans/2026-05-24-keychain-totp-provider.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 935,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "libexec/lemana-vpn-dns-cleanup",
|
||||
"language": "unknown",
|
||||
"sizeLines": 23,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "templates/openconnect-lite-config.toml",
|
||||
"language": "toml",
|
||||
"sizeLines": 32,
|
||||
"fileCategory": "config"
|
||||
},
|
||||
{
|
||||
"path": "tests/smoke.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 438,
|
||||
"fileCategory": "script"
|
||||
}
|
||||
],
|
||||
"batchImportData": {
|
||||
".agents/skills/lemana-vpn-operations/SKILL.md": [],
|
||||
".agents/skills/lemana-vpn-sso-autofill/SKILL.md": [],
|
||||
".agents/skills/lemana-vpn-testing/SKILL.md": [],
|
||||
"app/Package.swift": [],
|
||||
"app/Sources/LemanaVPN/LemanaVPNApp.swift": [],
|
||||
"app/Sources/LemanaVPN/VPNManager.swift": [],
|
||||
"bin/vpn-lemanapro.sh": [],
|
||||
"docs/goals/manual-first-auth-flow/GOAL.md": [],
|
||||
"docs/goals/manual-first-auth-flow/PLAN.md": [],
|
||||
"docs/superpowers/plans/2026-05-24-keychain-totp-provider.md": [],
|
||||
"libexec/lemana-vpn-dns-cleanup": [],
|
||||
"templates/openconnect-lite-config.toml": [],
|
||||
"tests/smoke.sh": []
|
||||
},
|
||||
"neighborMap": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
107
.understand-anything/intermediate/import-input.json
Normal file
107
.understand-anything/intermediate/import-input.json
Normal file
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"projectRoot": "/Users/dokril/git/home/lemana-vpn",
|
||||
"files": [
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-operations/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 70,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-sso-autofill/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 80,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-testing/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 101,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "AGENTS.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 88,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "app/Package.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 14,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "app/Sources/LemanaVPN/LemanaVPNApp.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 190,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 697,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "bin/vpn-lemanapro.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 1090,
|
||||
"fileCategory": "script"
|
||||
},
|
||||
{
|
||||
"path": "docs/goals/manual-first-auth-flow/GOAL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 12,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "docs/goals/manual-first-auth-flow/PLAN.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 251,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "docs/superpowers/plans/2026-05-24-keychain-totp-provider.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 935,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "install.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 943,
|
||||
"fileCategory": "script"
|
||||
},
|
||||
{
|
||||
"path": "libexec/lemana-vpn-dns-cleanup",
|
||||
"language": "unknown",
|
||||
"sizeLines": 23,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "README.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 579,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "templates/openconnect-lite-config.toml",
|
||||
"language": "toml",
|
||||
"sizeLines": 32,
|
||||
"fileCategory": "config"
|
||||
},
|
||||
{
|
||||
"path": "tests/smoke.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 438,
|
||||
"fileCategory": "script"
|
||||
},
|
||||
{
|
||||
"path": "uninstall.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 320,
|
||||
"fileCategory": "script"
|
||||
}
|
||||
]
|
||||
}
|
||||
27
.understand-anything/intermediate/import-map.json
Normal file
27
.understand-anything/intermediate/import-map.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"scriptCompleted": true,
|
||||
"stats": {
|
||||
"filesScanned": 17,
|
||||
"filesWithImports": 0,
|
||||
"totalEdges": 0
|
||||
},
|
||||
"importMap": {
|
||||
".agents/skills/lemana-vpn-operations/SKILL.md": [],
|
||||
".agents/skills/lemana-vpn-sso-autofill/SKILL.md": [],
|
||||
".agents/skills/lemana-vpn-testing/SKILL.md": [],
|
||||
"AGENTS.md": [],
|
||||
"app/Package.swift": [],
|
||||
"app/Sources/LemanaVPN/LemanaVPNApp.swift": [],
|
||||
"app/Sources/LemanaVPN/VPNManager.swift": [],
|
||||
"bin/vpn-lemanapro.sh": [],
|
||||
"docs/goals/manual-first-auth-flow/GOAL.md": [],
|
||||
"docs/goals/manual-first-auth-flow/PLAN.md": [],
|
||||
"docs/superpowers/plans/2026-05-24-keychain-totp-provider.md": [],
|
||||
"install.sh": [],
|
||||
"libexec/lemana-vpn-dns-cleanup": [],
|
||||
"README.md": [],
|
||||
"templates/openconnect-lite-config.toml": [],
|
||||
"tests/smoke.sh": [],
|
||||
"uninstall.sh": []
|
||||
}
|
||||
}
|
||||
126
.understand-anything/intermediate/scan-raw.json
Normal file
126
.understand-anything/intermediate/scan-raw.json
Normal file
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"scriptCompleted": true,
|
||||
"files": [
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-operations/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 70,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-sso-autofill/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 80,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-testing/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 101,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "AGENTS.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 88,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "app/Package.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 14,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "app/Sources/LemanaVPN/LemanaVPNApp.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 190,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 697,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "bin/vpn-lemanapro.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 1090,
|
||||
"fileCategory": "script"
|
||||
},
|
||||
{
|
||||
"path": "docs/goals/manual-first-auth-flow/GOAL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 12,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "docs/goals/manual-first-auth-flow/PLAN.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 251,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "docs/superpowers/plans/2026-05-24-keychain-totp-provider.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 935,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "install.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 943,
|
||||
"fileCategory": "script"
|
||||
},
|
||||
{
|
||||
"path": "libexec/lemana-vpn-dns-cleanup",
|
||||
"language": "unknown",
|
||||
"sizeLines": 23,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "README.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 579,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "templates/openconnect-lite-config.toml",
|
||||
"language": "toml",
|
||||
"sizeLines": 32,
|
||||
"fileCategory": "config"
|
||||
},
|
||||
{
|
||||
"path": "tests/smoke.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 438,
|
||||
"fileCategory": "script"
|
||||
},
|
||||
{
|
||||
"path": "uninstall.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 320,
|
||||
"fileCategory": "script"
|
||||
}
|
||||
],
|
||||
"totalFiles": 17,
|
||||
"filteredByIgnore": 3,
|
||||
"estimatedComplexity": "small",
|
||||
"stats": {
|
||||
"filesScanned": 17,
|
||||
"byCategory": {
|
||||
"docs": 8,
|
||||
"code": 4,
|
||||
"script": 4,
|
||||
"config": 1
|
||||
},
|
||||
"byLanguage": {
|
||||
"markdown": 8,
|
||||
"swift": 3,
|
||||
"shell": 4,
|
||||
"unknown": 1,
|
||||
"toml": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
163
.understand-anything/intermediate/scan-result.json
Normal file
163
.understand-anything/intermediate/scan-result.json
Normal file
@@ -0,0 +1,163 @@
|
||||
{
|
||||
"name": "Lemana VPN",
|
||||
"projectName": "Lemana VPN",
|
||||
"description": "macOS-пакет для корпоративного VPN Lemana: installer, CLI runtime, SSO/autofill через openconnect-lite, DNS cleanup wrapper и Swift menu bar app.",
|
||||
"rawDescription": "CLI-установка корпоративного VPN vpn.lemanapro.ru для macOS.",
|
||||
"readmeHead": "# Lemana VPN\n\nCLI-установка корпоративного VPN `vpn.lemanapro.ru` для macOS.\n\n**Модули по умолчанию:** Core: включён; Bitwarden: включён; Touch ID: включён; DNS cleanup: включён; Swift Menu Bar app: включён; автозапуск приложения: включён; runtime-патчи: применяются автоматически перед подключением.\n\n**Credential sources:** `bitwarden` синхронизирует LDAP-пароль и TOTP seed из Bitwarden в macOS Keychain; `keychain` хранит LDAP-пароль и постоянный TOTP seed напрямую в macOS Keychain. Оба источника используют один и тот же runtime `openconnect-lite` для SSO/autofill.\n\nРепозиторий собирает в один воспроизводимый пакет то, что раньше было ручной локальной настройкой:\n\n- `openconnect` как VPN-клиент;\n- `openconnect-lite` для SAML SSO через Keycloak;\n- опциональный Bitwarden CLI для LDAP-пароля и TOTP seed;\n- опциональный Touch ID helper для мастер-пароля Bitwarden;\n- Swift Menu Bar app `LemanaVPN.app`;\n- безопасный DNS cleanup через root-owned wrapper;\n- алиасы `vpn`, `vpn-auto`, `vpn-manual`, `vpn-manual-full`, `vpn-debug`, `vpn-fix-dns`.\n\n## Быстрая установка\n\n```sh\ncurl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh\n```\n\nЕсли установка запущена из терминала, скрипт работает как интерактивный wizard: проверит, что уже стоит, спросит как хранить credentials, предложит нужные модули и проведёт через настройку.\n\nПосле установки открой новый shell или выполни:\n\n```sh\nexec zsh\nvpn\n```\n\n## Интерактивная установка\n\nОбычный путь — запустить installer без флагов и ответить на вопросы:\n\n```sh\ncurl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh\n```\n\nПервый важный вопрос — как хранить VPN credentials:\n\n```text\nКак хранить VPN credentials?\n 1) Bitwarden -> macOS Keychain\n 2) macOS Keychain: ввести LDAP password и TOTP seed сейчас\n 3) macOS Keychain: настрою вручную позже\nВыбор [1/2/3, Enter=1]:\n```\n\nЧто означают варианты:\n\n- `1` — использовать Bitwarden как sync-provider: installer поставит/проверит `bw`, а при запуске `vpn` CLI переложит LDAP password и TOTP seed из Bitwarden в macOS Keychain.\n- `2` — бесплатный Keychain-only путь: после установки CLI спросит LDAP password и постоянный TOTP seed или `otpauth://...secret=...`, затем сохранит их в macOS Keychain.\n- `3` — поставить CLI/app сейчас, а credentials настроить позже командой `vpn --configure-keychain`.\n\nДальше installer спросит только про системные модули: Touch ID для Bitwarden, sudoers, aliases, Swift Menu Bar app и автозапуск.\n\nФлаги остаются для CI, повторяемых установок и диагностики. Для обычной установки они не нужны.\n\nПроверить действия без изменений:\n\n```sh\ncurl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --dry-run\n```\n\nПример неинтерактивного режима для автоматизации:\n\n```sh\ncurl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh \\\n | sh -s -- --non-interactive --credential-source keychain --configure-keychain\n```\n\nПринудительно включить интерактивные вопросы:\n\n```sh\n",
|
||||
"languages": [
|
||||
"markdown",
|
||||
"swift",
|
||||
"shell",
|
||||
"unknown",
|
||||
"toml"
|
||||
],
|
||||
"frameworks": [
|
||||
"SwiftUI",
|
||||
"AppKit",
|
||||
"openconnect-lite",
|
||||
"Bitwarden CLI",
|
||||
"macOS Keychain"
|
||||
],
|
||||
"files": [
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-operations/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 70,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-sso-autofill/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 80,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-testing/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 101,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "AGENTS.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 88,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "app/Package.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 14,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "app/Sources/LemanaVPN/LemanaVPNApp.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 190,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 697,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "bin/vpn-lemanapro.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 1090,
|
||||
"fileCategory": "script"
|
||||
},
|
||||
{
|
||||
"path": "docs/goals/manual-first-auth-flow/GOAL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 12,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "docs/goals/manual-first-auth-flow/PLAN.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 251,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "docs/superpowers/plans/2026-05-24-keychain-totp-provider.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 935,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "install.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 943,
|
||||
"fileCategory": "script"
|
||||
},
|
||||
{
|
||||
"path": "libexec/lemana-vpn-dns-cleanup",
|
||||
"language": "unknown",
|
||||
"sizeLines": 23,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "README.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 579,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "templates/openconnect-lite-config.toml",
|
||||
"language": "toml",
|
||||
"sizeLines": 32,
|
||||
"fileCategory": "config"
|
||||
},
|
||||
{
|
||||
"path": "tests/smoke.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 438,
|
||||
"fileCategory": "script"
|
||||
},
|
||||
{
|
||||
"path": "uninstall.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 320,
|
||||
"fileCategory": "script"
|
||||
}
|
||||
],
|
||||
"totalFiles": 17,
|
||||
"filteredByIgnore": 3,
|
||||
"estimatedComplexity": "small",
|
||||
"stats": {
|
||||
"filesScanned": 17,
|
||||
"byCategory": {
|
||||
"docs": 8,
|
||||
"code": 4,
|
||||
"script": 4,
|
||||
"config": 1
|
||||
},
|
||||
"byLanguage": {
|
||||
"markdown": 8,
|
||||
"swift": 3,
|
||||
"shell": 4,
|
||||
"unknown": 1,
|
||||
"toml": 1
|
||||
}
|
||||
},
|
||||
"importMap": {
|
||||
".agents/skills/lemana-vpn-operations/SKILL.md": [],
|
||||
".agents/skills/lemana-vpn-sso-autofill/SKILL.md": [],
|
||||
".agents/skills/lemana-vpn-testing/SKILL.md": [],
|
||||
"AGENTS.md": [],
|
||||
"app/Package.swift": [],
|
||||
"app/Sources/LemanaVPN/LemanaVPNApp.swift": [],
|
||||
"app/Sources/LemanaVPN/VPNManager.swift": [],
|
||||
"bin/vpn-lemanapro.sh": [],
|
||||
"docs/goals/manual-first-auth-flow/GOAL.md": [],
|
||||
"docs/goals/manual-first-auth-flow/PLAN.md": [],
|
||||
"docs/superpowers/plans/2026-05-24-keychain-totp-provider.md": [],
|
||||
"install.sh": [],
|
||||
"libexec/lemana-vpn-dns-cleanup": [],
|
||||
"README.md": [],
|
||||
"templates/openconnect-lite-config.toml": [],
|
||||
"tests/smoke.sh": [],
|
||||
"uninstall.sh": []
|
||||
}
|
||||
}
|
||||
206
.understand-anything/tmp/build-batches.cjs
Normal file
206
.understand-anything/tmp/build-batches.cjs
Normal file
@@ -0,0 +1,206 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const root = process.cwd();
|
||||
const inter = path.join(root, ".understand-anything", "intermediate");
|
||||
const tmp = path.join(root, ".understand-anything", "tmp");
|
||||
const scan = JSON.parse(fs.readFileSync(path.join(inter, "scan-result.json"), "utf8"));
|
||||
const batches = JSON.parse(fs.readFileSync(path.join(inter, "batches.json"), "utf8")).batches;
|
||||
|
||||
const fileInfo = new Map(scan.files.map((f) => [f.path, f]));
|
||||
const fileNodeId = (file) => {
|
||||
if (file.fileCategory === "docs") return `document:${file.path}`;
|
||||
if (file.fileCategory === "config") return `config:${file.path}`;
|
||||
return `file:${file.path}`;
|
||||
};
|
||||
const edge = (source, target, type, weight) => ({ source, target, type, direction: "forward", weight });
|
||||
const complexity = (lines) => (lines > 200 ? "complex" : lines > 50 ? "moderate" : "simple");
|
||||
const nameOf = (p) => p.split("/").pop();
|
||||
|
||||
const fileMeta = {
|
||||
".agents/skills/lemana-vpn-operations/SKILL.md": ["Lemana VPN Operations skill", "Инструкции для операционных задач: установка, обновление, статусы, безопасные ответы пользователю и live-connection правила.", ["skill", "operations", "documentation"]],
|
||||
".agents/skills/lemana-vpn-sso-autofill/SKILL.md": ["Lemana VPN SSO skill", "Инструкции по Bitwarden, Keychain, TOTP, Keycloak SSO, autofill и runtime-патчам openconnect-lite.", ["skill", "sso", "credentials"]],
|
||||
".agents/skills/lemana-vpn-testing/SKILL.md": ["Lemana VPN Testing skill", "Инструкции по non-live проверкам, status checks, live VPN safety и финальному отчету для изменений в репозитории.", ["skill", "testing", "verification"]],
|
||||
"AGENTS.md": ["AGENTS.md", "Репозиторные правила для агентов: модель системы, live VPN safety, тестовая лестница и контракт финального ответа.", ["agent-rules", "documentation", "safety"]],
|
||||
"README.md": ["README.md", "Основная пользовательская документация Lemana VPN: установка, credential sources, модули, использование, диагностика и удаление.", ["documentation", "overview", "user-guide"]],
|
||||
"docs/goals/manual-first-auth-flow/GOAL.md": ["Manual-first auth goal", "Краткое описание цели manual-first authentication flow и ожидаемого пользовательского результата.", ["goal", "auth-flow", "planning"]],
|
||||
"docs/goals/manual-first-auth-flow/PLAN.md": ["Manual-first auth plan", "Подробный план изменения installer UX, CLI auth profiles, Swift menu-bar surface и smoke coverage для manual-first сценария.", ["plan", "auth-flow", "implementation"]],
|
||||
"docs/superpowers/plans/2026-05-24-keychain-totp-provider.md": ["Keychain TOTP provider plan", "Исторический implementation plan для разделения credential sources, нормализации TOTP и расширения smoke-проверок.", ["plan", "keychain", "totp"]],
|
||||
"app/Package.swift": ["Package.swift", "Swift Package manifest для сборки executable target LemanaVPN под macOS 13+.", ["swift", "build-system", "menu-bar-app"]],
|
||||
"app/Sources/LemanaVPN/LemanaVPNApp.swift": ["LemanaVPNApp.swift", "SwiftUI/AppKit menu bar surface: отображает состояние VPN, кнопки подключения, логи и действия пользователя.", ["swiftui", "menu-bar", "ui"]],
|
||||
"app/Sources/LemanaVPN/VPNManager.swift": ["VPNManager.swift", "Основной state manager Swift-приложения: запускает CLI в JSON mode, декодирует события, отслеживает health и управляет reconnect/disconnect.", ["swift", "state-manager", "cli-bridge"]],
|
||||
"bin/vpn-lemanapro.sh": ["vpn-lemanapro.sh", "Главный runtime CLI: status JSON, sync credentials, Keychain/TOTP, runtime patches, SSO запуск, reconnect loop, DNS cleanup и фильтрация openconnect-lite output.", ["cli", "runtime", "sso"]],
|
||||
"install.sh": ["install.sh", "Installer wizard для Homebrew/pipx/openconnect-lite, CLI, config, DNS wrapper, sudoers, Touch ID helper, Swift app, launch agent и shell aliases.", ["installer", "wizard", "macos"]],
|
||||
"uninstall.sh": ["uninstall.sh", "Uninstaller для восстановления runtime patch backups, удаления CLI/app/config/sudoers/aliases и опциональной очистки Keychain/Touch ID state.", ["uninstaller", "cleanup", "macos"]],
|
||||
"libexec/lemana-vpn-dns-cleanup": ["lemana-vpn-dns-cleanup", "Узкий root-owned wrapper, который сбрасывает только корпоративные DNS и обновляет macOS resolver cache.", ["dns", "sudo-wrapper", "cleanup"]],
|
||||
"templates/openconnect-lite-config.toml": ["openconnect-lite-config.toml", "Шаблон профиля openconnect-lite: gateway, credentials mode и Keycloak auto-fill selectors для username, password и TOTP.", ["configuration", "openconnect-lite", "autofill"]],
|
||||
"tests/smoke.sh": ["smoke.sh", "Non-live smoke suite: shell syntax, installer/uninstaller checks, Swift wiring, SSO patch snippets, config selectors и regression assertions.", ["test", "smoke", "verification"]],
|
||||
};
|
||||
|
||||
const functionSummaries = {
|
||||
choose_credential_source_interactive: "Показывает интерактивный выбор credential source и нормализует ответ пользователя.",
|
||||
choose_modules: "Определяет набор устанавливаемых модулей с учетом интерактивного режима и флагов installer.",
|
||||
install_openconnect_lite: "Устанавливает или проверяет openconnect-lite runtime, необходимый для SAML SSO.",
|
||||
install_cli: "Копирует основной CLI и uninstall helper в пользовательский bin.",
|
||||
install_config: "Устанавливает шаблон openconnect-lite config и сохраняет локальную конфигурацию модулей.",
|
||||
install_dns_cleanup: "Устанавливает root-owned DNS cleanup wrapper для безопасного сброса корпоративных DNS.",
|
||||
install_sudoers: "Пишет ограниченные sudoers правила для openconnect и DNS cleanup wrapper.",
|
||||
install_touchid_helper: "Собирает и устанавливает Touch ID helper для Bitwarden master password.",
|
||||
install_menu_bar_app: "Собирает release-версию Swift menu bar app и устанавливает LemanaVPN.app.",
|
||||
install_launch_agent: "Создает LaunchAgent для автозапуска menu bar app при логине.",
|
||||
restart_running_menu_bar_app: "Перезапускает уже открытое menu bar приложение после обновления bundle.",
|
||||
install_shell_aliases: "Добавляет idempotent zsh aliases для CLI режимов подключения и диагностики.",
|
||||
maybe_login_bitwarden: "Проверяет Bitwarden session и при необходимости запускает login/unlock flow.",
|
||||
restore_openconnect_lite_patch: "Возвращает backup исходника openconnect-lite, если runtime patch был установлен.",
|
||||
remove_zshrc_block: "Удаляет idempotent блок Lemana VPN aliases из zshrc.",
|
||||
remove_keychain_entries: "Опционально удаляет password, TOTP seed и username из macOS Keychain.",
|
||||
_module_status_json: "Собирает машинно-читаемый JSON со статусом модулей, credentials, app, DNS и patches.",
|
||||
_module_status_human: "Печатает человекочитаемую строку статуса модулей перед подключением.",
|
||||
_check_status: "Проверяет PID/status file, running process, DNS target и возвращает disconnected/connected JSON.",
|
||||
_patch_oc: "Применяет runtime patches к openconnect-lite для Keycloak autofill/manual/debug сценариев.",
|
||||
_normalize_totp_secret: "Нормализует TOTP secret из raw secret или otpauth URI перед сохранением.",
|
||||
_configure_keychain: "Интерактивно сохраняет LDAP password и TOTP seed в macOS Keychain.",
|
||||
_ensure_keychain_credentials: "Проверяет наличие обязательных Keychain credentials перед auto/manual подключением.",
|
||||
_bw_unlock: "Открывает Bitwarden session с учетом Touch ID helper и fallback prompts.",
|
||||
_sync_bitwarden: "Синхронизирует LDAP password и TOTP seed из Bitwarden в macOS Keychain.",
|
||||
_sync_credentials: "Выбирает credential source и запускает соответствующий sync/preflight путь.",
|
||||
_dns_cleanup: "Вызывает narrow sudo wrapper для очистки VPN DNS при завершении сессии.",
|
||||
_filter_output: "Фильтрует поток openconnect-lite, извлекает IP/expiry, пишет status JSON и события для app.",
|
||||
refreshStatus: "Запускает CLI status JSON и обновляет module summary в menu bar app.",
|
||||
connect: "Запускает CLI в JSON mode с выбранным режимом подключения и подписывается на stdout/stderr.",
|
||||
disconnect: "Отправляет SIGINT текущему CLI process и останавливает reconnect state.",
|
||||
parseEvent: "Декодирует JSON events из CLI и переводит их в Swift state updates.",
|
||||
handleTermination: "Обрабатывает завершение CLI process и решает, нужен ли автоматический reconnect.",
|
||||
startHealthCheck: "Периодически проверяет tunnel health и запускает reconnect при повторных failures.",
|
||||
};
|
||||
|
||||
const swiftTypeSummaries = {
|
||||
LemanaVPNApp: "Точка входа SwiftUI menu bar app, связывает VPNManager с MenuBarExtra.",
|
||||
StatusBarLabel: "Компактный status bar label с иконкой и оставшимся временем сессии.",
|
||||
VPNMenuView: "Основной menu UI с командами auto/manual/manual-full connect, disconnect, logs и quit.",
|
||||
VPNEvent: "Decodable-модель JSON events, которые runtime CLI отправляет в Swift app.",
|
||||
VPNStatusResponse: "Decodable envelope для ответа `vpn --status --json`.",
|
||||
ModuleStatus: "Модель статуса модулей и вычисление summary/systemImage для menu bar app.",
|
||||
VPNState: "Finite state model для disconnected/connecting/connected/reconnecting/error состояний.",
|
||||
VPNLaunchMode: "Маппинг app launch modes на CLI flags `--auto`, `--manual`, `--manual-full`.",
|
||||
VPNManager: "ObservableObject, который мостит Swift menu bar app с shell CLI, JSON events и health checks.",
|
||||
};
|
||||
|
||||
function makeFileNode(file) {
|
||||
const meta = fileMeta[file.path] || [nameOf(file.path), `Файл проекта ${file.path}.`, ["project-file", file.language, file.fileCategory]];
|
||||
const type = file.fileCategory === "docs" ? "document" : file.fileCategory === "config" ? "config" : "file";
|
||||
return {
|
||||
id: fileNodeId(file),
|
||||
type,
|
||||
name: meta[0],
|
||||
filePath: file.path,
|
||||
summary: meta[1],
|
||||
tags: meta[2],
|
||||
complexity: complexity(file.sizeLines),
|
||||
};
|
||||
}
|
||||
|
||||
function shouldIncludeFunction(filePath, fn) {
|
||||
if (fn.name === "autoFill" || fn.name === "vpn") return false;
|
||||
const span = fn.endLine - fn.startLine + 1;
|
||||
if (functionSummaries[fn.name]) return true;
|
||||
if (fn.name === "usage" || fn.name === "main") return true;
|
||||
if (/^(install_|remove_|restore_|choose_|_sync|_ensure|_configure|_patch|_check|_filter|_module|_dns)/.test(fn.name)) return true;
|
||||
return span >= 18 && !/^log|color_line|die|run|need_cmd|has_tty|bool_word|_emit|_json_get|_can_prompt|_prompt_read/.test(fn.name);
|
||||
}
|
||||
|
||||
function makeFunctionNode(filePath, fn) {
|
||||
const id = `function:${filePath}:${fn.name}`;
|
||||
return {
|
||||
id,
|
||||
type: "function",
|
||||
name: fn.name,
|
||||
filePath,
|
||||
lineRange: [fn.startLine, fn.endLine],
|
||||
summary: functionSummaries[fn.name] || `Значимая функция ${fn.name} в ${filePath}.`,
|
||||
tags: ["function", filePath.includes("test") ? "verification" : "runtime", fn.name.replace(/^_+/, "").replaceAll("_", "-")],
|
||||
complexity: complexity(fn.endLine - fn.startLine + 1),
|
||||
};
|
||||
}
|
||||
|
||||
function swiftDecls(filePath) {
|
||||
if (!filePath.endsWith(".swift")) return [];
|
||||
const text = fs.readFileSync(path.join(root, filePath), "utf8");
|
||||
const lines = text.split(/\n/);
|
||||
const decls = [];
|
||||
lines.forEach((line, index) => {
|
||||
const m = line.match(/^\s*(?:@main\s+)?(?:struct|class|enum)\s+([A-Za-z_][A-Za-z0-9_]*)/);
|
||||
if (m && swiftTypeSummaries[m[1]]) {
|
||||
decls.push({ name: m[1], startLine: index + 1 });
|
||||
}
|
||||
});
|
||||
for (let i = 0; i < decls.length; i += 1) {
|
||||
decls[i].endLine = (decls[i + 1]?.startLine || lines.length + 1) - 1;
|
||||
}
|
||||
return decls;
|
||||
}
|
||||
|
||||
function swiftMethods(filePath) {
|
||||
if (!filePath.endsWith("VPNManager.swift")) return [];
|
||||
const text = fs.readFileSync(path.join(root, filePath), "utf8");
|
||||
const lines = text.split(/\n/);
|
||||
const wanted = new Set(["refreshStatus", "connect", "disconnect", "parseEvent", "handleTermination", "startHealthCheck"]);
|
||||
const methods = [];
|
||||
lines.forEach((line, index) => {
|
||||
const m = line.match(/^\s*(?:private\s+)?func\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(/);
|
||||
if (m && wanted.has(m[1])) methods.push({ name: m[1], startLine: index + 1 });
|
||||
});
|
||||
for (let i = 0; i < methods.length; i += 1) {
|
||||
methods[i].endLine = (methods[i + 1]?.startLine || Math.min(lines.length + 1, methods[i].startLine + 90)) - 1;
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
function graphForBatch(batch) {
|
||||
const extract = JSON.parse(fs.readFileSync(path.join(tmp, `ua-file-extract-results-${batch.batchIndex}.json`), "utf8"));
|
||||
const nodes = [];
|
||||
const edges = [];
|
||||
const resultByPath = new Map(extract.results.map((r) => [r.path, r]));
|
||||
|
||||
for (const file of batch.files) {
|
||||
const fileNode = makeFileNode(file);
|
||||
nodes.push(fileNode);
|
||||
const result = resultByPath.get(file.path);
|
||||
|
||||
for (const fn of result?.functions || []) {
|
||||
if (!shouldIncludeFunction(file.path, fn)) continue;
|
||||
const node = makeFunctionNode(file.path, fn);
|
||||
nodes.push(node);
|
||||
edges.push(edge(fileNode.id, node.id, "contains", 1.0));
|
||||
}
|
||||
|
||||
for (const decl of swiftDecls(file.path)) {
|
||||
const node = {
|
||||
id: `class:${file.path}:${decl.name}`,
|
||||
type: "class",
|
||||
name: decl.name,
|
||||
filePath: file.path,
|
||||
lineRange: [decl.startLine, decl.endLine],
|
||||
summary: swiftTypeSummaries[decl.name],
|
||||
tags: ["swift", "type", decl.name === "VPNManager" ? "state-manager" : "menu-bar-app"],
|
||||
complexity: complexity(decl.endLine - decl.startLine + 1),
|
||||
};
|
||||
nodes.push(node);
|
||||
edges.push(edge(fileNode.id, node.id, "contains", 1.0));
|
||||
}
|
||||
|
||||
for (const fn of swiftMethods(file.path)) {
|
||||
const node = makeFunctionNode(file.path, fn);
|
||||
nodes.push(node);
|
||||
edges.push(edge(fileNode.id, node.id, "contains", 1.0));
|
||||
}
|
||||
}
|
||||
|
||||
return { nodes, edges };
|
||||
}
|
||||
|
||||
for (const batch of batches) {
|
||||
const graph = graphForBatch(batch);
|
||||
fs.writeFileSync(path.join(inter, `batch-${batch.batchIndex}.json`), JSON.stringify(graph, null, 2));
|
||||
}
|
||||
|
||||
35
.understand-anything/tmp/ua-file-analyzer-input-1.json
Normal file
35
.understand-anything/tmp/ua-file-analyzer-input-1.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"projectRoot": "/Users/dokril/git/home/lemana-vpn",
|
||||
"batchFiles": [
|
||||
{
|
||||
"path": "AGENTS.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 88,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "README.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 579,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "install.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 943,
|
||||
"fileCategory": "script"
|
||||
},
|
||||
{
|
||||
"path": "uninstall.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 320,
|
||||
"fileCategory": "script"
|
||||
}
|
||||
],
|
||||
"batchImportData": {
|
||||
"AGENTS.md": [],
|
||||
"README.md": [],
|
||||
"install.sh": [],
|
||||
"uninstall.sh": []
|
||||
}
|
||||
}
|
||||
98
.understand-anything/tmp/ua-file-analyzer-input-2.json
Normal file
98
.understand-anything/tmp/ua-file-analyzer-input-2.json
Normal file
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"projectRoot": "/Users/dokril/git/home/lemana-vpn",
|
||||
"batchFiles": [
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-operations/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 70,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-sso-autofill/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 80,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-testing/SKILL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 101,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "app/Package.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 14,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "app/Sources/LemanaVPN/LemanaVPNApp.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 190,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"language": "swift",
|
||||
"sizeLines": 697,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "bin/vpn-lemanapro.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 1090,
|
||||
"fileCategory": "script"
|
||||
},
|
||||
{
|
||||
"path": "docs/goals/manual-first-auth-flow/GOAL.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 12,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "docs/goals/manual-first-auth-flow/PLAN.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 251,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "docs/superpowers/plans/2026-05-24-keychain-totp-provider.md",
|
||||
"language": "markdown",
|
||||
"sizeLines": 935,
|
||||
"fileCategory": "docs"
|
||||
},
|
||||
{
|
||||
"path": "libexec/lemana-vpn-dns-cleanup",
|
||||
"language": "unknown",
|
||||
"sizeLines": 23,
|
||||
"fileCategory": "code"
|
||||
},
|
||||
{
|
||||
"path": "templates/openconnect-lite-config.toml",
|
||||
"language": "toml",
|
||||
"sizeLines": 32,
|
||||
"fileCategory": "config"
|
||||
},
|
||||
{
|
||||
"path": "tests/smoke.sh",
|
||||
"language": "shell",
|
||||
"sizeLines": 438,
|
||||
"fileCategory": "script"
|
||||
}
|
||||
],
|
||||
"batchImportData": {
|
||||
".agents/skills/lemana-vpn-operations/SKILL.md": [],
|
||||
".agents/skills/lemana-vpn-sso-autofill/SKILL.md": [],
|
||||
".agents/skills/lemana-vpn-testing/SKILL.md": [],
|
||||
"app/Package.swift": [],
|
||||
"app/Sources/LemanaVPN/LemanaVPNApp.swift": [],
|
||||
"app/Sources/LemanaVPN/VPNManager.swift": [],
|
||||
"bin/vpn-lemanapro.sh": [],
|
||||
"docs/goals/manual-first-auth-flow/GOAL.md": [],
|
||||
"docs/goals/manual-first-auth-flow/PLAN.md": [],
|
||||
"docs/superpowers/plans/2026-05-24-keychain-totp-provider.md": [],
|
||||
"libexec/lemana-vpn-dns-cleanup": [],
|
||||
"templates/openconnect-lite-config.toml": [],
|
||||
"tests/smoke.sh": []
|
||||
}
|
||||
}
|
||||
540
.understand-anything/tmp/ua-file-extract-results-1.json
Normal file
540
.understand-anything/tmp/ua-file-extract-results-1.json
Normal file
@@ -0,0 +1,540 @@
|
||||
{
|
||||
"scriptCompleted": true,
|
||||
"filesAnalyzed": 4,
|
||||
"filesSkipped": [],
|
||||
"results": [
|
||||
{
|
||||
"path": "AGENTS.md",
|
||||
"language": "markdown",
|
||||
"fileCategory": "docs",
|
||||
"totalLines": 88,
|
||||
"nonEmptyLines": 61,
|
||||
"sections": [
|
||||
{
|
||||
"heading": "Lemana VPN Agent Instructions",
|
||||
"level": 1,
|
||||
"line": 1
|
||||
},
|
||||
{
|
||||
"heading": "Scope",
|
||||
"level": 2,
|
||||
"line": 3
|
||||
},
|
||||
{
|
||||
"heading": "System Model",
|
||||
"level": 2,
|
||||
"line": 13
|
||||
},
|
||||
{
|
||||
"heading": "Live VPN Safety",
|
||||
"level": 2,
|
||||
"line": 22
|
||||
},
|
||||
{
|
||||
"heading": "Testing",
|
||||
"level": 2,
|
||||
"line": 44
|
||||
},
|
||||
{
|
||||
"heading": "Answer Contract",
|
||||
"level": 2,
|
||||
"line": 60
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"importCount": 0,
|
||||
"exportCount": 0,
|
||||
"functionCount": 0,
|
||||
"classCount": 0,
|
||||
"sectionCount": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "README.md",
|
||||
"language": "markdown",
|
||||
"fileCategory": "docs",
|
||||
"totalLines": 579,
|
||||
"nonEmptyLines": 405,
|
||||
"sections": [
|
||||
{
|
||||
"heading": "Lemana VPN",
|
||||
"level": 1,
|
||||
"line": 1
|
||||
},
|
||||
{
|
||||
"heading": "Быстрая установка",
|
||||
"level": 2,
|
||||
"line": 19
|
||||
},
|
||||
{
|
||||
"heading": "Интерактивная установка",
|
||||
"level": 2,
|
||||
"line": 34
|
||||
},
|
||||
{
|
||||
"heading": "Что ставится",
|
||||
"level": 2,
|
||||
"line": 94
|
||||
},
|
||||
{
|
||||
"heading": "Статус модулей",
|
||||
"level": 2,
|
||||
"line": 111
|
||||
},
|
||||
{
|
||||
"heading": "Интерактивная установка",
|
||||
"level": 2,
|
||||
"line": 148
|
||||
},
|
||||
{
|
||||
"heading": "Логи установщика и удаления",
|
||||
"level": 2,
|
||||
"line": 183
|
||||
},
|
||||
{
|
||||
"heading": "Модули",
|
||||
"level": 2,
|
||||
"line": 201
|
||||
},
|
||||
{
|
||||
"heading": "Core",
|
||||
"level": 3,
|
||||
"line": 203
|
||||
},
|
||||
{
|
||||
"heading": "Как работают credential sources",
|
||||
"level": 3,
|
||||
"line": 214
|
||||
},
|
||||
{
|
||||
"heading": "Bitwarden",
|
||||
"level": 3,
|
||||
"line": 235
|
||||
},
|
||||
{
|
||||
"heading": "Если Bitwarden нет",
|
||||
"level": 3,
|
||||
"line": 259
|
||||
},
|
||||
{
|
||||
"heading": "Touch ID",
|
||||
"level": 3,
|
||||
"line": 302
|
||||
},
|
||||
{
|
||||
"heading": "Swift Menu Bar app",
|
||||
"level": 3,
|
||||
"line": 314
|
||||
},
|
||||
{
|
||||
"heading": "Использование",
|
||||
"level": 2,
|
||||
"line": 352
|
||||
},
|
||||
{
|
||||
"heading": "Настройка",
|
||||
"level": 2,
|
||||
"line": 385
|
||||
},
|
||||
{
|
||||
"heading": "Bitwarden item",
|
||||
"level": 2,
|
||||
"line": 415
|
||||
},
|
||||
{
|
||||
"heading": "Почему DNS wrapper, а не wildcard sudoers",
|
||||
"level": 2,
|
||||
"line": 426
|
||||
},
|
||||
{
|
||||
"heading": "Runtime-патчи openconnect-lite",
|
||||
"level": 2,
|
||||
"line": 432
|
||||
},
|
||||
{
|
||||
"heading": "Диагностика",
|
||||
"level": 2,
|
||||
"line": 460
|
||||
},
|
||||
{
|
||||
"heading": "Удаление",
|
||||
"level": 2,
|
||||
"line": 528
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"importCount": 0,
|
||||
"exportCount": 0,
|
||||
"functionCount": 0,
|
||||
"classCount": 0,
|
||||
"sectionCount": 21
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "install.sh",
|
||||
"language": "shell",
|
||||
"fileCategory": "script",
|
||||
"totalLines": 943,
|
||||
"nonEmptyLines": 824,
|
||||
"functions": [
|
||||
{
|
||||
"name": "usage",
|
||||
"startLine": 72,
|
||||
"endLine": 106,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "log",
|
||||
"startLine": 220,
|
||||
"endLine": 222,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "color_line",
|
||||
"startLine": 224,
|
||||
"endLine": 228,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "log_step",
|
||||
"startLine": 230,
|
||||
"endLine": 232,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "log_info",
|
||||
"startLine": 234,
|
||||
"endLine": 236,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "log_detail",
|
||||
"startLine": 238,
|
||||
"endLine": 240,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "log_ok",
|
||||
"startLine": 242,
|
||||
"endLine": 244,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "log_warn",
|
||||
"startLine": 246,
|
||||
"endLine": 248,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "log_skip",
|
||||
"startLine": 250,
|
||||
"endLine": 252,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "die",
|
||||
"startLine": 254,
|
||||
"endLine": 257,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "run",
|
||||
"startLine": 259,
|
||||
"endLine": 269,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "need_cmd",
|
||||
"startLine": 271,
|
||||
"endLine": 273,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "has_tty",
|
||||
"startLine": 275,
|
||||
"endLine": 277,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "interactive_enabled",
|
||||
"startLine": 279,
|
||||
"endLine": 286,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "prompt_printf",
|
||||
"startLine": 288,
|
||||
"endLine": 294,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "prompt_read_answer",
|
||||
"startLine": 296,
|
||||
"endLine": 302,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "yes_no",
|
||||
"startLine": 304,
|
||||
"endLine": 330,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "choose_credential_source_interactive",
|
||||
"startLine": 332,
|
||||
"endLine": 382,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "bool_word",
|
||||
"startLine": 384,
|
||||
"endLine": 390,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "keychain_has",
|
||||
"startLine": 392,
|
||||
"endLine": 394,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "zsh_aliases_installed",
|
||||
"startLine": 396,
|
||||
"endLine": 398,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "print_detected_state",
|
||||
"startLine": 400,
|
||||
"endLine": 417,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "choose_modules",
|
||||
"startLine": 419,
|
||||
"endLine": 514,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "script_dir",
|
||||
"startLine": 516,
|
||||
"endLine": 521,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "download_file",
|
||||
"startLine": 523,
|
||||
"endLine": 540,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "write_file",
|
||||
"startLine": 542,
|
||||
"endLine": 550,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "install_homebrew_packages",
|
||||
"startLine": 552,
|
||||
"endLine": 578,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "install_openconnect_lite",
|
||||
"startLine": 580,
|
||||
"endLine": 597,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "install_cli",
|
||||
"startLine": 599,
|
||||
"endLine": 613,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "install_config",
|
||||
"startLine": 615,
|
||||
"endLine": 639,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "install_dns_cleanup",
|
||||
"startLine": 641,
|
||||
"endLine": 654,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "install_sudoers",
|
||||
"startLine": 656,
|
||||
"endLine": 683,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "install_touchid_helper",
|
||||
"startLine": 685,
|
||||
"endLine": 708,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "install_menu_bar_app",
|
||||
"startLine": 710,
|
||||
"endLine": 769,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "install_launch_agent",
|
||||
"startLine": 771,
|
||||
"endLine": 811,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "restart_running_menu_bar_app",
|
||||
"startLine": 813,
|
||||
"endLine": 831,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "install_shell_aliases",
|
||||
"startLine": 833,
|
||||
"endLine": 877,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "vpn",
|
||||
"startLine": 854,
|
||||
"endLine": 854,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "maybe_login_bitwarden",
|
||||
"startLine": 879,
|
||||
"endLine": 900,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "main",
|
||||
"startLine": 902,
|
||||
"endLine": 941,
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"importCount": 0,
|
||||
"exportCount": 0,
|
||||
"functionCount": 40,
|
||||
"classCount": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "uninstall.sh",
|
||||
"language": "shell",
|
||||
"fileCategory": "script",
|
||||
"totalLines": 320,
|
||||
"nonEmptyLines": 279,
|
||||
"functions": [
|
||||
{
|
||||
"name": "usage",
|
||||
"startLine": 62,
|
||||
"endLine": 79,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "log",
|
||||
"startLine": 102,
|
||||
"endLine": 104,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "color_line",
|
||||
"startLine": 106,
|
||||
"endLine": 110,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "log_step",
|
||||
"startLine": 112,
|
||||
"endLine": 114,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "log_info",
|
||||
"startLine": 116,
|
||||
"endLine": 118,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "log_detail",
|
||||
"startLine": 120,
|
||||
"endLine": 122,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "log_ok",
|
||||
"startLine": 124,
|
||||
"endLine": 126,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "log_warn",
|
||||
"startLine": 128,
|
||||
"endLine": 130,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "log_skip",
|
||||
"startLine": 132,
|
||||
"endLine": 134,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "die",
|
||||
"startLine": 136,
|
||||
"endLine": 139,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "run",
|
||||
"startLine": 141,
|
||||
"endLine": 151,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "find_webengine_process",
|
||||
"startLine": 153,
|
||||
"endLine": 159,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "restore_openconnect_lite_patch",
|
||||
"startLine": 161,
|
||||
"endLine": 181,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "remove_zshrc_block",
|
||||
"startLine": 183,
|
||||
"endLine": 204,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "remove_keychain_entries",
|
||||
"startLine": 206,
|
||||
"endLine": 234,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "main",
|
||||
"startLine": 236,
|
||||
"endLine": 318,
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"importCount": 0,
|
||||
"exportCount": 0,
|
||||
"functionCount": 16,
|
||||
"classCount": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
727
.understand-anything/tmp/ua-file-extract-results-2.json
Normal file
727
.understand-anything/tmp/ua-file-extract-results-2.json
Normal file
@@ -0,0 +1,727 @@
|
||||
{
|
||||
"scriptCompleted": true,
|
||||
"filesAnalyzed": 13,
|
||||
"filesSkipped": [],
|
||||
"results": [
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-operations/SKILL.md",
|
||||
"language": "markdown",
|
||||
"fileCategory": "docs",
|
||||
"totalLines": 70,
|
||||
"nonEmptyLines": 48,
|
||||
"sections": [
|
||||
{
|
||||
"heading": "Lemana VPN Operations",
|
||||
"level": 1,
|
||||
"line": 6
|
||||
},
|
||||
{
|
||||
"heading": "Overview",
|
||||
"level": 2,
|
||||
"line": 8
|
||||
},
|
||||
{
|
||||
"heading": "System Map",
|
||||
"level": 2,
|
||||
"line": 12
|
||||
},
|
||||
{
|
||||
"heading": "Live Connection Rules",
|
||||
"level": 2,
|
||||
"line": 20
|
||||
},
|
||||
{
|
||||
"heading": "Update Commands",
|
||||
"level": 2,
|
||||
"line": 38
|
||||
},
|
||||
{
|
||||
"heading": "Answer Format",
|
||||
"level": 2,
|
||||
"line": 60
|
||||
},
|
||||
{
|
||||
"heading": "Common Mistakes",
|
||||
"level": 2,
|
||||
"line": 64
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"importCount": 0,
|
||||
"exportCount": 0,
|
||||
"functionCount": 0,
|
||||
"classCount": 0,
|
||||
"sectionCount": 7
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-sso-autofill/SKILL.md",
|
||||
"language": "markdown",
|
||||
"fileCategory": "docs",
|
||||
"totalLines": 80,
|
||||
"nonEmptyLines": 54,
|
||||
"sections": [
|
||||
{
|
||||
"heading": "Lemana VPN SSO And Autofill",
|
||||
"level": 1,
|
||||
"line": 6
|
||||
},
|
||||
{
|
||||
"heading": "Overview",
|
||||
"level": 2,
|
||||
"line": 8
|
||||
},
|
||||
{
|
||||
"heading": "Credential Flow",
|
||||
"level": 2,
|
||||
"line": 12
|
||||
},
|
||||
{
|
||||
"heading": "Runtime Patches",
|
||||
"level": 2,
|
||||
"line": 29
|
||||
},
|
||||
{
|
||||
"heading": "Modes",
|
||||
"level": 2,
|
||||
"line": 47
|
||||
},
|
||||
{
|
||||
"heading": "Testing",
|
||||
"level": 2,
|
||||
"line": 56
|
||||
},
|
||||
{
|
||||
"heading": "Troubleshooting Signals",
|
||||
"level": 2,
|
||||
"line": 76
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"importCount": 0,
|
||||
"exportCount": 0,
|
||||
"functionCount": 0,
|
||||
"classCount": 0,
|
||||
"sectionCount": 7
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": ".agents/skills/lemana-vpn-testing/SKILL.md",
|
||||
"language": "markdown",
|
||||
"fileCategory": "docs",
|
||||
"totalLines": 101,
|
||||
"nonEmptyLines": 68,
|
||||
"sections": [
|
||||
{
|
||||
"heading": "Lemana VPN Testing",
|
||||
"level": 1,
|
||||
"line": 6
|
||||
},
|
||||
{
|
||||
"heading": "Overview",
|
||||
"level": 2,
|
||||
"line": 8
|
||||
},
|
||||
{
|
||||
"heading": "Non-Live Verification",
|
||||
"level": 2,
|
||||
"line": 12
|
||||
},
|
||||
{
|
||||
"heading": "Status Checks",
|
||||
"level": 2,
|
||||
"line": 28
|
||||
},
|
||||
{
|
||||
"heading": "Live VPN Checks",
|
||||
"level": 2,
|
||||
"line": 46
|
||||
},
|
||||
{
|
||||
"heading": "Installer/Uninstaller Checks",
|
||||
"level": 2,
|
||||
"line": 83
|
||||
},
|
||||
{
|
||||
"heading": "Final Report",
|
||||
"level": 2,
|
||||
"line": 94
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"importCount": 0,
|
||||
"exportCount": 0,
|
||||
"functionCount": 0,
|
||||
"classCount": 0,
|
||||
"sectionCount": 7
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "app/Package.swift",
|
||||
"language": "swift",
|
||||
"fileCategory": "code",
|
||||
"totalLines": 14,
|
||||
"nonEmptyLines": 12,
|
||||
"metrics": {}
|
||||
},
|
||||
{
|
||||
"path": "app/Sources/LemanaVPN/LemanaVPNApp.swift",
|
||||
"language": "swift",
|
||||
"fileCategory": "code",
|
||||
"totalLines": 190,
|
||||
"nonEmptyLines": 174,
|
||||
"metrics": {}
|
||||
},
|
||||
{
|
||||
"path": "app/Sources/LemanaVPN/VPNManager.swift",
|
||||
"language": "swift",
|
||||
"fileCategory": "code",
|
||||
"totalLines": 697,
|
||||
"nonEmptyLines": 618,
|
||||
"metrics": {}
|
||||
},
|
||||
{
|
||||
"path": "bin/vpn-lemanapro.sh",
|
||||
"language": "shell",
|
||||
"fileCategory": "script",
|
||||
"totalLines": 1090,
|
||||
"nonEmptyLines": 954,
|
||||
"functions": [
|
||||
{
|
||||
"name": "_emit",
|
||||
"startLine": 119,
|
||||
"endLine": 126,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_write_status",
|
||||
"startLine": 128,
|
||||
"endLine": 131,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_clear_status",
|
||||
"startLine": 133,
|
||||
"endLine": 135,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_prepare_connection_log",
|
||||
"startLine": 137,
|
||||
"endLine": 144,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_log_connection_line",
|
||||
"startLine": 146,
|
||||
"endLine": 148,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_show_connection_log_tail",
|
||||
"startLine": 150,
|
||||
"endLine": 154,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_start_connect_progress",
|
||||
"startLine": 158,
|
||||
"endLine": 166,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_stop_connect_progress",
|
||||
"startLine": 168,
|
||||
"endLine": 174,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_json_get",
|
||||
"startLine": 176,
|
||||
"endLine": 179,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_find_webengine_process",
|
||||
"startLine": 181,
|
||||
"endLine": 187,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_find_authenticator",
|
||||
"startLine": 189,
|
||||
"endLine": 195,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_module_bool",
|
||||
"startLine": 197,
|
||||
"endLine": 203,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_patches_active",
|
||||
"startLine": 205,
|
||||
"endLine": 219,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_keychain_has",
|
||||
"startLine": 221,
|
||||
"endLine": 223,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_module_status_json",
|
||||
"startLine": 225,
|
||||
"endLine": 264,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_module_human_part",
|
||||
"startLine": 266,
|
||||
"endLine": 275,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_module_status_human",
|
||||
"startLine": 277,
|
||||
"endLine": 311,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_check_status",
|
||||
"startLine": 313,
|
||||
"endLine": 375,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_patch_oc",
|
||||
"startLine": 377,
|
||||
"endLine": 604,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "autoFill",
|
||||
"startLine": 470,
|
||||
"endLine": 473,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "autoFill",
|
||||
"startLine": 488,
|
||||
"endLine": 497,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_store_keychain",
|
||||
"startLine": 606,
|
||||
"endLine": 621,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_normalize_totp_secret",
|
||||
"startLine": 623,
|
||||
"endLine": 649,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_can_prompt",
|
||||
"startLine": 651,
|
||||
"endLine": 653,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_prompt_read",
|
||||
"startLine": 655,
|
||||
"endLine": 673,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_persist_username",
|
||||
"startLine": 675,
|
||||
"endLine": 718,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_ensure_username",
|
||||
"startLine": 720,
|
||||
"endLine": 745,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_configure_keychain",
|
||||
"startLine": 747,
|
||||
"endLine": 785,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_ensure_keychain_credentials",
|
||||
"startLine": 787,
|
||||
"endLine": 814,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_bw_cache_session",
|
||||
"startLine": 816,
|
||||
"endLine": 821,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_bw_unlock",
|
||||
"startLine": 823,
|
||||
"endLine": 889,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_sync_bitwarden",
|
||||
"startLine": 891,
|
||||
"endLine": 925,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_sync_credentials",
|
||||
"startLine": 927,
|
||||
"endLine": 936,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_dns_cleanup",
|
||||
"startLine": 938,
|
||||
"endLine": 954,
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "_filter_output",
|
||||
"startLine": 956,
|
||||
"endLine": 1006,
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"importCount": 0,
|
||||
"exportCount": 0,
|
||||
"functionCount": 35,
|
||||
"classCount": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "docs/goals/manual-first-auth-flow/GOAL.md",
|
||||
"language": "markdown",
|
||||
"fileCategory": "docs",
|
||||
"totalLines": 13,
|
||||
"nonEmptyLines": 11,
|
||||
"sections": [
|
||||
{
|
||||
"heading": "Goal: Manual-First Auth Flow",
|
||||
"level": 1,
|
||||
"line": 1
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"importCount": 0,
|
||||
"exportCount": 0,
|
||||
"functionCount": 0,
|
||||
"classCount": 0,
|
||||
"sectionCount": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "docs/goals/manual-first-auth-flow/PLAN.md",
|
||||
"language": "markdown",
|
||||
"fileCategory": "docs",
|
||||
"totalLines": 440,
|
||||
"nonEmptyLines": 318,
|
||||
"sections": [
|
||||
{
|
||||
"heading": "Manual-First Auth Flow Implementation Plan",
|
||||
"level": 1,
|
||||
"line": 1
|
||||
},
|
||||
{
|
||||
"heading": "Короткий План",
|
||||
"level": 2,
|
||||
"line": 18
|
||||
},
|
||||
{
|
||||
"heading": "Architecture Slice",
|
||||
"level": 2,
|
||||
"line": 40
|
||||
},
|
||||
{
|
||||
"heading": "Пользовательский Процесс Установки",
|
||||
"level": 2,
|
||||
"line": 120
|
||||
},
|
||||
{
|
||||
"heading": "Первый Экран",
|
||||
"level": 3,
|
||||
"line": 124
|
||||
},
|
||||
{
|
||||
"heading": "Шаг 1: Что Будет Установлено",
|
||||
"level": 3,
|
||||
"line": 148
|
||||
},
|
||||
{
|
||||
"heading": "Шаг 2: Базовый Режим Входа",
|
||||
"level": 3,
|
||||
"line": 162
|
||||
},
|
||||
{
|
||||
"heading": "Шаг 3: Автоматический Вход Только Для Продвинутого Пути",
|
||||
"level": 3,
|
||||
"line": 187
|
||||
},
|
||||
{
|
||||
"heading": "Шаг 4: Bitwarden Как Дополнительная Функция",
|
||||
"level": 3,
|
||||
"line": 214
|
||||
},
|
||||
{
|
||||
"heading": "Шаг 5: Touch ID Только После Bitwarden",
|
||||
"level": 3,
|
||||
"line": 234
|
||||
},
|
||||
{
|
||||
"heading": "Шаг 6: Понятный Прогресс Установки",
|
||||
"level": 3,
|
||||
"line": 246
|
||||
},
|
||||
{
|
||||
"heading": "Финальный Экран",
|
||||
"level": 3,
|
||||
"line": 265
|
||||
},
|
||||
{
|
||||
"heading": "Ошибки И Подсказки",
|
||||
"level": 3,
|
||||
"line": 292
|
||||
},
|
||||
{
|
||||
"heading": "Acceptance Evidence Для Installer UX",
|
||||
"level": 3,
|
||||
"line": 301
|
||||
},
|
||||
{
|
||||
"heading": "Дополнительные Функции",
|
||||
"level": 2,
|
||||
"line": 308
|
||||
},
|
||||
{
|
||||
"heading": "Tasks",
|
||||
"level": 2,
|
||||
"line": 345
|
||||
},
|
||||
{
|
||||
"heading": "Task 1: Rewrite user-facing contract and docs",
|
||||
"level": 3,
|
||||
"line": 347
|
||||
},
|
||||
{
|
||||
"heading": "Task 2: Change installer defaults and capability selection",
|
||||
"level": 3,
|
||||
"line": 356
|
||||
},
|
||||
{
|
||||
"heading": "Task 3: Refactor CLI auth profiles and partial Keychain support",
|
||||
"level": 3,
|
||||
"line": 365
|
||||
},
|
||||
{
|
||||
"heading": "Task 4: Preserve automatic compatibility without keeping it dominant",
|
||||
"level": 3,
|
||||
"line": 374
|
||||
},
|
||||
{
|
||||
"heading": "Task 5: Update Swift menu-bar app surface",
|
||||
"level": 3,
|
||||
"line": 383
|
||||
},
|
||||
{
|
||||
"heading": "Task 6: Expand smoke coverage",
|
||||
"level": 3,
|
||||
"line": 392
|
||||
},
|
||||
{
|
||||
"heading": "Task 7: Final verification and local update notes",
|
||||
"level": 3,
|
||||
"line": 401
|
||||
},
|
||||
{
|
||||
"heading": "Non-Goals",
|
||||
"level": 2,
|
||||
"line": 417
|
||||
},
|
||||
{
|
||||
"heading": "Risks",
|
||||
"level": 2,
|
||||
"line": 425
|
||||
},
|
||||
{
|
||||
"heading": "Execution Order",
|
||||
"level": 2,
|
||||
"line": 432
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"importCount": 0,
|
||||
"exportCount": 0,
|
||||
"functionCount": 0,
|
||||
"classCount": 0,
|
||||
"sectionCount": 26
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "docs/superpowers/plans/2026-05-24-keychain-totp-provider.md",
|
||||
"language": "markdown",
|
||||
"fileCategory": "docs",
|
||||
"totalLines": 935,
|
||||
"nonEmptyLines": 656,
|
||||
"sections": [
|
||||
{
|
||||
"heading": "Keychain TOTP Provider Implementation Plan",
|
||||
"level": 1,
|
||||
"line": 1
|
||||
},
|
||||
{
|
||||
"heading": "File Structure",
|
||||
"level": 2,
|
||||
"line": 13
|
||||
},
|
||||
{
|
||||
"heading": "Task 1: Add Smoke Tests For Credential Source Separation",
|
||||
"level": 3,
|
||||
"line": 39
|
||||
},
|
||||
{
|
||||
"heading": "Task 2: Add Runtime Credential Source And TOTP Normalization",
|
||||
"level": 3,
|
||||
"line": 135
|
||||
},
|
||||
{
|
||||
"heading": "Task 3: Split Bitwarden Sync From Keychain Source Flow",
|
||||
"level": 3,
|
||||
"line": 325
|
||||
},
|
||||
{
|
||||
"heading": "Task 4: Update Installer Flags And Config",
|
||||
"level": 3,
|
||||
"line": 457
|
||||
},
|
||||
{
|
||||
"heading": "Task 5: Update Swift Menu-Bar Status Decoding",
|
||||
"level": 3,
|
||||
"line": 639
|
||||
},
|
||||
{
|
||||
"heading": "Task 6: Update Documentation",
|
||||
"level": 3,
|
||||
"line": 736
|
||||
},
|
||||
{
|
||||
"heading": "Task 7: Full Verification",
|
||||
"level": 3,
|
||||
"line": 799
|
||||
},
|
||||
{
|
||||
"heading": "Self-Review",
|
||||
"level": 2,
|
||||
"line": 931
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"importCount": 0,
|
||||
"exportCount": 0,
|
||||
"functionCount": 0,
|
||||
"classCount": 0,
|
||||
"sectionCount": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "libexec/lemana-vpn-dns-cleanup",
|
||||
"language": "unknown",
|
||||
"fileCategory": "code",
|
||||
"totalLines": 23,
|
||||
"nonEmptyLines": 17,
|
||||
"metrics": {}
|
||||
},
|
||||
{
|
||||
"path": "templates/openconnect-lite-config.toml",
|
||||
"language": "toml",
|
||||
"fileCategory": "config",
|
||||
"totalLines": 32,
|
||||
"nonEmptyLines": 23,
|
||||
"sections": [
|
||||
{
|
||||
"heading": "default_profile",
|
||||
"level": 1,
|
||||
"line": 3
|
||||
},
|
||||
{
|
||||
"heading": "credentials",
|
||||
"level": 1,
|
||||
"line": 8
|
||||
},
|
||||
{
|
||||
"heading": "auto_fill_rules",
|
||||
"level": 1,
|
||||
"line": 11
|
||||
},
|
||||
{
|
||||
"heading": "[[auto_fill_rules.\"https://employee.auth.lemanapro.ru/*\"]]",
|
||||
"level": 5,
|
||||
"line": 13
|
||||
},
|
||||
{
|
||||
"heading": "[[auto_fill_rules.\"https://employee.auth.lemanapro.ru/*\"]]",
|
||||
"level": 5,
|
||||
"line": 17
|
||||
},
|
||||
{
|
||||
"heading": "[[auto_fill_rules.\"https://employee.auth.lemanapro.ru/*\"]]",
|
||||
"level": 5,
|
||||
"line": 21
|
||||
},
|
||||
{
|
||||
"heading": "[[auto_fill_rules.\"https://employee.auth.lemanapro.ru/*\"]]",
|
||||
"level": 5,
|
||||
"line": 25
|
||||
},
|
||||
{
|
||||
"heading": "[[auto_fill_rules.\"https://employee.auth.lemanapro.ru/*\"]]",
|
||||
"level": 5,
|
||||
"line": 29
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"importCount": 0,
|
||||
"exportCount": 0,
|
||||
"functionCount": 0,
|
||||
"classCount": 0,
|
||||
"sectionCount": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "tests/smoke.sh",
|
||||
"language": "shell",
|
||||
"fileCategory": "script",
|
||||
"totalLines": 438,
|
||||
"nonEmptyLines": 392,
|
||||
"functions": [
|
||||
{
|
||||
"name": "autoFill",
|
||||
"startLine": 113,
|
||||
"endLine": 116,
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"importCount": 0,
|
||||
"exportCount": 0,
|
||||
"functionCount": 1,
|
||||
"classCount": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
13
docs/goals/manual-first-auth-flow/GOAL.md
Normal file
13
docs/goals/manual-first-auth-flow/GOAL.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Goal: Manual-First Auth Flow
|
||||
|
||||
Use Krypton Execution to execute `docs/goals/manual-first-auth-flow/PLAN.md`.
|
||||
|
||||
Core rules:
|
||||
- Treat PLAN.md as the source plan.
|
||||
- Preserve intent, ownership, contract, cutover, evidence, and kill criteria.
|
||||
- Make fresh install and default `vpn` / app connect manual-first.
|
||||
- Keep the installer understandable for a non-IT user: plain wording, manual mode on Enter, optional conveniences explained as choices.
|
||||
- Keep Bitwarden, Touch ID, saved TOTP, autosubmit, and headless mode as explicit opt-in features.
|
||||
- Do not add a new dominant path without deleting, redirecting, demoting, or shimming the displaced Bitwarden/auto-first path.
|
||||
- Capture acceptance evidence from a clean temporary HOME and the menu-bar app contract.
|
||||
- Say "implemented but unproven" if that evidence cannot be captured.
|
||||
440
docs/goals/manual-first-auth-flow/PLAN.md
Normal file
440
docs/goals/manual-first-auth-flow/PLAN.md
Normal file
@@ -0,0 +1,440 @@
|
||||
# Manual-First Auth Flow Implementation Plan
|
||||
|
||||
**Intent:** Перевернуть Lemana VPN из auto/Bitwarden-first в manual-first: новая установка и действие по умолчанию открывают видимый SSO-браузер без сохранённых секретов, а удобства включаются отдельными функциями.
|
||||
**Current Behavior:** Fresh install по умолчанию выбирает `credential_source=bitwarden`, ставит Bitwarden/Touch ID, `vpn` запускает hidden browser с autofill+submit, а `manual-full` существует как отдельный диагностический режим.
|
||||
**Expected Outcome:** Базовый путь не требует Bitwarden, Keychain, TOTP seed или автоотправки. Пользователь может поэтапно включить сохранение LDAP login/password в macOS Keychain, затем TOTP seed/autofill, затем Bitwarden sync, Touch ID, autosubmit/headless.
|
||||
**Target-Perspective Output:** Новый пользователь запускает installer, ничего специально не выбирает, нажимает `vpn` или основную кнопку в `LemanaVPN.app`, видит Keycloak форму и вводит LDAP, пароль и одноразовый код руками. Если он включил сохранение LDAP в Keychain, username/password подставляются, а одноразовый код он вводит руками.
|
||||
**Truth Owner:** `bin/vpn-lemanapro.sh` владеет runtime-решением режима; `~/.config/lemana-vpn/env` хранит выбранные capabilities; macOS Keychain хранит только секреты; Swift app только вызывает CLI и читает JSON.
|
||||
**Contract Boundary:** Installer пишет env/config, CLI читает env/flags/Keychain и отдаёт JSON Lines/status JSON, Swift app потребляет только этот CLI-контракт. `openconnect-lite` остаётся SSO/VPN engine.
|
||||
**Cutover:** Fresh installs become manual-first. Existing configs with `LEMANA_VPN_CREDENTIAL_SOURCE=bitwarden` keep working, but docs/UI stop presenting Bitwarden as default. Legacy aliases `vpn-auto`, `vpn-manual`, `vpn-manual-full` stay as compatibility shims.
|
||||
**Displaced Path:** The displaced dominant path is `Bitwarden + Touch ID + hidden autofill + autosubmit by default`. It becomes an explicit opt-in feature set, not the default route.
|
||||
**Value Density:** First slice removes the biggest onboarding failure: users without Bitwarden can connect manually immediately, then add only the conveniences they need.
|
||||
**Acceptance Evidence:** In a clean temporary HOME, `sh install.sh --dry-run --non-interactive` reports manual/default auth with Bitwarden and Touch ID off; the interactive installer copy reads like a step-by-step setup for a non-IT user; `bash bin/vpn-lemanapro.sh --json` without credentials reaches a visible browser launch without `username_required` or `keychain_required`; a Keychain-login-only fixture fills username/password but leaves OTP manual and submit disabled; the Swift app primary action maps to the manual-first path.
|
||||
**Evidence Lane:** Non-live smoke ladder first: `sh -n`, `bash -n`, `tests/smoke.sh`, `swift build`, `git diff --check`. Live VPN is optional after status check and should start with visible manual mode only.
|
||||
**Kill Criteria:** No fresh-install default may select Bitwarden, require a TOTP seed, autosubmit, or hide the browser. No docs may state Bitwarden is enabled by default. No default CLI/app path may require saved credentials before opening manual SSO.
|
||||
**Architecture Slice:** Installer defaults + CLI auth profile/capability model + Swift launch mode labels + README/smoke coverage.
|
||||
**Plan Review Gate:** Requires PRE review before execution.
|
||||
|
||||
## Короткий План
|
||||
|
||||
🎯 Цель: fresh install, `vpn` и основная кнопка приложения стартуют с полностью ручного SSO; сохранение LDAP, TOTP, Bitwarden, autosubmit и headless включаются отдельно.
|
||||
|
||||
📦 Репозитории: только `/Users/dokril/git/home/lemana-vpn`.
|
||||
|
||||
🧩 Модули: `install.sh`, `bin/vpn-lemanapro.sh`, `app/Sources/LemanaVPN`, `tests/smoke.sh`, `README.md`; `templates/openconnect-lite-config.toml` только если login-only autofill потребует правки правил.
|
||||
|
||||
🛠 Основные изменения:
|
||||
- Переключить fresh defaults с Bitwarden/auto-first на manual-first.
|
||||
- Ввести явный auth profile/capability contract: manual, saved-login, saved-totp, auto.
|
||||
- Разрешить Keychain хранить только LDAP username/password без обязательного TOTP seed.
|
||||
- Оставить Bitwarden, Touch ID, autosubmit/headless как opt-in функции.
|
||||
- Сделать Swift app CLI-wrapper поверх тех же режимов, с manual-first primary action.
|
||||
- Зафиксировать всё в smoke-тестах и README.
|
||||
|
||||
✅ Проверка: `sh -n install.sh`, `sh -n uninstall.sh`, `bash -n bin/vpn-lemanapro.sh`, `sh -n tests/smoke.sh`, `tests/smoke.sh`, `swift build -c release --package-path app`, `git diff --check`.
|
||||
|
||||
⚠️ Риски: старые пользователи могут ожидать auto-connect на `vpn`; partial Keychain без TOTP надо проверить fake launch тестом и, при необходимости, видимым manual smoke.
|
||||
|
||||
➡️ Следующий шаг: начать с `install.sh` и зафиксировать новый env/flag contract для manual-first defaults.
|
||||
|
||||
## Architecture Slice
|
||||
|
||||
Files to create:
|
||||
|
||||
- None required for runtime. This goal package is the planning artifact.
|
||||
|
||||
Files to modify:
|
||||
|
||||
- `install.sh` - defaults, wizard wording, flags, env rendering, dry-run output.
|
||||
- `bin/vpn-lemanapro.sh` - auth profile/capability model, default mode, partial Keychain support, JSON events.
|
||||
- `app/Sources/LemanaVPN/VPNManager.swift` - launch mode enum/default, JSON handling if new events are added.
|
||||
- `app/Sources/LemanaVPN/LemanaVPNApp.swift` - menu labels/order so manual-first is primary and auto is secondary.
|
||||
- `tests/smoke.sh` - regression coverage for fresh defaults, manual launch without credentials, partial Keychain, app wiring.
|
||||
- `README.md` - user-facing feature matrix and migration/update instructions.
|
||||
- `templates/openconnect-lite-config.toml` only if login-only filling needs rule adjustments; otherwise avoid.
|
||||
|
||||
Files to avoid:
|
||||
|
||||
- `libexec/lemana-vpn-dns-cleanup` unless DNS behavior breaks.
|
||||
- `uninstall.sh` except if new Keychain account names require cleanup.
|
||||
- Direct edits inside installed `openconnect-lite`; use the existing runtime patch path from `bin/vpn-lemanapro.sh`.
|
||||
|
||||
Source of truth:
|
||||
|
||||
- Repo source for behavior.
|
||||
- Installed env file for user-selected capabilities.
|
||||
- Keychain for LDAP password and optional TOTP seed.
|
||||
|
||||
Read path:
|
||||
|
||||
- `install.sh` writes `~/.config/lemana-vpn/env`.
|
||||
- `bin/vpn-lemanapro.sh` sources env, applies CLI flag overrides, checks Keychain capability presence, then launches `openconnect-lite`.
|
||||
- `LemanaVPN.app` calls `~/bin/vpn-lemanapro.sh --json <mode>`.
|
||||
|
||||
Write path:
|
||||
|
||||
- `vpn --configure-login` or equivalent writes username/password only.
|
||||
- `vpn --configure-keychain` remains the full saved-credentials path and writes password plus permanent TOTP seed.
|
||||
- Bitwarden sync, when enabled, writes password plus optional TOTP seed into Keychain.
|
||||
|
||||
Contract boundary:
|
||||
|
||||
- CLI flags and JSON events are the public contract for the app and tests.
|
||||
- Suggested new runtime contract:
|
||||
- `auth_profile=manual`: visible browser, no preflight, no autofill, no submit.
|
||||
- `auth_profile=saved-login`: visible browser, username/password autofill, OTP manual, no submit.
|
||||
- `auth_profile=saved-totp`: visible browser, username/password/TOTP autofill, no submit.
|
||||
- `auth_profile=auto`: hidden or visible automatic mode, full autofill and submit; explicit only.
|
||||
- `credential_source=none|keychain|bitwarden`: where saved values come from, separate from whether submit/headless are enabled.
|
||||
|
||||
Integration points:
|
||||
|
||||
- macOS Keychain service `openconnect-lite`.
|
||||
- Optional Bitwarden CLI item `LM LDAP`.
|
||||
- `openconnect-lite` config autofill rules.
|
||||
- Swift app menu and reconnect behavior.
|
||||
- Existing aliases from shell install block.
|
||||
|
||||
Migration/cutover:
|
||||
|
||||
- Fresh installs write `credential_source=none` and `auth_profile=manual`.
|
||||
- Existing `credential_source=bitwarden` installs continue as saved-totp/auto-capable, but `vpn` default should still prefer manual-visible unless the user explicitly chooses auto.
|
||||
- `vpn-auto` remains the explicit old behavior.
|
||||
- `--manual-full` remains compatibility alias for the new default manual profile.
|
||||
|
||||
Displaced path:
|
||||
|
||||
- Installer Enter=Bitwarden.
|
||||
- CLI `CONNECT_MODE=auto`.
|
||||
- Full password+TOTP seed as mandatory preflight for non-`manual-full`.
|
||||
- App primary/default connect action pointing at `.auto`.
|
||||
|
||||
Acceptance evidence gate:
|
||||
|
||||
- A clean temp HOME proves fresh defaults.
|
||||
- Captured interactive installer output proves that the default path is understandable without knowing Bitwarden, Keychain, TOTP seed, or `credential_source`.
|
||||
- Fake `openconnect-lite` launch capture proves display/autofill/click env for each profile.
|
||||
- App source/smoke proves primary UI wiring.
|
||||
- Installed-bundle verification is required only when refreshing the local app after implementation.
|
||||
|
||||
## Пользовательский Процесс Установки
|
||||
|
||||
Цель этого сценария: человек, далёкий от IT, должен понимать, что сейчас произойдёт, что от него требуется, и что можно пропустить. Installer не должен начинать с технического выбора credential source.
|
||||
|
||||
### Первый Экран
|
||||
|
||||
Текст должен быть коротким:
|
||||
|
||||
```text
|
||||
Lemana VPN установит приложение для подключения к корпоративному VPN.
|
||||
|
||||
По умолчанию ничего не сохраняется: при подключении откроется окно входа,
|
||||
а вы введёте логин, пароль и одноразовый код сами.
|
||||
|
||||
Дополнительные удобства можно включить сейчас или позже.
|
||||
```
|
||||
|
||||
Основное действие по Enter: продолжить с ручным безопасным режимом.
|
||||
|
||||
Запрещено на первом экране:
|
||||
|
||||
- `credential_source`
|
||||
- `auth_profile`
|
||||
- `TOTP seed`
|
||||
- `openconnect-lite`
|
||||
- `Bitwarden item`
|
||||
- `headless`
|
||||
|
||||
### Шаг 1: Что Будет Установлено
|
||||
|
||||
Installer показывает не список пакетов, а понятные пункты:
|
||||
|
||||
```text
|
||||
Будет установлено:
|
||||
- приложение Lemana VPN в меню macOS;
|
||||
- команда vpn для подключения из Terminal;
|
||||
- VPN-компонент, который открывает корпоративную страницу входа;
|
||||
- безопасная очистка DNS после отключения.
|
||||
```
|
||||
|
||||
Технические зависимости (`openconnect`, `pipx`, `openconnect-lite`) можно показывать только в подробном логе ниже или в debug/dry-run.
|
||||
|
||||
### Шаг 2: Базовый Режим Входа
|
||||
|
||||
Вопрос:
|
||||
|
||||
```text
|
||||
Как подключаться по умолчанию?
|
||||
|
||||
1) Вводить всё вручную (рекомендуется)
|
||||
Откроется окно входа. Вы сами введёте логин, пароль и одноразовый код.
|
||||
|
||||
2) Запомнить логин и пароль на этом Mac
|
||||
При входе логин и пароль подставятся сами. Одноразовый код вы введёте вручную.
|
||||
|
||||
3) Настроить автоматический вход
|
||||
Нужно заранее сохранить пароль и постоянный код настройки 2FA.
|
||||
|
||||
Выбор [1/2/3, Enter=1]:
|
||||
```
|
||||
|
||||
Поведение:
|
||||
|
||||
- `1` ставит `auth_profile=manual`, `credential_source=none`.
|
||||
- `2` ставит `auth_profile=saved-login`, `credential_source=keychain`, после установки запускает настройку LDAP username/password.
|
||||
- `3` открывает следующий уточняющий шаг и не должен быть default.
|
||||
|
||||
### Шаг 3: Автоматический Вход Только Для Продвинутого Пути
|
||||
|
||||
Если выбран пункт 3, installer объясняет риск простым языком:
|
||||
|
||||
```text
|
||||
Автоматический вход работает только если на Mac сохранены:
|
||||
- корпоративный пароль;
|
||||
- постоянный код настройки 2FA.
|
||||
|
||||
Это не текущие 6 цифр из приложения-аутентификатора.
|
||||
Если вы не знаете, что это такое, выберите ручной режим.
|
||||
```
|
||||
|
||||
Варианты:
|
||||
|
||||
```text
|
||||
1) Вернуться к ручному режиму
|
||||
2) Сохранить пароль и постоянный код 2FA в macOS Keychain
|
||||
3) Взять пароль и код 2FA из Bitwarden
|
||||
```
|
||||
|
||||
Поведение:
|
||||
|
||||
- `1` возвращает к `auth_profile=manual`.
|
||||
- `2` ставит `auth_profile=saved-totp`, `credential_source=keychain`, запускает full Keychain setup.
|
||||
- `3` ставит `credential_source=bitwarden`; Touch ID спрашивается отдельным простым вопросом.
|
||||
|
||||
### Шаг 4: Bitwarden Как Дополнительная Функция
|
||||
|
||||
Bitwarden не должен выглядеть обязательным. Вопрос появляется только после явного выбора Bitwarden:
|
||||
|
||||
```text
|
||||
Использовать Bitwarden для хранения VPN-пароля?
|
||||
|
||||
Подойдёт, если у вас уже есть запись с корпоративным VPN-паролем
|
||||
и постоянным кодом настройки 2FA.
|
||||
```
|
||||
|
||||
Если Bitwarden CLI не найден:
|
||||
|
||||
```text
|
||||
Bitwarden не найден на этом Mac.
|
||||
Можно установить его сейчас или вернуться к ручному режиму.
|
||||
```
|
||||
|
||||
Default: вернуться к ручному режиму, если пользователь не уверен.
|
||||
|
||||
### Шаг 5: Touch ID Только После Bitwarden
|
||||
|
||||
Вопрос:
|
||||
|
||||
```text
|
||||
Разрешить Touch ID для открытия Bitwarden?
|
||||
|
||||
Это не VPN-пароль. Touch ID только помогает достать данные из Bitwarden.
|
||||
```
|
||||
|
||||
Default: нет, если пользователь не выбирал Bitwarden; да/нет можно спрашивать только в Bitwarden path.
|
||||
|
||||
### Шаг 6: Понятный Прогресс Установки
|
||||
|
||||
Progress log должен говорить человеческим языком:
|
||||
|
||||
```text
|
||||
Проверяю компоненты для VPN...
|
||||
Устанавливаю приложение Lemana VPN...
|
||||
Добавляю команду vpn...
|
||||
Настраиваю безопасное отключение VPN...
|
||||
Готово.
|
||||
```
|
||||
|
||||
Если macOS просит пароль администратора, installer заранее объясняет:
|
||||
|
||||
```text
|
||||
macOS может запросить пароль от компьютера.
|
||||
Он нужен, чтобы разрешить VPN подключаться и корректно очищать DNS после отключения.
|
||||
```
|
||||
|
||||
### Финальный Экран
|
||||
|
||||
Для ручного режима:
|
||||
|
||||
```text
|
||||
Готово. Как подключиться:
|
||||
1. Откройте Lemana VPN из Applications или меню macOS.
|
||||
2. Нажмите "Подключить".
|
||||
3. В открывшемся окне введите корпоративный логин, пароль и одноразовый код.
|
||||
|
||||
Проверить состояние: vpn --status
|
||||
```
|
||||
|
||||
Для режима с сохранённым логином и паролем:
|
||||
|
||||
```text
|
||||
Готово. При подключении логин и пароль будут подставлены автоматически.
|
||||
Одноразовый код нужно ввести вручную.
|
||||
```
|
||||
|
||||
Для автоматического режима:
|
||||
|
||||
```text
|
||||
Готово. Автоматический вход включён.
|
||||
Если он не сработает, используйте ручной режим: vpn --manual-full
|
||||
```
|
||||
|
||||
### Ошибки И Подсказки
|
||||
|
||||
Ошибки должны говорить, что делать дальше:
|
||||
|
||||
- Нет сохранённого пароля: `Запустите vpn --configure-login или подключитесь вручную.`
|
||||
- Нет постоянного кода 2FA для auto mode: `Автоматический вход не настроен. Выберите ручной режим или добавьте постоянный код настройки 2FA.`
|
||||
- Bitwarden заблокирован в приложении без Terminal: `Откройте Terminal и выполните vpn, либо подключитесь вручную.`
|
||||
- Пользователь выбрал непонятный вариант: `Введите 1, 2 или 3. Если не уверены, нажмите Enter.`
|
||||
|
||||
### Acceptance Evidence Для Installer UX
|
||||
|
||||
- Interactive dry-run через `expect` должен захватить первый экран, выбор режима и финальную подсказку.
|
||||
- Smoke должен проверять, что Enter выбирает ручной режим.
|
||||
- README должен показывать тот же сценарий, что и installer.
|
||||
- В обычном wizard не должно быть слов `credential_source`, `auth_profile`, `TOTP seed`; они допустимы только в help/debug/docs for advanced.
|
||||
|
||||
## Дополнительные Функции
|
||||
|
||||
1. **Core manual SSO (default)**
|
||||
- Что делает: открывает видимый браузер Keycloak, не читает Bitwarden/Keychain, не заполняет поля, не нажимает submit.
|
||||
- Когда включается: всегда на fresh install и при `vpn` без дополнительных настроек.
|
||||
- Когда не включается: если пользователь явно запускает `vpn-auto` или выбрал auto profile.
|
||||
|
||||
2. **Saved LDAP login/password in macOS Keychain**
|
||||
- Что делает: сохраняет LDAP username/password в Keychain и подставляет их в видимую форму.
|
||||
- Когда включается: пользователь явно запускает настройку сохранения LDAP или выбирает этот пункт в installer.
|
||||
- OTP: текущий одноразовый код вводится руками; TOTP seed не требуется.
|
||||
|
||||
3. **Saved TOTP seed**
|
||||
- Что делает: сохраняет постоянный TOTP seed и позволяет `openconnect-lite` генерировать текущий код для autofill.
|
||||
- Когда включается: пользователь явно добавил seed через full Keychain setup или Bitwarden sync.
|
||||
- Ограничение: текущий 6-значный код нельзя сохранять как настройку.
|
||||
|
||||
4. **Autofill**
|
||||
- Что делает: заполняет те поля, для которых есть capability: username/password, а затем optional TOTP.
|
||||
- Когда включается: при `saved-login` или `saved-totp`.
|
||||
- Submit: по умолчанию выключен; пользователь нажимает вход сам.
|
||||
|
||||
5. **Autosubmit/headless**
|
||||
- Что делает: запускает старый удобный путь с hidden browser, полным autofill и нажатием submit.
|
||||
- Когда включается: только явным `vpn-auto`, `--auto`, profile `auto` или отдельным opt-in флагом.
|
||||
- Предусловие: saved login/password и TOTP seed должны быть доступны; без TOTP seed этот режим должен fail fast с понятной ошибкой.
|
||||
|
||||
6. **Bitwarden sync**
|
||||
- Что делает: читает LDAP password и TOTP seed из Bitwarden item, нормализует TOTP seed и перекладывает в Keychain.
|
||||
- Когда включается: только если пользователь выбрал Bitwarden provider.
|
||||
- Fallback: если `bw` отсутствует или locked в GUI/no-tty, использовать уже сохранённый Keychain либо manual path, не блокировать default.
|
||||
|
||||
7. **Touch ID helper**
|
||||
- Что делает: локально защищает чтение Bitwarden master password.
|
||||
- Когда включается: только вместе с Bitwarden provider и явным согласием пользователя.
|
||||
- Не делает: не заменяет LDAP password и не является хранением VPN credentials.
|
||||
|
||||
## Tasks
|
||||
|
||||
### Task 1: Rewrite user-facing contract and docs
|
||||
|
||||
- Scope: `README.md`.
|
||||
- Change: describe manual-first default, feature matrix, migration from old Bitwarden-first install, and the non-IT installation scenario from this plan.
|
||||
- Expected output: docs no longer say Bitwarden/auto are default; examples show `vpn` as manual and `vpn-auto` as opt-in; installation instructions use the same wording as the installer.
|
||||
- Verification: `rg -n "по умолчанию|Bitwarden|vpn-auto|manual-full|одноразовый код|ручной режим" README.md`.
|
||||
- Acceptance evidence: a new user can read the first install section and understand what will be installed, what they will type during first connect, and that Bitwarden is optional.
|
||||
- Parallel: yes, but must be reconciled with installer flag names from Task 2.
|
||||
|
||||
### Task 2: Change installer defaults and capability selection
|
||||
|
||||
- Scope: `install.sh`.
|
||||
- Change: default credential source becomes none/manual; Bitwarden and Touch ID default off; wizard Enter keeps manual; add or reuse flags for saved LDAP, full Keychain, Bitwarden provider, and auto profile; rewrite interactive copy using the "Пользовательский Процесс Установки" section.
|
||||
- Expected output: dry-run shows manual/default auth and no Bitwarden/Touch ID install unless selected; interactive prompts say "вводить всё вручную", "запомнить логин и пароль", and "автоматический вход" instead of exposing low-level config names.
|
||||
- Verification: `sh -n install.sh`; `sh install.sh --dry-run --non-interactive`; interactive dry-run through `expect` if available.
|
||||
- Acceptance evidence: clean install dry-run does not try to install Bitwarden and does not schedule Keychain credential prompts by default; captured interactive output is understandable without IT vocabulary and Enter chooses manual mode.
|
||||
- Parallel: no, because it defines env contract consumed by CLI/tests.
|
||||
|
||||
### Task 3: Refactor CLI auth profiles and partial Keychain support
|
||||
|
||||
- Scope: `bin/vpn-lemanapro.sh`.
|
||||
- Change: replace auto-first/default credential preflight with an auth-profile resolver; add partial Keychain support for saved login without TOTP; keep full Keychain and Bitwarden sync as opt-ins.
|
||||
- Expected output: no-credential default reaches visible manual browser; saved-login fills username/password and leaves OTP manual; saved-totp can fill OTP but still does not submit by default.
|
||||
- Verification: `bash -n bin/vpn-lemanapro.sh`; fake `openconnect-lite` capture in `tests/smoke.sh`.
|
||||
- Acceptance evidence: JSON events clearly state active auth profile and missing optional capabilities without treating them as fatal in manual profiles.
|
||||
- Parallel: no, central truth owner.
|
||||
|
||||
### Task 4: Preserve automatic compatibility without keeping it dominant
|
||||
|
||||
- Scope: `bin/vpn-lemanapro.sh`, `install.sh`, shell aliases.
|
||||
- Change: keep `vpn-auto`/`--auto` as explicit old behavior; guard autosubmit/headless behind full saved credentials; keep `--manual-full` compatibility alias for the default manual profile.
|
||||
- Expected output: current power users can still run the old path intentionally.
|
||||
- Verification: `tests/smoke.sh` assertions for `vpn-auto`, `--auto`, `--manual-full`, launch env.
|
||||
- Acceptance evidence: default path and auto path are both test-covered and visibly separate.
|
||||
- Parallel: can run after Task 3.
|
||||
|
||||
### Task 5: Update Swift menu-bar app surface
|
||||
|
||||
- Scope: `app/Sources/LemanaVPN/VPNManager.swift`, `app/Sources/LemanaVPN/LemanaVPNApp.swift`.
|
||||
- Change: make the primary menu action manual-first; demote automatic connect to an explicit secondary action; keep app as a CLI wrapper; surface missing saved credentials as setup guidance, not hidden prompts.
|
||||
- Expected output: app default action does not trigger Bitwarden or autosubmit unless the user chooses an explicit auto/saved mode.
|
||||
- Verification: `swift build -c release --package-path app`; smoke greps for the new labels/mode wiring.
|
||||
- Acceptance evidence: app source and built binary contain the manual-first label and map it to the manual profile.
|
||||
- Parallel: yes after CLI mode names are stable.
|
||||
|
||||
### Task 6: Expand smoke coverage
|
||||
|
||||
- Scope: `tests/smoke.sh`.
|
||||
- Change: update old assertions and add cases for fresh manual default, saved-login-only, saved-totp, Bitwarden opt-in, and app wiring.
|
||||
- Expected output: smoke becomes the executable spec for the feature matrix and installer UX.
|
||||
- Verification: `sh -n tests/smoke.sh`; `tests/smoke.sh`.
|
||||
- Acceptance evidence: smoke fails if Bitwarden or auto become fresh defaults again, or if the basic wizard no longer defaults to manual mode with non-technical wording.
|
||||
- Parallel: yes after Task 2 and Task 3 contracts are known.
|
||||
|
||||
### Task 7: Final verification and local update notes
|
||||
|
||||
- Scope: whole repo; installed app only if doing a local refresh.
|
||||
- Change: run non-live ladder, then optionally install/update local checkout with `sh install.sh` after user approval for real local changes.
|
||||
- Expected output: plan implemented with evidence and a clear "live VPN not run" or live manual evidence.
|
||||
- Verification:
|
||||
- `sh -n install.sh`
|
||||
- `sh -n uninstall.sh`
|
||||
- `bash -n bin/vpn-lemanapro.sh`
|
||||
- `sh -n tests/smoke.sh`
|
||||
- `tests/smoke.sh`
|
||||
- `swift build -c release --package-path app`
|
||||
- `git diff --check`
|
||||
- Acceptance evidence: final answer includes how to run, how to check, how to update installed setup, and what live checks were intentionally skipped.
|
||||
- Parallel: no, final integration gate.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Do not replace `openconnect-lite` or implement a separate VPN client in Swift.
|
||||
- Do not store current 6-digit OTP codes.
|
||||
- Do not make the GUI collect secrets directly.
|
||||
- Do not remove existing automatic mode; demote it to explicit opt-in.
|
||||
- Do not change DNS cleanup behavior as part of this refactor.
|
||||
|
||||
## Risks
|
||||
|
||||
- Existing users may expect `vpn` to auto-connect. Mitigation: keep `vpn-auto` and document the cutover clearly.
|
||||
- Partial Keychain support may need careful `openconnect-lite` behavior verification when TOTP is absent. Mitigation: fake launch tests first, then one visible manual smoke if needed.
|
||||
- Swift app could lag behind installed source again. Mitigation: after implementation refresh installed bundle via `sh install.sh` and verify with app source/build or `strings` if doing local install.
|
||||
- Credential language can confuse Bitwarden master password, LDAP password, TOTP seed, and current OTP. Mitigation: keep labels explicit in CLI and README.
|
||||
|
||||
## Execution Order
|
||||
|
||||
1. Task 2 defines the installer/env contract.
|
||||
2. Task 3 implements the CLI truth owner.
|
||||
3. Task 4 preserves explicit auto compatibility.
|
||||
4. Task 5 updates the app wrapper.
|
||||
5. Task 6 turns the matrix into smoke coverage.
|
||||
6. Task 1 updates docs with final flag/profile names.
|
||||
7. Task 7 verifies and prepares local update instructions.
|
||||
@@ -81,6 +81,7 @@ grep -q 'vpn-manual-full' "$ROOT/install.sh"
|
||||
grep -q 'connect(mode: .auto)' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift"
|
||||
grep -q 'connect(mode: .manual)' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift"
|
||||
grep -q 'connect(mode: .manualFull)' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift"
|
||||
grep -q 'Подключить полностью вручную' "$ROOT/app/Sources/LemanaVPN/LemanaVPNApp.swift"
|
||||
grep -q 'enum VPNLaunchMode' "$ROOT/app/Sources/LemanaVPN/VPNManager.swift"
|
||||
grep -q 'struct Credentials: Decodable' "$ROOT/app/Sources/LemanaVPN/VPNManager.swift"
|
||||
grep -q 'credential_source' "$ROOT/app/Sources/LemanaVPN/VPNManager.swift"
|
||||
|
||||
Reference in New Issue
Block a user