Check manual-full UI text in smoke test

This commit is contained in:
2026-06-23 09:32:39 +03:00
parent 747ed623ab
commit 042c4d6550
19 changed files with 5821 additions and 0 deletions

View 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

View File

@@ -0,0 +1,3 @@
{
"outputLanguage": "ru"
}

View File

@@ -0,0 +1,11 @@
{
"issues": [],
"warnings": [],
"stats": {
"nodes": 73,
"edges": 56,
"duplicates": 0,
"dangling": 0,
"missingFiles": 0
}
}

File diff suppressed because it is too large Load Diff

View 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
}
]
}

View 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
}
]
}

View 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": {}
}
]
}

View 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"
}
]
}

View 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": []
}
}

View 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
}
}
}

View 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": []
}
}

View 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));
}

View 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": []
}
}

View 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": []
}
}

View 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
}
}
]
}

View 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
}
}
]
}