sneakoscope 8.0.2 → 8.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +150 -37
- package/config/skills-hash-ledger.v1.json +1 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/bin/fast-inline.js +55 -27
- package/dist/bin/install.js +6 -32
- package/dist/bin/sks-dispatch.js +1 -1
- package/dist/cli/args.js +0 -2
- package/dist/cli/cli-theme.js +18 -9
- package/dist/cli/command-manifest-lite.js +6 -6
- package/dist/cli/command-registry.js +6 -6
- package/dist/cli/feature-commands.js +9 -7
- package/dist/cli/install-helpers-codex-lb-chain.js +198 -7
- package/dist/cli/install-helpers-codex-lb-config.js +78 -10
- package/dist/cli/install-helpers-install-support.js +9 -6
- package/dist/cli/install-helpers.js +101 -49
- package/dist/cli/install-tool-helpers.js +2 -82
- package/dist/cli/output.js +6 -1
- package/dist/cli/router.js +101 -2
- package/dist/commands/codex-lb.js +119 -19
- package/dist/commands/doctor-profile.js +1 -1
- package/dist/commands/doctor.js +727 -144
- package/dist/commands/mad-sks.js +1 -5
- package/dist/config/skills-manifest.json +4 -2
- package/dist/core/agents/agent-codex-cockpit.js +0 -3
- package/dist/core/agents/agent-effort-policy.js +5 -5
- package/dist/core/agents/agent-janitor.js +0 -20
- package/dist/core/agents/agent-orchestrator.js +9 -56
- package/dist/core/agents/agent-output-validator.js +2 -3
- package/dist/core/agents/agent-patch-schema.js +2 -2
- package/dist/core/agents/agent-proof-evidence.js +0 -65
- package/dist/core/agents/agent-recursion-guard.js +1 -1
- package/dist/core/agents/agent-runner-codex-exec.js +5 -3
- package/dist/core/agents/agent-scheduler.js +327 -85
- package/dist/core/agents/agent-schema.js +1 -1
- package/dist/core/agents/agent-trust-report.js +0 -36
- package/dist/core/agents/agent-worker-pipeline.js +0 -1
- package/dist/core/agents/fast-mode-policy.js +1 -3
- package/dist/core/agents/native-cli-worker-runtime-proof.js +1 -1
- package/dist/core/agents/native-cli-worker-runtime.js +69 -647
- package/dist/core/agents/native-cli-worker.js +6 -7
- package/dist/core/agents/native-worker-backend-router.js +2 -32
- package/dist/core/agents/no-subagent-scaling-policy.js +1 -1
- package/dist/core/agents/parallel-runtime-proof.js +1 -6
- package/dist/core/agents/runtime-proof-summary.js +1 -35
- package/dist/core/architecture-hardening/contracts/contracts.js +193 -0
- package/dist/core/architecture-hardening/migration/migration.js +111 -0
- package/dist/core/architecture-hardening/state/state-service.js +155 -0
- package/dist/core/artifact-schemas.js +0 -3
- package/dist/core/codex/codex-config-guard.js +238 -52
- package/dist/core/codex-adapter.js +5 -3
- package/dist/core/codex-app/auxiliary-oauth-policy.js +36 -0
- package/dist/core/codex-app/catalog-compat/catalog-service.js +104 -0
- package/dist/core/codex-app/child-policy/child-policy.js +35 -0
- package/dist/core/codex-app/codex-app-handoff.js +0 -1
- package/dist/core/codex-app/codex-hook-lifecycle.js +1 -1
- package/dist/core/codex-app/codex-init-deep.js +1 -1
- package/dist/core/codex-app/menubar/app-bundle.js +2 -0
- package/dist/core/codex-app/menubar/constants.js +14 -1
- package/dist/core/codex-app/menubar/global-install.js +288 -0
- package/dist/core/codex-app/menubar/installer.js +79 -14
- package/dist/core/codex-app/menubar/launch-agent.js +267 -45
- package/dist/core/codex-app/menubar/paths.js +3 -0
- package/dist/core/codex-app/menubar/rollback.js +55 -7
- package/dist/core/codex-app/menubar/status.js +75 -4
- package/dist/core/codex-app/provider-child-policy.js +64 -0
- package/dist/core/codex-app/provider-mode.js +91 -0
- package/dist/core/codex-app/provider-operation-stages.js +20 -0
- package/dist/core/codex-app/provider-session-policy.js +85 -0
- package/dist/core/codex-app/session-policy/session-pinning.js +68 -0
- package/dist/core/codex-app.js +3 -2
- package/dist/core/codex-control/codex-0139-image-path-real-probe.js +3 -1
- package/dist/core/codex-control/codex-0139-web-search-probe.js +3 -1
- package/dist/core/codex-control/codex-app-server-runtime-env.js +13 -4
- package/dist/core/codex-control/codex-app-server-v2-client.js +3 -3
- package/dist/core/codex-control/codex-control-proof.js +0 -1
- package/dist/core/codex-control/codex-task-runner.js +1 -4
- package/dist/core/codex-exec-output-schema.js +4 -1
- package/dist/core/codex-lb/codex-lb-env.js +63 -72
- package/dist/core/codex-lb/codex-lb-setup.js +15 -2
- package/dist/core/codex-lb/desktop-bridge/header-policy.js +6 -0
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +48 -6
- package/dist/core/codex-lb/desktop-bridge/security.js +134 -0
- package/dist/core/codex-lb/desktop-bridge/server.js +9 -3
- package/dist/core/codex-lb/desktop-bridge/state.js +26 -6
- package/dist/core/codex-lb/desktop-bridge/types.js +1 -0
- package/dist/core/codex-lb/desktop-center-credentials.js +517 -58
- package/dist/core/codex-lb/desktop-controller.js +177 -56
- package/dist/core/codex-lb/desktop-service.js +179 -40
- package/dist/core/codex-lb/legacy-migration.js +1 -3
- package/dist/core/codex-lb/native-openai-transport/transport.js +68 -0
- package/dist/core/codex-lb/probes/cli-image-tool-probe.js +1 -1
- package/dist/core/codex-lb/provider-routing/provider-router.js +58 -0
- package/dist/core/codex-lb/routing-truth.js +327 -0
- package/dist/core/codex-native/codex-native-feature-broker.js +4 -0
- package/dist/core/codex-native/codex-native-pattern-analysis.js +1 -2
- package/dist/core/codex-native/core-skill-manifest.js +1 -1
- package/dist/core/codex-plugins/codex-plugin-json.js +26 -6
- package/dist/core/codex-plugins/codex-plugin-repair.js +32 -6
- package/dist/core/commands/basic-cli.js +9 -7
- package/dist/core/commands/computer-use-command.js +1 -1
- package/dist/core/commands/image-ux-review-command.js +16 -9
- package/dist/core/commands/install-package-command.js +105 -0
- package/dist/core/commands/intent-normalization/normalizer.js +96 -0
- package/dist/core/commands/mad-sks-command.js +23 -274
- package/dist/core/commands/mad-sks-headless.js +11 -30
- package/dist/core/commands/naruto-command.js +0 -25
- package/dist/core/commands/qa-loop-command.js +104 -22
- package/dist/core/commands/remote-command.js +2 -2
- package/dist/core/commands/research-command.js +10 -5
- package/dist/core/commands/telegram-command.js +407 -0
- package/dist/core/config-adopt/config-adopt-command.js +35 -0
- package/dist/core/config-adopt/config-adopt.js +346 -0
- package/dist/core/config-adopt/index.js +3 -0
- package/dist/core/doctor/command-alias-cleanup.js +4 -1
- package/dist/core/doctor/current-project-guidance-nested.js +37 -6
- package/dist/core/doctor/current-project-guidance.js +15 -9
- package/dist/core/doctor/doctor-dirty-planner.js +1 -4
- package/dist/core/doctor/doctor-readiness-matrix.js +15 -18
- package/dist/core/doctor/retired-managed-residue-private.js +4 -3
- package/dist/core/doctor/retired-managed-residue-runtime.js +2 -2
- package/dist/core/doctor/skill-legacy-surface.js +11 -10
- package/dist/core/doctor/sks-menubar-doctor.js +4 -0
- package/dist/core/evaluation.js +0 -19
- package/dist/core/evidence/v2/evidence-key.js +105 -0
- package/dist/core/feature-fixtures.js +12 -7
- package/dist/core/feature-registry.js +52 -21
- package/dist/core/hooks-runtime/hook-context.js +9 -0
- package/dist/core/hooks-runtime/managed-skill-admission.js +54 -4
- package/dist/core/hooks-runtime/official-subagent-lifecycle.js +69 -52
- package/dist/core/hooks-runtime/stop-finalization.js +226 -0
- package/dist/core/hooks-runtime/stop-repeat-guard.js +21 -2
- package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +2 -2
- package/dist/core/hooks-runtime/subagent-skill-availability-evidence.js +3 -3
- package/dist/core/hooks-runtime/subagent-skill-availability-guards.js +43 -5
- package/dist/core/hooks-runtime/subagent-skill-availability.js +64 -30
- package/dist/core/hooks-runtime/subagent-stop-hook.js +15 -2
- package/dist/core/hooks-runtime.js +179 -212
- package/dist/core/image/reference-evidence/reference-registry.js +145 -0
- package/dist/core/image-ux-review/imagegen-adapter.js +21 -9
- package/dist/core/image-ux-review/reference-policy/reference-policy.js +9 -0
- package/dist/core/imagegen/codex-lb-imagegen-target.js +8 -0
- package/dist/core/imagegen/imagegen-capability.js +59 -2
- package/dist/core/imagegen/require-imagegen.js +2 -2
- package/dist/core/init/skills.js +17 -3
- package/dist/core/init.js +3 -3
- package/dist/core/install/installed-package-smoke.js +23 -5
- package/dist/core/json/canonical.js +9 -0
- package/dist/core/locks/file-lock.js +0 -1
- package/dist/core/loops/loop-continuation-enforcer.js +65 -7
- package/dist/core/loops/loop-decomposer.js +0 -1
- package/dist/core/loops/loop-gate-runner.js +65 -4
- package/dist/core/loops/loop-gate-selector.js +0 -2
- package/dist/core/loops/loop-proof-validation.js +38 -0
- package/dist/core/loops/loop-risk-classifier.js +1 -1
- package/dist/core/loops/loop-runtime.js +14 -2
- package/dist/core/loops/loop-scheduler.js +7 -2
- package/dist/core/loops/loop-worker-runtime.js +0 -15
- package/dist/core/loops/loop-worktree-runtime.js +52 -4
- package/dist/core/managed-assets/managed-assets-manifest.js +14 -14
- package/dist/core/naruto/hardware-capacity-probe.js +9 -6
- package/dist/core/naruto/naruto-active-pool.js +2 -14
- package/dist/core/naruto/naruto-concurrency-governor.js +71 -31
- package/dist/core/naruto/naruto-loop-worker-router.js +0 -2
- package/dist/core/naruto/naruto-verification-dag.js +0 -1
- package/dist/core/naruto/naruto-write-e2e.js +1 -2
- package/dist/core/naruto/resource-pressure-monitor.js +0 -4
- package/dist/core/observability/architecture-hardening/safe-projection.js +65 -0
- package/dist/core/perf/import-graph-budget.js +0 -1
- package/dist/core/perf/release-latency-slo.js +3 -2
- package/dist/core/preflight/parallel-preflight-engine.js +2 -15
- package/dist/core/product-design-app-server.js +13 -1
- package/dist/core/proof/fake-real-proof-policy.js +2 -17
- package/dist/core/proof/route-proof-gate.js +2 -10
- package/dist/core/proof/route-proof-policy.js +1 -1
- package/dist/core/proof/runtime-evidence-policy.js +185 -0
- package/dist/core/proof/runtime-truth-matrix.js +64 -39
- package/dist/core/proof/validation.js +27 -11
- package/dist/core/provider/provider-context.js +18 -2
- package/dist/core/providers/openrouter/openrouter-account.js +3 -2
- package/dist/core/python-tools/python-tool-runner.js +1 -1
- package/dist/core/qa-loop/qa-execution-control.js +57 -0
- package/dist/core/questions.js +1 -1
- package/dist/core/release/extreme-parallel-scheduler.js +0 -3
- package/dist/core/release/gate-manifest.js +3 -7
- package/dist/core/release/gate-pack-manifest.js +3 -7
- package/dist/core/release/package-size-budget.js +2 -2
- package/dist/core/release/physical-release-gates.js +189 -0
- package/dist/core/release/publish-preflight.js +113 -0
- package/dist/core/release/release-gate-affected-selector.js +0 -2
- package/dist/core/release/release-gate-batch-runner.js +1 -1
- package/dist/core/release/release-gate-node.js +0 -1
- package/dist/core/release/release-gate-resource-governor.js +0 -1
- package/dist/core/release/release-pack-receipt.js +5 -8
- package/dist/core/release/release-real-contract.js +0 -6
- package/dist/core/release/resource-class-budget.js +0 -1
- package/dist/core/release/stage-publish.js +178 -36
- package/dist/core/research/research-adversarial-review.js +103 -9
- package/dist/core/research.js +1 -1
- package/dist/core/retention.js +0 -1
- package/dist/core/routes/constants.js +1 -1
- package/dist/core/routes/diagnostic-allowlist.js +0 -1
- package/dist/core/routes/dollar-manifest-lite.js +1 -1
- package/dist/core/routes.js +46 -15
- package/dist/core/runtime/execution-control.js +226 -0
- package/dist/core/runtime/progress-recovery/progress-recovery.js +89 -0
- package/dist/core/runtime/task-profile.js +10 -4
- package/dist/core/runtime/verification-budget.js +10 -1
- package/dist/core/safety/intent-contract/intent-contract.js +94 -0
- package/dist/core/safety/mutation-guard.js +0 -2
- package/dist/core/safety/mutation-ledger.js +0 -2
- package/dist/core/safety/requested-scope-contract.js +0 -0
- package/dist/core/search-visibility/discovery.js +1 -1
- package/dist/core/search-visibility/index.js +12 -0
- package/dist/core/search-visibility/verifier.js +112 -3
- package/dist/core/security/private-credential-file.js +209 -0
- package/dist/core/session/project-namespace.js +0 -5
- package/dist/core/skills/skill-agent-metadata.js +1 -3
- package/dist/core/strategy/adhd-orchestrating-gate.js +0 -1
- package/dist/core/subagents/agent-catalog.js +19 -34
- package/dist/core/subagents/model-policy.js +65 -24
- package/dist/core/subagents/naruto-help-contract.js +8 -8
- package/dist/core/subagents/naruto-host-credentials.js +13 -0
- package/dist/core/subagents/official-subagent-config.js +16 -6
- package/dist/core/subagents/official-subagent-preparation.js +35 -16
- package/dist/core/subagents/official-subagent-prompt.js +7 -7
- package/dist/core/subagents/official-subagent-runner.js +27 -4
- package/dist/core/subagents/role-model-preferences.js +1 -1
- package/dist/core/subagents/thread-budget.js +19 -12
- package/dist/core/subagents/wave-lifecycle.js +18 -6
- package/dist/core/telegram/access.js +58 -0
- package/dist/core/telegram/client.js +157 -0
- package/dist/core/telegram/confirmation.js +37 -0
- package/dist/core/telegram/doctor.js +154 -0
- package/dist/core/telegram/index.js +9 -0
- package/dist/core/telegram/keychain.js +317 -0
- package/dist/core/telegram/liveness.js +118 -0
- package/dist/core/telegram/poller.js +90 -0
- package/dist/core/telegram/redaction.js +15 -0
- package/dist/core/telegram/state-lock.js +416 -0
- package/dist/core/telegram/types.js +1 -0
- package/dist/core/text/strings.js +3 -0
- package/dist/core/triwiki/context-graph/store/evidence-write-lock.js +29 -0
- package/dist/core/triwiki/context-graph/store/fragment-cache.js +24 -0
- package/dist/core/triwiki/context-graph/store/snapshot-store.js +69 -21
- package/dist/core/triwiki/triwiki-gate-impact-map.js +2 -2
- package/dist/core/triwiki/triwiki-module-card.js +1 -2
- package/dist/core/update/installed-cli-resolution.js +170 -0
- package/dist/core/update/update-diagnostics.js +52 -0
- package/dist/core/update/update-migration-state.js +30 -12
- package/dist/core/update/update-operation.js +2 -1
- package/dist/core/update/update-stage-diagnostics.js +38 -0
- package/dist/core/update/update-status.js +2 -9
- package/dist/core/update-check.js +279 -91
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/AppDelegate.swift +19 -1
- package/dist/native/sks-menubar/Sources/AppIdentity.swift +7 -0
- package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +6 -2
- package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +159 -92
- package/dist/native/sks-menubar/Sources/OperationModels.swift +389 -0
- package/dist/native/sks-menubar/Sources/OverviewViewController.swift +57 -0
- package/dist/native/sks-menubar/Sources/ProcessClient.swift +3 -1
- package/dist/native/sks-menubar/Sources/ProvidersConnectTest.swift +115 -0
- package/dist/native/sks-menubar/Sources/ProvidersFastMode.swift +17 -0
- package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +3 -0
- package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +58 -17
- package/dist/native/sks-menubar/Sources/ProvidersReliability.swift +302 -0
- package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +5 -0
- package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +190 -0
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +158 -126
- package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +462 -26
- package/dist/native/sks-menubar/Sources/SKSKeychainStore.swift +344 -0
- package/dist/native/sks-menubar/Sources/SecureProcessEnvelope.swift +27 -0
- package/dist/native/sks-menubar/Sources/SingletonInstanceGuard.swift +328 -0
- package/dist/native/sks-menubar/Sources/StatusItemController.swift +10 -2
- package/dist/native/sks-menubar/Sources/TelegramPrivateFileStore.swift +498 -0
- package/dist/native/sks-menubar/Sources/TelegramPrivateFileSupport.swift +236 -0
- package/dist/native/sks-menubar/Sources/TelegramProcessGateway.swift +454 -0
- package/dist/native/sks-menubar/Sources/TelegramRuntimeSupport.swift +56 -0
- package/dist/native/sks-menubar/Sources/TelegramStateLock.swift +448 -0
- package/dist/native/sks-menubar/Sources/TelegramSupport.swift +187 -0
- package/dist/native/sks-menubar/Sources/TelegramTransport.swift +499 -0
- package/dist/native/sks-menubar/Sources/main.swift +15 -1
- package/dist/scripts/all-feature-deep-completion-check.js +5 -1
- package/dist/scripts/check-publish-tag.js +56 -0
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +10 -3
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +12 -3
- package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +4 -2
- package/dist/scripts/codex-native-agent-role-content-check.js +3 -3
- package/dist/scripts/codex-native-gate-lib.js +14 -7
- package/dist/scripts/current-command-surface-check.js +7 -7
- package/dist/scripts/current-surface-update-e2e-check.js +4 -3
- package/dist/scripts/current-upgrade-matrix-check.js +5 -24
- package/dist/scripts/docs-truthfulness-check.js +17 -13
- package/dist/scripts/gate-lib.js +0 -2
- package/dist/scripts/gate-policy-audit-check.js +3 -2
- package/dist/scripts/harness-benchmark-check.js +1 -1
- package/dist/scripts/init-deep-backup-retention-check.js +1 -1
- package/dist/scripts/lib/codex-sdk-gate-lib.js +1 -2
- package/dist/scripts/loop-directive-check-lib.js +28 -38
- package/dist/scripts/mad-sks-app-ui-no-mutation-check.js +14 -180
- package/dist/scripts/mad-sks-launch-sql-plane-boundary-check.js +2 -11
- package/dist/scripts/official-subagent-workflow-check.js +1 -1
- package/dist/scripts/packlist-performance-check.js +1 -2
- package/dist/scripts/prepublish-release-check-or-fast.js +32 -0
- package/dist/scripts/publish-preflight.js +12 -0
- package/dist/scripts/release-affected-selector-check.js +2 -2
- package/dist/scripts/release-gate-batch-runner-check.js +2 -2
- package/dist/scripts/release-metadata-check.js +8 -12
- package/dist/scripts/release-real-check.js +2 -54
- package/dist/scripts/runtime-domain-gate-lib.js +2 -96
- package/dist/scripts/runtime-proof-summary-check.js +2 -6
- package/dist/scripts/search-visibility-gate-lib.js +0 -1
- package/dist/scripts/sks-cli-gate-lib.js +1 -1
- package/dist/scripts/typed-routing-gate-lib.js +7 -59
- package/docs/demo.tape +1 -1
- package/infra-harness-gates.json +1 -1456
- package/package.json +10 -7
- package/release-gates.v2.json +0 -1
- package/schemas/agents/parallel-runtime-proof.schema.json +0 -2
- package/schemas/codex/agent-result.schema.json +0 -6
- package/schemas/naruto/naruto-concurrency-governor.schema.json +1 -3
- package/schemas/release/release-gate-node.schema.json +1 -1
- package/dist/commands/zellij-lane.js +0 -22
- package/dist/commands/zellij-monitor-pane.js +0 -26
- package/dist/commands/zellij-slot-column-anchor.js +0 -22
- package/dist/commands/zellij-slot-pane.js +0 -80
- package/dist/commands/zellij-viewport-pane.js +0 -57
- package/dist/commands/zellij.js +0 -422
- package/dist/core/agents/agent-runner-zellij.js +0 -164
- package/dist/core/agents/agent-slot-pane-binding-proof.js +0 -96
- package/dist/core/agents/parallel-write-fixture.js +0 -305
- package/dist/core/agents/worker-pane-communication-contract.js +0 -100
- package/dist/core/agents/zellij-lane-supervisor.js +0 -428
- package/dist/core/agents/zellij-right-lane-cockpit.js +0 -66
- package/dist/core/doctor/doctor-zellij-repair.js +0 -40
- package/dist/core/ops/upgrade-migration-fixtures.js +0 -119
- package/dist/core/proof/route-finalizer-fixtures.js +0 -21
- package/dist/core/zellij/homebrew-policy.js +0 -43
- package/dist/core/zellij/zellij-capability.js +0 -126
- package/dist/core/zellij/zellij-clipboard-config.js +0 -52
- package/dist/core/zellij/zellij-command.js +0 -149
- package/dist/core/zellij/zellij-fake-adapter.js +0 -186
- package/dist/core/zellij/zellij-lane-renderer.js +0 -706
- package/dist/core/zellij/zellij-lane-runtime.js +0 -189
- package/dist/core/zellij/zellij-launcher.js +0 -268
- package/dist/core/zellij/zellij-layout-builder.js +0 -191
- package/dist/core/zellij/zellij-monitor-renderer.js +0 -52
- package/dist/core/zellij/zellij-official-subagent-activity.js +0 -526
- package/dist/core/zellij/zellij-official-subagent-telemetry.js +0 -258
- package/dist/core/zellij/zellij-pane-proof.js +0 -226
- package/dist/core/zellij/zellij-right-column-manager.js +0 -308
- package/dist/core/zellij/zellij-screen-proof.js +0 -120
- package/dist/core/zellij/zellij-self-heal-types.js +0 -44
- package/dist/core/zellij/zellij-self-heal.js +0 -408
- package/dist/core/zellij/zellij-slot-column-anchor.js +0 -304
- package/dist/core/zellij/zellij-slot-pane-renderer.js +0 -521
- package/dist/core/zellij/zellij-slot-telemetry.js +0 -384
- package/dist/core/zellij/zellij-theme.js +0 -66
- package/dist/core/zellij/zellij-ui-mode.js +0 -34
- package/dist/core/zellij/zellij-update.js +0 -348
- package/dist/core/zellij/zellij-viewport-binder.js +0 -61
- package/dist/core/zellij/zellij-worker-pane-manager.js +0 -881
- package/dist/core/zellij/zellij-worker-pane-summary.js +0 -64
- package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +0 -26
- package/dist/native/sks-menubar/Tests/OperationCoordinatorTests.swift +0 -174
- package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +0 -20
- package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +0 -142
- package/dist/scripts/zellij-pane-proof-check.js +0 -88
- package/dist/scripts/zellij-real-session-cleanup-check.js +0 -139
- package/dist/scripts/zellij-real-session-launch-check.js +0 -126
- package/dist/scripts/zellij-screen-proof-check.js +0 -43
- package/schemas/zellij/zellij-right-column-state.schema.json +0 -40
- package/schemas/zellij/zellij-slot-telemetry.schema.json +0 -89
- package/schemas/zellij/zellij-worker-pane.schema.json +0 -53
- package/scripts/build-clean-atomic.mjs +0 -323
|
@@ -1,49 +1,485 @@
|
|
|
1
1
|
import Cocoa
|
|
2
2
|
|
|
3
|
-
final class RemoteCodingViewController: NSViewController {
|
|
4
|
-
private static let
|
|
5
|
-
private static let
|
|
3
|
+
final class RemoteCodingViewController: NSViewController, ControlCenterPage {
|
|
4
|
+
private static let botFatherURL = URL(string: "https://t.me/BotFather")!
|
|
5
|
+
private static let tokenPattern = #"^\d{5,20}:[A-Za-z0-9_-]{20,128}$"#
|
|
6
|
+
|
|
7
|
+
private let processClient: ProcessClient
|
|
8
|
+
private let telegramService: TelegramMenuBarService
|
|
9
|
+
private let serviceBadge = NativeView.badge("Checking Telegram service", color: .systemBlue)
|
|
10
|
+
private let serviceDetail = NativeView.detail("Reading secret-free Telegram liveness status…")
|
|
11
|
+
private let pairingDetail = NativeView.detail("No pairing code has been issued in this session.")
|
|
12
|
+
private let serviceSpinner = NativeView.spinner(label: "Telegram operation in progress")
|
|
13
|
+
private lazy var setupButton = NativeView.button("Enter Bot Token…", target: self, action: #selector(enterToken))
|
|
14
|
+
private lazy var pairButton = NativeView.button("Generate Pairing Code", target: self, action: #selector(generatePairingCode))
|
|
15
|
+
private lazy var startButton = NativeView.button("Start", target: self, action: #selector(startService))
|
|
16
|
+
private lazy var stopButton = NativeView.button("Stop", target: self, action: #selector(stopService))
|
|
17
|
+
private lazy var restartButton = NativeView.button("Restart", target: self, action: #selector(restartService))
|
|
18
|
+
private lazy var refreshButton = NativeView.button("Refresh Status", target: self, action: #selector(refreshStatusAction))
|
|
19
|
+
private var operationInFlight = false
|
|
20
|
+
private var tokenConfigured = false
|
|
21
|
+
private var pollerRunning = false
|
|
22
|
+
|
|
23
|
+
init(processClient: ProcessClient, telegramService: TelegramMenuBarService) {
|
|
24
|
+
self.processClient = processClient
|
|
25
|
+
self.telegramService = telegramService
|
|
26
|
+
super.init(nibName: nil, bundle: nil)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
required init?(coder: NSCoder) { nil }
|
|
6
30
|
|
|
7
31
|
override func loadView() {
|
|
8
|
-
let
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
)
|
|
13
|
-
|
|
32
|
+
let botFatherButton = NativeView.button("Open @BotFather", target: self, action: #selector(openBotFather))
|
|
33
|
+
botFatherButton.setAccessibilityHelp("Open the official Telegram BotFather chat in your default handler.")
|
|
34
|
+
setupButton.setAccessibilityHelp("Enter a BotFather token securely. The token is verified live and stored in the private SKS user secret file.")
|
|
35
|
+
pairButton.setAccessibilityHelp("With Telegram polling running, issue a short-lived, single-use code for pairing one intended private chat.")
|
|
36
|
+
startButton.setAccessibilityHelp("Start Telegram polling for the current SKS companion session after a bot token is configured.")
|
|
37
|
+
stopButton.setAccessibilityHelp("Stop polling for the current companion session without deleting the token or paired chats. A companion relaunch starts it again.")
|
|
38
|
+
restartButton.setAccessibilityHelp("Restart the single resident Telegram poller and revalidate the stored bot identity.")
|
|
39
|
+
refreshButton.setAccessibilityHelp("Refresh secret-free bot, pairing, audit, and poller status.")
|
|
40
|
+
pairingDetail.isSelectable = true
|
|
41
|
+
pairingDetail.setAccessibilityLabel("Telegram pairing instructions")
|
|
42
|
+
serviceDetail.setAccessibilityLabel("Telegram service status detail")
|
|
14
43
|
|
|
15
|
-
let
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
44
|
+
let setupCard = NativeView.card(
|
|
45
|
+
title: "Connect with BotFather",
|
|
46
|
+
subtitle: "Create a Telegram bot, then let SKS validate and store its token privately.",
|
|
47
|
+
views: [
|
|
48
|
+
NativeView.detail(
|
|
49
|
+
"1. Open @BotFather and send /newbot. 2. Finish the bot name and username prompts. " +
|
|
50
|
+
"3. Copy the HTTP API token BotFather returns. 4. Choose Enter Bot Token below."
|
|
51
|
+
),
|
|
52
|
+
NativeView.detail(
|
|
53
|
+
"The token is sent only through process stdin to the canonical SKS setup command, verified with Telegram getMe, and stored in the private user secret file. It is never shown again or written to the SKS action log. Revoke it in BotFather if you suspect exposure."
|
|
54
|
+
),
|
|
55
|
+
NativeView.row([botFatherButton, setupButton])
|
|
56
|
+
]
|
|
19
57
|
)
|
|
20
|
-
sourceButton.setAccessibilityHelp("Open the external Orca source repository in your default browser.")
|
|
21
58
|
|
|
22
|
-
let
|
|
23
|
-
title: "
|
|
24
|
-
subtitle: "
|
|
59
|
+
let pairingCard = NativeView.card(
|
|
60
|
+
title: "Pair a Private Chat",
|
|
61
|
+
subtitle: "Authorize only the intended Telegram account and private chat.",
|
|
25
62
|
views: [
|
|
26
63
|
NativeView.detail(
|
|
27
|
-
"
|
|
64
|
+
"With Telegram polling running, generate a short-lived code, open your new bot from the intended private chat, and send /start followed by that code. Codes are single-use; group chats are rejected. After pairing, try /sks status {}."
|
|
28
65
|
),
|
|
29
|
-
|
|
66
|
+
pairingDetail,
|
|
67
|
+
pairButton
|
|
68
|
+
]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
let statusCard = NativeView.card(
|
|
72
|
+
title: "Service Status",
|
|
73
|
+
subtitle: "Control the single SKS-owned poller for this companion session and inspect secret-free health evidence.",
|
|
74
|
+
views: [
|
|
75
|
+
NativeView.row([serviceBadge, serviceSpinner]),
|
|
76
|
+
serviceDetail,
|
|
77
|
+
NativeView.row([startButton, stopButton, restartButton, refreshButton])
|
|
30
78
|
]
|
|
31
79
|
)
|
|
32
80
|
|
|
33
81
|
view = NativeView.page([
|
|
34
|
-
NativeView.title("Remote
|
|
82
|
+
NativeView.title("Telegram Remote Control"),
|
|
35
83
|
NativeView.detail(
|
|
36
|
-
"
|
|
84
|
+
"Connect SKS to a BotFather-created Telegram bot for paired, allowlisted remote commands with confirmation gates and a local audit trail."
|
|
37
85
|
),
|
|
38
|
-
|
|
86
|
+
setupCard,
|
|
87
|
+
pairingCard,
|
|
88
|
+
statusCard
|
|
39
89
|
])
|
|
90
|
+
updateControls()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
func refreshOnAppear() { refreshStatus() }
|
|
94
|
+
|
|
95
|
+
@objc private func openBotFather() {
|
|
96
|
+
NSWorkspace.shared.open(Self.botFatherURL)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@objc private func enterToken() {
|
|
100
|
+
if TelegramPrivateFileStore.operatorEnvironmentOverrideActive() {
|
|
101
|
+
showAttention(
|
|
102
|
+
"Environment-managed token is active",
|
|
103
|
+
detail: "SKS is using an operator-managed Telegram token environment override. Remove TELEGRAM_BOT_TOKEN or SKS_TELEGRAM_BOT_TOKEN and relaunch the companion before storing a BotFather token file."
|
|
104
|
+
)
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
promptForToken(removeWebhook: false)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private func promptForToken(removeWebhook: Bool) {
|
|
111
|
+
guard !operationInFlight, let window = view.window else { return }
|
|
112
|
+
AlertFactory.textSheet(
|
|
113
|
+
window: window,
|
|
114
|
+
title: "Enter Telegram Bot Token",
|
|
115
|
+
message: removeWebhook
|
|
116
|
+
? "Paste the BotFather token again. With your consent, SKS will remove this bot's existing webhook without dropping pending updates, verify the bot, and store the token privately."
|
|
117
|
+
: "Paste the HTTP API token supplied by @BotFather. SKS will verify the bot identity before storing it privately.",
|
|
118
|
+
secure: true,
|
|
119
|
+
placeholder: "123456789:BotFatherToken",
|
|
120
|
+
actionTitle: removeWebhook ? "Remove Webhook & Save" : "Verify & Save"
|
|
121
|
+
) { [weak self] token in
|
|
122
|
+
guard let self, let token else { return }
|
|
123
|
+
let normalizedToken = token.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
124
|
+
guard self.validTokenShape(normalizedToken) else {
|
|
125
|
+
self.showAttention(
|
|
126
|
+
"Token not saved",
|
|
127
|
+
detail: "That value does not match the BotFather token format. Copy the complete HTTP API token and try again."
|
|
128
|
+
)
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
self.saveToken(normalizedToken, removeWebhook: removeWebhook)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@objc private func generatePairingCode() {
|
|
136
|
+
guard !operationInFlight else { return }
|
|
137
|
+
beginOperation("Issuing a short-lived pairing code…")
|
|
138
|
+
processClient.run(
|
|
139
|
+
["telegram", "pair", "--json"],
|
|
140
|
+
environment: telegramSecretFreeEnvironment,
|
|
141
|
+
timeout: NativeView.statusTimeout,
|
|
142
|
+
logOutput: false
|
|
143
|
+
) { [weak self] result in
|
|
144
|
+
guard let self else { return }
|
|
145
|
+
self.endOperation()
|
|
146
|
+
guard result.code == 0,
|
|
147
|
+
let response = self.decode(TelegramCenterPairResponse.self, result.output),
|
|
148
|
+
response.ok,
|
|
149
|
+
let code = response.code,
|
|
150
|
+
code.range(of: #"^\d{6}-[A-F0-9]{4}$"#, options: .regularExpression) != nil else {
|
|
151
|
+
self.showAttention(
|
|
152
|
+
"Pairing code unavailable",
|
|
153
|
+
detail: self.publicFailure(result.output, fallback: "SKS could not create a pairing code. Refresh status and try again.")
|
|
154
|
+
)
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
let expiration = response.expires_at.map { " It expires at \($0)." } ?? ""
|
|
158
|
+
let instruction = response.instruction
|
|
159
|
+
?? "Send /start \(code) to this bot from the intended private chat."
|
|
160
|
+
let postPair = response.post_pair_command ?? "/sks status {}"
|
|
161
|
+
let confirmation = response.confirmation_grammar ?? "/confirm <nonce>"
|
|
162
|
+
let statusGuidance = instruction.contains(postPair) ? "" : " Then try \(postPair)."
|
|
163
|
+
self.pairingDetail.stringValue = "\(instruction)\(expiration)\(statusGuidance) Confirm prompted actions with \(confirmation)."
|
|
164
|
+
self.pairingDetail.setAccessibilityValue(self.pairingDetail.stringValue)
|
|
165
|
+
self.showProgress("Pairing code ready", detail: "Use the displayed code once in the intended private chat.")
|
|
166
|
+
self.refreshStatus(after: 1)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@objc private func startService() { runLifecycle(.start) }
|
|
171
|
+
@objc private func stopService() { runLifecycle(.stop) }
|
|
172
|
+
@objc private func restartService() { runLifecycle(.restart) }
|
|
173
|
+
@objc private func refreshStatusAction() { refreshStatus() }
|
|
174
|
+
|
|
175
|
+
private enum LifecycleAction { case start, stop, restart }
|
|
176
|
+
|
|
177
|
+
private func runLifecycle(_ action: LifecycleAction) {
|
|
178
|
+
guard !operationInFlight else { return }
|
|
179
|
+
let actionName: String
|
|
180
|
+
switch action {
|
|
181
|
+
case .start: actionName = "Starting"
|
|
182
|
+
case .stop: actionName = "Stopping"
|
|
183
|
+
case .restart: actionName = "Restarting"
|
|
184
|
+
}
|
|
185
|
+
beginOperation("\(actionName) Telegram polling…")
|
|
186
|
+
|
|
187
|
+
switch action {
|
|
188
|
+
case .stop:
|
|
189
|
+
let task = telegramService.stop()
|
|
190
|
+
Task { @MainActor [weak self] in
|
|
191
|
+
await task.value
|
|
192
|
+
self?.endOperation()
|
|
193
|
+
self?.showProgress(
|
|
194
|
+
"Telegram polling stopped",
|
|
195
|
+
detail: "The token and paired chats were preserved. Relaunching the SKS companion starts polling again."
|
|
196
|
+
)
|
|
197
|
+
self?.refreshStatus(after: 0.2)
|
|
198
|
+
}
|
|
199
|
+
case .start, .restart:
|
|
200
|
+
let task = action == .start ? telegramService.start() : telegramService.restart()
|
|
201
|
+
Task { @MainActor [weak self] in
|
|
202
|
+
do {
|
|
203
|
+
let receipt = try await task.value
|
|
204
|
+
self?.endOperation()
|
|
205
|
+
self?.render(receipt)
|
|
206
|
+
} catch {
|
|
207
|
+
self?.endOperation()
|
|
208
|
+
self?.showAttention(
|
|
209
|
+
"Telegram polling did not start",
|
|
210
|
+
detail: "Refresh status for the verified blocker. Re-enter the BotFather token if identity validation failed."
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
self?.refreshStatus(after: 0.3)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private func saveToken(_ token: String, removeWebhook: Bool) {
|
|
219
|
+
let normalizedToken = token.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
220
|
+
guard validTokenShape(normalizedToken) else {
|
|
221
|
+
showAttention(
|
|
222
|
+
"Token not saved",
|
|
223
|
+
detail: "That value does not match the BotFather token format. Copy the complete token and try again."
|
|
224
|
+
)
|
|
225
|
+
return
|
|
226
|
+
}
|
|
227
|
+
beginOperation("Verifying bot identity with Telegram…")
|
|
228
|
+
var arguments = ["telegram", "setup", "--token-stdin", "--json"]
|
|
229
|
+
if TelegramPrivateFileStore.operatorEnvironmentOverrideActive() {
|
|
230
|
+
arguments.append("--operator-env-override-active")
|
|
231
|
+
}
|
|
232
|
+
if removeWebhook { arguments.append("--remove-webhook") }
|
|
233
|
+
processClient.run(
|
|
234
|
+
arguments,
|
|
235
|
+
stdin: normalizedToken + "\n",
|
|
236
|
+
environment: telegramSecretFreeEnvironment,
|
|
237
|
+
timeout: NativeView.mutationTimeout,
|
|
238
|
+
logOutput: false
|
|
239
|
+
) { [weak self] result in
|
|
240
|
+
guard let self else { return }
|
|
241
|
+
let response = self.decode(TelegramCenterSetupResponse.self, result.output)
|
|
242
|
+
guard result.code == 0,
|
|
243
|
+
let response,
|
|
244
|
+
response.ok,
|
|
245
|
+
response.getme_verified == true,
|
|
246
|
+
response.token_stored == true else {
|
|
247
|
+
self.endOperation()
|
|
248
|
+
if !removeWebhook,
|
|
249
|
+
response?.error == "telegram_webhook_configured_remove_consent_required" {
|
|
250
|
+
self.confirmWebhookRemoval()
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
if response?.error == "telegram_operator_env_override_active" {
|
|
254
|
+
self.showAttention(
|
|
255
|
+
"Environment-managed token is active",
|
|
256
|
+
detail: response?.operator_action
|
|
257
|
+
?? "Remove the operator-managed Telegram token environment override and relaunch before storing a file token."
|
|
258
|
+
)
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
if response?.partial_success == true {
|
|
262
|
+
self.showAttention(
|
|
263
|
+
"Telegram setup partially completed",
|
|
264
|
+
detail: self.partialSetupRecovery(response)
|
|
265
|
+
)
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
self.showAttention(
|
|
269
|
+
"Token not saved",
|
|
270
|
+
detail: self.publicFailure(
|
|
271
|
+
result.output,
|
|
272
|
+
fallback: "Telegram could not verify this bot token. Confirm it in @BotFather and try again."
|
|
273
|
+
)
|
|
274
|
+
)
|
|
275
|
+
return
|
|
276
|
+
}
|
|
277
|
+
let restart = self.telegramService.restart()
|
|
278
|
+
Task { @MainActor [weak self] in
|
|
279
|
+
do {
|
|
280
|
+
let receipt = try await restart.value
|
|
281
|
+
self?.endOperation()
|
|
282
|
+
self?.render(receipt)
|
|
283
|
+
self?.showProgress(
|
|
284
|
+
"Bot verified and connected",
|
|
285
|
+
detail: response.bot_state_reset == true
|
|
286
|
+
? "The token is stored privately. Bot-scoped pairing and poll state were reset; generate a new pairing code."
|
|
287
|
+
: "The token is stored privately. Generate a pairing code for the intended private chat."
|
|
288
|
+
)
|
|
289
|
+
} catch {
|
|
290
|
+
self?.endOperation()
|
|
291
|
+
self?.showAttention(
|
|
292
|
+
"Token saved; poller needs attention",
|
|
293
|
+
detail: "The verified token was stored, but the resident poller did not restart. Refresh status for the blocker."
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
self?.refreshStatus(after: 0.3)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private func confirmWebhookRemoval() {
|
|
302
|
+
guard let window = view.window else { return }
|
|
303
|
+
AlertFactory.confirmSheet(
|
|
304
|
+
window: window,
|
|
305
|
+
title: "Remove the existing Telegram webhook?",
|
|
306
|
+
message: "Telegram cannot run getUpdates while this bot has a webhook. Removing it changes delivery: the existing webhook endpoint will stop receiving updates and SKS long polling can start. SKS requests removal without dropping pending updates. The token was not retained; after Continue, paste it again to authorize this one change.",
|
|
307
|
+
destructive: true,
|
|
308
|
+
actionTitle: "Continue"
|
|
309
|
+
) { [weak self] confirmed in
|
|
310
|
+
guard confirmed else { return }
|
|
311
|
+
self?.promptForToken(removeWebhook: true)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private var telegramSecretFreeEnvironment: [String: String] {
|
|
316
|
+
["TELEGRAM_BOT_TOKEN": "", "SKS_TELEGRAM_BOT_TOKEN": ""]
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
private func refreshStatus(after delay: TimeInterval = 0) {
|
|
320
|
+
guard delay > 0 else {
|
|
321
|
+
loadStatus()
|
|
322
|
+
return
|
|
323
|
+
}
|
|
324
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in self?.loadStatus() }
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
private func loadStatus() {
|
|
328
|
+
guard !operationInFlight else { return }
|
|
329
|
+
beginOperation("Refreshing secret-free Telegram status…")
|
|
330
|
+
processClient.run(
|
|
331
|
+
["telegram", "doctor", "--json"],
|
|
332
|
+
environment: telegramSecretFreeEnvironment,
|
|
333
|
+
timeout: NativeView.statusTimeout,
|
|
334
|
+
logOutput: false
|
|
335
|
+
) { [weak self] result in
|
|
336
|
+
guard let self else { return }
|
|
337
|
+
self.endOperation()
|
|
338
|
+
guard let response = self.decode(TelegramCenterDoctorResponse.self, result.output) else {
|
|
339
|
+
self.tokenConfigured = false
|
|
340
|
+
self.pollerRunning = false
|
|
341
|
+
self.showAttention("Status unavailable", detail: "SKS could not read the Telegram liveness receipt.")
|
|
342
|
+
return
|
|
343
|
+
}
|
|
344
|
+
self.render(response)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
private func render(_ response: TelegramCenterDoctorResponse) {
|
|
349
|
+
tokenConfigured = response.token_configured
|
|
350
|
+
pollerRunning = response.poller.running
|
|
351
|
+
let paired = response.paired_chat_count == 1 ? "1 paired chat" : "\(response.paired_chat_count) paired chats"
|
|
352
|
+
let source = tokenSourceDescription(response.token_source)
|
|
353
|
+
if response.ok {
|
|
354
|
+
NativeView.setBadge(serviceBadge, text: "Ready", color: .systemGreen)
|
|
355
|
+
serviceDetail.stringValue = "Bot identity verified · \(source) · \(paired) · poller running · audit healthy."
|
|
356
|
+
} else if response.self_heal_action == "operator_remove_webhook"
|
|
357
|
+
|| response.poller.last_error?.range(of: "409|webhook", options: .regularExpression) != nil {
|
|
358
|
+
NativeView.setBadge(serviceBadge, text: "Webhook blocks polling", color: .systemOrange)
|
|
359
|
+
serviceDetail.stringValue = "This bot has an active Telegram webhook, so getUpdates polling cannot run. Choose Enter Bot Token again; SKS will explain the delivery change and request explicit consent before removing the webhook without dropping pending updates."
|
|
360
|
+
} else if !response.token_configured {
|
|
361
|
+
NativeView.setBadge(serviceBadge, text: "Bot token required", color: .systemOrange)
|
|
362
|
+
serviceDetail.stringValue = "Create a bot with @BotFather, then enter and verify its HTTP API token."
|
|
363
|
+
} else if !response.bot_identity_valid {
|
|
364
|
+
NativeView.setBadge(serviceBadge, text: "Bot identity not verified", color: .systemRed)
|
|
365
|
+
serviceDetail.stringValue = "Telegram could not validate the stored bot identity. Enter the current BotFather token again."
|
|
366
|
+
} else if !response.audit_healthy {
|
|
367
|
+
NativeView.setBadge(serviceBadge, text: "Audit unavailable", color: .systemRed)
|
|
368
|
+
serviceDetail.stringValue = "Remote control is stopped because its local audit ledger is unavailable. Repair local SKS permissions before restarting."
|
|
369
|
+
} else if response.paired_chat_count == 0 {
|
|
370
|
+
NativeView.setBadge(serviceBadge, text: "Private chat not paired", color: .systemOrange)
|
|
371
|
+
serviceDetail.stringValue = "Bot verified · \(source) · no paired chats · \(response.poller.running ? "poller running" : "poller stopped"). Generate a pairing code and use it once in the intended private chat."
|
|
372
|
+
} else if !response.poller.running {
|
|
373
|
+
NativeView.setBadge(serviceBadge, text: "Poller stopped", color: .systemOrange)
|
|
374
|
+
serviceDetail.stringValue = "Bot identity verified · \(source) · \(paired) · poller stopped. Choose Start to resume."
|
|
375
|
+
} else {
|
|
376
|
+
NativeView.setBadge(serviceBadge, text: "Needs attention", color: .systemOrange)
|
|
377
|
+
serviceDetail.stringValue = "Bot identity verified · \(paired) · poller degraded after \(response.poller.consecutive_failures) consecutive failures. Refresh status or Restart."
|
|
378
|
+
}
|
|
379
|
+
serviceDetail.setAccessibilityValue(serviceDetail.stringValue)
|
|
380
|
+
updateControls()
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
private func render(_ receipt: TelegramLivenessReceipt) {
|
|
384
|
+
tokenConfigured = receipt.token_configured
|
|
385
|
+
pollerRunning = receipt.running && receipt.poller.running
|
|
386
|
+
let paired = receipt.paired_chat_count == 1 ? "1 paired chat" : "\(receipt.paired_chat_count) paired chats"
|
|
387
|
+
let source = tokenSourceDescription(receipt.token_source.rawValue)
|
|
388
|
+
NativeView.setBadge(
|
|
389
|
+
serviceBadge,
|
|
390
|
+
text: pollerRunning ? "Running" : "Stopped",
|
|
391
|
+
color: pollerRunning ? .systemGreen : .systemOrange
|
|
392
|
+
)
|
|
393
|
+
serviceDetail.stringValue = "Bot identity \(receipt.bot_identity_valid ? "verified" : "not verified") · \(source) · \(paired) · poller \(pollerRunning ? "running" : "stopped") · audit \(receipt.audit_healthy == true ? "healthy" : "unavailable")."
|
|
394
|
+
serviceDetail.setAccessibilityValue(serviceDetail.stringValue)
|
|
395
|
+
updateControls()
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
private func beginOperation(_ detail: String) {
|
|
399
|
+
operationInFlight = true
|
|
400
|
+
serviceSpinner.startAnimation(nil)
|
|
401
|
+
serviceDetail.stringValue = detail
|
|
402
|
+
updateControls()
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
private func endOperation() {
|
|
406
|
+
operationInFlight = false
|
|
407
|
+
serviceSpinner.stopAnimation(nil)
|
|
408
|
+
updateControls()
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
private func showProgress(_ title: String, detail: String) {
|
|
412
|
+
NativeView.setBadge(serviceBadge, text: title, color: .systemBlue)
|
|
413
|
+
serviceDetail.stringValue = detail
|
|
414
|
+
serviceDetail.setAccessibilityValue(detail)
|
|
415
|
+
updateControls()
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
private func showAttention(_ title: String, detail: String) {
|
|
419
|
+
NativeView.setBadge(serviceBadge, text: title, color: .systemOrange)
|
|
420
|
+
serviceDetail.stringValue = detail
|
|
421
|
+
serviceDetail.setAccessibilityValue(detail)
|
|
422
|
+
updateControls()
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
private func updateControls() {
|
|
426
|
+
setupButton.isEnabled = !operationInFlight
|
|
427
|
+
pairButton.isEnabled = !operationInFlight && tokenConfigured && pollerRunning
|
|
428
|
+
startButton.isEnabled = !operationInFlight && tokenConfigured && !pollerRunning
|
|
429
|
+
stopButton.isEnabled = !operationInFlight && pollerRunning
|
|
430
|
+
restartButton.isEnabled = !operationInFlight && tokenConfigured
|
|
431
|
+
refreshButton.isEnabled = !operationInFlight
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
private func validTokenShape(_ value: String) -> Bool {
|
|
435
|
+
value.utf8.count <= 1024
|
|
436
|
+
&& value.range(of: Self.tokenPattern, options: .regularExpression) != nil
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
private func decode<T: Decodable>(_ type: T.Type, _ output: String) -> T? {
|
|
440
|
+
guard let data = output.data(using: .utf8) else { return nil }
|
|
441
|
+
return try? JSONDecoder().decode(type, from: data)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
private func publicFailure(_ output: String, fallback: String) -> String {
|
|
445
|
+
guard let data = output.data(using: .utf8),
|
|
446
|
+
let row = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
447
|
+
let error = row["error"] as? String else { return fallback }
|
|
448
|
+
switch error {
|
|
449
|
+
case "telegram_token_invalid", "telegram_token_stdin_empty":
|
|
450
|
+
return "That value is not a complete BotFather HTTP API token."
|
|
451
|
+
case let value where value.contains("401") || value.contains("Unauthorized"):
|
|
452
|
+
return "Telegram rejected this token. Copy the current token from @BotFather and try again."
|
|
453
|
+
case let value where value.contains("timeout") || value.contains("network"):
|
|
454
|
+
return "Telegram identity verification could not complete. Check the network connection and try again."
|
|
455
|
+
default:
|
|
456
|
+
return fallback
|
|
457
|
+
}
|
|
40
458
|
}
|
|
41
459
|
|
|
42
|
-
|
|
43
|
-
|
|
460
|
+
private func partialSetupRecovery(_ response: TelegramCenterSetupResponse?) -> String {
|
|
461
|
+
var parts: [String] = []
|
|
462
|
+
if response?.webhook_removed == true {
|
|
463
|
+
parts.append("The existing webhook was removed without dropping pending updates.")
|
|
464
|
+
}
|
|
465
|
+
if response?.bot_state_reset == true {
|
|
466
|
+
parts.append("Bot-scoped pairing and poll state were reset safely.")
|
|
467
|
+
}
|
|
468
|
+
if response?.token_stored == false {
|
|
469
|
+
parts.append("The token was not stored; rerun secure setup to finish.")
|
|
470
|
+
}
|
|
471
|
+
if let note = response?.recovery?.note, !note.isEmpty { parts.append(note) }
|
|
472
|
+
return parts.isEmpty
|
|
473
|
+
? "Review the secret-free recovery status, then rerun secure setup."
|
|
474
|
+
: parts.joined(separator: " ")
|
|
44
475
|
}
|
|
45
476
|
|
|
46
|
-
|
|
47
|
-
|
|
477
|
+
private func tokenSourceDescription(_ source: String) -> String {
|
|
478
|
+
switch source {
|
|
479
|
+
case "env": return "operator environment token"
|
|
480
|
+
case "user_secret_file": return "private token file"
|
|
481
|
+
case "none": return "no token source"
|
|
482
|
+
default: return "token source unknown"
|
|
483
|
+
}
|
|
48
484
|
}
|
|
49
485
|
}
|