Files

943 lines
28 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"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
}
]
}