sneakoscope 7.3.0 → 7.6.0
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 +129 -10
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/cli/codex-app-command.js +1 -1
- package/dist/cli/command-manifest-lite.js +4 -1
- package/dist/cli/command-registry.js +2 -0
- package/dist/cli/install-helpers-codex-lb-chain.js +42 -0
- package/dist/cli/install-helpers-codex-lb-config.js +114 -9
- package/dist/cli/install-helpers-install-support.js +6 -16
- package/dist/cli/install-helpers.js +788 -600
- package/dist/cli/router.js +30 -2
- package/dist/commands/codex-app.js +37 -1
- package/dist/commands/codex-lb.js +491 -300
- package/dist/config/skills-manifest.json +57 -71
- package/dist/core/align/align-route.js +127 -0
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +8 -4
- package/dist/core/codex-app/codex-app-restart.js +83 -0
- package/dist/core/codex-app/codex-app-ui-state-snapshot.js +8 -4
- package/dist/core/codex-app/desktop-routing-snapshot.js +1 -1
- package/dist/core/codex-app/menubar/constants.js +5 -0
- package/dist/core/codex-app/menubar/generation-transaction-journal.js +1 -3
- package/dist/core/codex-app/menubar/installer.js +18 -0
- package/dist/core/codex-app/multi-provider-router.js +22 -10
- package/dist/core/codex-app/openrouter-activate.js +26 -1
- package/dist/core/codex-app.js +118 -26
- package/dist/core/codex-control/codex-app-server-runtime-env.js +86 -0
- package/dist/core/codex-control/codex-app-server-v2-client.js +78 -8
- package/dist/core/codex-control/codex-task-runner.js +2 -2
- package/dist/core/codex-lb/capability-runner.js +204 -0
- package/dist/core/codex-lb/capability-types.js +1 -0
- package/dist/core/codex-lb/codex-lb-env.js +9 -13
- package/dist/core/codex-lb/codex-lb-setup.js +45 -8
- package/dist/core/codex-lb/codex-lb-tool-catalog.js +52 -74
- package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +1 -1
- package/dist/core/codex-lb/desktop-auth-invariant.js +185 -0
- package/dist/core/codex-lb/desktop-bridge/client.js +34 -0
- package/dist/core/codex-lb/desktop-bridge/header-policy.js +106 -0
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +101 -0
- package/dist/core/codex-lb/desktop-bridge/index.js +10 -0
- package/dist/core/codex-lb/desktop-bridge/launchd.js +81 -0
- package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +27 -0
- package/dist/core/codex-lb/desktop-bridge/security.js +274 -0
- package/dist/core/codex-lb/desktop-bridge/server.js +179 -0
- package/dist/core/codex-lb/desktop-bridge/state.js +119 -0
- package/dist/core/codex-lb/desktop-bridge/types.js +16 -0
- package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +151 -0
- package/dist/core/codex-lb/desktop-center-credentials.js +189 -0
- package/dist/core/codex-lb/desktop-controller.js +1410 -0
- package/dist/core/codex-lb/desktop-mode.js +32 -0
- package/dist/core/codex-lb/desktop-real-evidence.js +168 -0
- package/dist/core/codex-lb/desktop-service.js +613 -0
- package/dist/core/codex-lb/legacy-migration.js +575 -0
- package/dist/core/codex-lb/migration-receipt.js +246 -0
- package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +60 -0
- package/dist/core/codex-lb/probes/bridge-probe.js +75 -0
- package/dist/core/codex-lb/probes/catalog-probe.js +128 -0
- package/dist/core/codex-lb/probes/computer-use-probe.js +50 -0
- package/dist/core/codex-lb/probes/image-generation-probe.js +47 -0
- package/dist/core/codex-lb/probes/probe-evidence.js +39 -0
- package/dist/core/codex-lb/probes/voice-realtime-probe.js +44 -0
- package/dist/core/codex-lb/trusted-deep-evidence.js +403 -0
- package/dist/core/codex-native/core-skill-manifest.js +22 -6
- package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -1
- package/dist/core/commands/align-command.js +220 -0
- package/dist/core/commands/image-ux-review-command.js +280 -68
- package/dist/core/commands/naruto-command.js +257 -8
- package/dist/core/commands/ppt-command.js +2 -2
- package/dist/core/commands/telegram-command.js +402 -34
- package/dist/core/commands/triwiki-command.js +11 -1
- package/dist/core/commands/triwiki-graph-command.js +160 -0
- package/dist/core/commands/wiki-command.js +100 -20
- package/dist/core/doctor/skill-legacy-surface.js +1 -3
- package/dist/core/errors/message.js +3 -0
- package/dist/core/evidence/evidence-freshness.js +14 -0
- package/dist/core/feature-fixtures.js +9 -1
- package/dist/core/feature-registry.js +5 -3
- package/dist/core/fsx.js +7 -3
- package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
- package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
- package/dist/core/hooks-runtime/hook-context.js +15 -2
- package/dist/core/hooks-runtime/managed-skill-generation-heal.js +46 -0
- package/dist/core/hooks-runtime/naruto-terminal-finalization.js +1 -0
- package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
- package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
- package/dist/core/hooks-runtime.js +33 -89
- package/dist/core/image-ux-review/callout-extraction.js +6 -0
- package/dist/core/image-ux-review/imagegen-adapter.js +64 -23
- package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
- package/dist/core/image-ux-review.js +230 -12
- package/dist/core/imagegen/codex-lb-imagegen-target.js +21 -7
- package/dist/core/imagegen/imagegen-capability.js +10 -4
- package/dist/core/imagegen/require-imagegen.js +29 -5
- package/dist/core/init/skills.js +25 -13
- package/dist/core/init.js +1 -1
- package/dist/core/install/installed-package-smoke.js +157 -4
- package/dist/core/managed-assets/managed-assets-manifest.js +2 -1
- package/dist/core/managed-path-safety.js +8 -10
- package/dist/core/mcp-config/backup.js +2 -4
- package/dist/core/mcp-config/config-reader.js +2 -4
- package/dist/core/mcp-config/scope.js +3 -5
- package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
- package/dist/core/naruto/context-graph-advisor.js +0 -0
- package/dist/core/perf/release-latency-slo.js +12 -6
- package/dist/core/pipeline-internals/runtime-core.js +78 -0
- package/dist/core/pipeline-internals/runtime-gates.js +5 -2
- package/dist/core/policy/latest-version-guidance.js +113 -0
- package/dist/core/ppt.js +8 -8
- package/dist/core/preflight/parallel-preflight-engine.js +1 -1
- package/dist/core/proof/auto-finalize.js +98 -6
- package/dist/core/proof/route-proof-gate.js +12 -1
- package/dist/core/proof/route-proof-policy.js +8 -8
- package/dist/core/release/gate-manifest.js +32 -1
- package/dist/core/release/npm-stage-contract.js +21 -0
- package/dist/core/release/package-size-budget.js +2 -2
- package/dist/core/release/release-gate-contract.js +5 -0
- package/dist/core/release/release-proof-io.js +1 -3
- package/dist/core/release/release-real-contract.js +5 -4
- package/dist/core/release/release-stamp-proof.js +1 -3
- package/dist/core/release/stage-publish.js +27 -0
- package/dist/core/remote/audit-idempotency.js +50 -18
- package/dist/core/remote/codex-public-activity.js +283 -0
- package/dist/core/remote/index.js +1 -0
- package/dist/core/remote/ssh-worker-client.js +4 -1
- package/dist/core/remote/worker.js +206 -27
- package/dist/core/research/research-adversarial-review.js +1 -3
- package/dist/core/research/research-claim-synthesizer.js +1 -3
- package/dist/core/research/research-falsification-runner.js +1 -3
- package/dist/core/research/research-source-evidence.js +1 -3
- package/dist/core/responses-stream.js +65 -19
- package/dist/core/routes/constants.js +1 -1
- package/dist/core/routes/design-policy.js +7 -2
- package/dist/core/routes/dollar-manifest-lite.js +2 -4
- package/dist/core/routes/evidence.js +1 -1
- package/dist/core/routes.js +30 -10
- package/dist/core/safety/command-contract/contracts.js +2 -0
- package/dist/core/safety/command-contract/types.js +1 -1
- package/dist/core/search/context-graph-seeds.js +205 -0
- package/dist/core/search/context.js +0 -0
- package/dist/core/search/provider.js +2 -2
- package/dist/core/secret-redaction.js +6 -0
- package/dist/core/subagents/naruto-help-contract.js +2 -1
- package/dist/core/subagents/naruto-host-credentials.js +161 -0
- package/dist/core/subagents/official-subagent-preparation.js +4 -1
- package/dist/core/subagents/official-subagent-prompt.js +44 -2
- package/dist/core/subagents/official-subagent-runner.js +26 -8
- package/dist/core/subagents/triwiki-attention.js +135 -123
- package/dist/core/telegram/bot-api.js +85 -10
- package/dist/core/telegram/center-snapshot.js +718 -0
- package/dist/core/telegram/config.js +158 -31
- package/dist/core/telegram/hub.js +288 -33
- package/dist/core/telegram/index.js +2 -0
- package/dist/core/telegram/ingress-spool.js +419 -0
- package/dist/core/telegram/ledgers.js +188 -29
- package/dist/core/telegram/messages.js +26 -6
- package/dist/core/telegram/owner-lock.js +10 -7
- package/dist/core/telegram/runtime-projection.js +38 -3
- package/dist/core/telegram/runtime.js +298 -63
- package/dist/core/telegram/service.js +137 -8
- package/dist/core/telegram/setup.js +291 -73
- package/dist/core/text/strings.js +3 -0
- package/dist/core/triwiki/code-pack.js +12 -120
- package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
- package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
- package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
- package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
- package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
- package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
- package/dist/core/triwiki/context-graph/contracts.js +236 -0
- package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
- package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
- package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
- package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
- package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
- package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
- package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
- package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
- package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
- package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
- package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
- package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
- package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
- package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
- package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
- package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
- package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
- package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
- package/dist/core/triwiki/context-graph/graph-index.js +143 -0
- package/dist/core/triwiki/context-graph/ids.js +0 -0
- package/dist/core/triwiki/context-graph/lint/index.js +0 -0
- package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
- package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
- package/dist/core/triwiki/context-graph/paths.js +116 -0
- package/dist/core/triwiki/context-graph/profiles.js +78 -0
- package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
- package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
- package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
- package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
- package/dist/core/triwiki/context-graph/projections/index.js +7 -0
- package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
- package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
- package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
- package/dist/core/triwiki/context-graph/query/explain.js +0 -0
- package/dist/core/triwiki/context-graph/query/index.js +178 -0
- package/dist/core/triwiki/context-graph/query/load.js +116 -0
- package/dist/core/triwiki/context-graph/query/pack.js +117 -0
- package/dist/core/triwiki/context-graph/query/rank.js +142 -0
- package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
- package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
- package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
- package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
- package/dist/core/triwiki/context-graph/query-types.js +29 -0
- package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
- package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
- package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
- package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
- package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
- package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
- package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
- package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
- package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
- package/dist/core/verification/context-graph-affected.js +271 -0
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/AlertFactory.swift +11 -3
- package/dist/native/sks-menubar/Sources/AppDelegate.swift +9 -0
- package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +25 -3
- package/dist/native/sks-menubar/Sources/ControlKit.swift +19 -8
- package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -2
- package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +45 -17
- package/dist/native/sks-menubar/Sources/OverviewSummary.swift +162 -0
- package/dist/native/sks-menubar/Sources/OverviewViewController.swift +136 -190
- package/dist/native/sks-menubar/Sources/ProcessClient.swift +107 -11
- package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +5 -1
- package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +16 -4
- package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +4 -1
- package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +131 -0
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +288 -341
- package/dist/native/sks-menubar/Sources/RemoteTelegramCenterState.swift +447 -0
- package/dist/native/sks-menubar/Sources/RemoteTelegramRendering.swift +484 -0
- package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +364 -115
- package/dist/native/sks-menubar/Sources/SettingsViewController.swift +73 -12
- package/dist/native/sks-menubar/Sources/SidebarItem.swift +3 -3
- package/dist/native/sks-menubar/Sources/StatusItemController.swift +13 -13
- package/dist/native/sks-menubar/Sources/TelegramHubSupervisor.swift +469 -0
- package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +1 -6
- package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +12 -0
- package/dist/native/sks-menubar/Tests/NativeTelegramRegressionRunner.swift +55 -0
- package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +33 -0
- package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +142 -0
- package/dist/native/sks-menubar/Tests/RemoteTelegramCenterStateTests.swift +56 -0
- package/dist/native/sks-menubar/Tests/TelegramHubSupervisorTests.swift +55 -0
- package/dist/scripts/codex-lb-catalog-passthrough-check.js +120 -0
- package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +169 -0
- package/dist/scripts/codex-lb-desktop-bridge-check.js +394 -0
- package/dist/scripts/codex-lb-desktop-bridge-latency-check.js +196 -0
- package/dist/scripts/codex-lb-desktop-capabilities-check.js +287 -0
- package/dist/scripts/codex-lb-desktop-real-evidence-check.js +123 -0
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +10 -3
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +81 -40
- package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +99 -37
- package/dist/scripts/context-graph-check.js +191 -0
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +1 -1
- package/dist/scripts/gate-policy-audit-check.js +1 -1
- package/dist/scripts/helper-dedup-check.js +0 -2
- package/dist/scripts/installed-package-smoke-check.js +4 -1
- package/dist/scripts/latest-version-guidance-check.js +74 -0
- package/dist/scripts/release-metadata-check.js +21 -4
- package/dist/scripts/release-real-check.js +7 -0
- package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
- package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
- package/package.json +8 -3
- package/release-gates.v2.json +176 -1
- package/runtime-required-scripts.json +30 -0
- package/schemas/triwiki/context-graph.schema.json +119 -0
- package/dist/cli/install-helpers-codex-lb-selftest-chain.js +0 -112
- package/dist/cli/install-helpers-codex-lb-selftest.js +0 -481
- package/dist/core/triwiki/code-index-scanner.js +0 -309
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { isHelpRequest } from '../../cli/help.js';
|
|
3
|
-
import { globalSksRoot, projectRoot, readJson, readStdin } from '../fsx.js';
|
|
3
|
+
import { globalSksRoot, nowIso, projectRoot, readJson, readStdin, writeTextAtomic } from '../fsx.js';
|
|
4
4
|
import { RemoteCodexSessionBindingStore, loadRemoteMachineRegistry, loadRemoteSessionIndex, remoteCodexSessionBindingsPath, remoteMachineRegistryPath, remoteSessionIndexPath, validateRemoteMachineRegistry, validateRemoteSessionIndex } from '../remote/index.js';
|
|
5
|
-
import { loadTelegramConfig, resolveTelegramBotToken, TelegramActionBroker, TelegramAuditLedger, TelegramBotApiClient, TelegramHubRouter, TelegramHubRuntime, TelegramIdempotencyLedger, TelegramOwnerLock, TelegramMessageProjector, TelegramPollingHub, TelegramTopicRegistry, installAndStartTelegramHubService, restartTelegramHubService, setupTelegramLocalCoding, stopTelegramHubService, telegramHubPaths, telegramHubServiceStatus, telegramTokenFingerprint, validateTelegramConfig, validateTelegramPrivatePairing } from '../telegram/index.js';
|
|
5
|
+
import { loadTelegramConfig, probeTelegramBotReadiness, resolveTelegramBotToken, TelegramActionBroker, TelegramAuditLedger, TelegramBotApiClient, TelegramBotApiError, TelegramHubRouter, TelegramHubRuntime, TelegramIdempotencyLedger, TelegramIngressSpool, TelegramOwnerLock, TelegramMessageProjector, TelegramPollingHub, TelegramTopicRegistry, telegramCenterLiveSnapshot, telegramCenterSnapshot, installAndStartTelegramHubService, restartTelegramHubService, setupTelegramLocalCoding, stopTelegramHubService, telegramHubPaths, telegramHubServiceStatus, telegramRuntimeIdentityFingerprint, telegramTokenFingerprint, validateTelegramConfig, validateTelegramPrivatePairing } from '../telegram/index.js';
|
|
6
|
+
export const TELEGRAM_STATUS_NATIVE_DEADLINE_MS = 12_000;
|
|
7
|
+
export const TELEGRAM_STATUS_TOKEN_LOOKUP_TIMEOUT_MS = 3_000;
|
|
8
|
+
export const TELEGRAM_STATUS_BOT_REQUEST_TIMEOUT_MS = 1_500;
|
|
9
|
+
export const TELEGRAM_STATUS_BOT_MAX_RETRIES = 0;
|
|
10
|
+
export const TELEGRAM_STATUS_LIVE_PROBE_BUDGET_MS = TELEGRAM_STATUS_TOKEN_LOOKUP_TIMEOUT_MS
|
|
11
|
+
+ TELEGRAM_STATUS_BOT_REQUEST_TIMEOUT_MS * 2;
|
|
6
12
|
export async function telegramCommand(args = []) {
|
|
7
13
|
if (isHelpRequest(args)) {
|
|
8
14
|
printTelegramUsage();
|
|
@@ -17,6 +23,23 @@ export async function telegramCommand(args = []) {
|
|
|
17
23
|
if (action === 'status') {
|
|
18
24
|
return print(await telegramStatus(args, root, paths, configPath), json);
|
|
19
25
|
}
|
|
26
|
+
if (action === 'center-snapshot') {
|
|
27
|
+
const controllingRoot = path.resolve(readOption(args, '--project-root') ?? await projectRoot());
|
|
28
|
+
const sortDirection = telegramCenterSortDirection(readOption(args, '--sort-direction'));
|
|
29
|
+
return print(await telegramCenterSnapshot({
|
|
30
|
+
projectRoot: controllingRoot,
|
|
31
|
+
sessionId: readOption(args, '--session-id'),
|
|
32
|
+
cursor: readOption(args, '--cursor'),
|
|
33
|
+
sortDirection
|
|
34
|
+
}), json);
|
|
35
|
+
}
|
|
36
|
+
if (action === 'center-live') {
|
|
37
|
+
const controllingRoot = path.resolve(readOption(args, '--project-root') ?? await projectRoot());
|
|
38
|
+
return print(await telegramCenterLiveSnapshot({
|
|
39
|
+
projectRoot: controllingRoot,
|
|
40
|
+
sessionId: readOption(args, '--session-id')
|
|
41
|
+
}), json);
|
|
42
|
+
}
|
|
20
43
|
if (action === 'setup') {
|
|
21
44
|
if (!args.includes('--bot-token-stdin'))
|
|
22
45
|
return fail('bot_token_stdin_required', ['setup --bot-token-stdin --project-root <path>'], json);
|
|
@@ -36,19 +59,83 @@ export async function telegramCommand(args = []) {
|
|
|
36
59
|
const validation = validateTelegramConfig(await readJson(configPath, null));
|
|
37
60
|
return print({ schema: 'sks.telegram-config-validation.v1', ...validation, config: validation.config ? redactConfig(validation.config) : null }, json);
|
|
38
61
|
}
|
|
62
|
+
if (action === 'dead-letters') {
|
|
63
|
+
const request = telegramDeadLetterCommandRequest(args);
|
|
64
|
+
const config = await loadTelegramConfig(configPath);
|
|
65
|
+
const token = await resolveTelegramBotToken(config.bot_token_ref);
|
|
66
|
+
const tokenFingerprint = telegramTokenFingerprint(token);
|
|
67
|
+
const ingress = new TelegramIngressSpool(paths.ingress, {
|
|
68
|
+
identityFingerprint: telegramRuntimeIdentityFingerprint(config, tokenFingerprint),
|
|
69
|
+
legacyTokenFingerprint: tokenFingerprint
|
|
70
|
+
});
|
|
71
|
+
if (request.action === 'list' || request.action === 'inspect') {
|
|
72
|
+
const deadLetters = await ingress.deadLetters(request.updateId ?? undefined);
|
|
73
|
+
if (request.action === 'inspect' && deadLetters.length === 0) {
|
|
74
|
+
throw new Error('telegram_ingress_dead_letter_not_found');
|
|
75
|
+
}
|
|
76
|
+
return print({
|
|
77
|
+
schema: 'sks.telegram-ingress-dead-letter-list.v1',
|
|
78
|
+
ok: true,
|
|
79
|
+
unhealthy: deadLetters.length > 0,
|
|
80
|
+
dead_letters: deadLetters,
|
|
81
|
+
next_action: deadLetters.length > 0
|
|
82
|
+
? 'Stop the Hub, then explicitly requeue only requeue_safe entries or acknowledge entries whose effect may be ambiguous.'
|
|
83
|
+
: 'No dead-lettered Telegram ingress remains.'
|
|
84
|
+
}, json);
|
|
85
|
+
}
|
|
86
|
+
const controllingRoot = path.resolve(readOption(args, '--project-root') ?? await projectRoot());
|
|
87
|
+
const service = await telegramHubServiceStatus({
|
|
88
|
+
projectRoot: controllingRoot,
|
|
89
|
+
globalRoot: root
|
|
90
|
+
});
|
|
91
|
+
if (service.running)
|
|
92
|
+
throw new Error('telegram_hub_must_be_stopped_before_dead_letter_recovery');
|
|
93
|
+
if (!request.confirmed)
|
|
94
|
+
throw new Error('telegram_dead_letter_recovery_confirmation_required');
|
|
95
|
+
if (request.action === 'requeue') {
|
|
96
|
+
const requeued = await ingress.requeueDeadLetter(request.updateId);
|
|
97
|
+
const counts = await ingress.counts();
|
|
98
|
+
await updateTelegramIngressHealth(paths.receive_health, counts);
|
|
99
|
+
return print({
|
|
100
|
+
schema: 'sks.telegram-ingress-recovery.v1',
|
|
101
|
+
ok: true,
|
|
102
|
+
disposition: 'requeued',
|
|
103
|
+
update_id: requeued.update_id,
|
|
104
|
+
replayed: false,
|
|
105
|
+
pending_ingress: counts.pending,
|
|
106
|
+
dead_letter_ingress: counts.dead_letters,
|
|
107
|
+
next_action: 'Restart the Telegram Hub to process the explicitly requeued request.'
|
|
108
|
+
}, json);
|
|
109
|
+
}
|
|
110
|
+
const acknowledged = await ingress.acknowledgeDeadLetters(request.updateId === null ? undefined : [request.updateId]);
|
|
111
|
+
const counts = await ingress.counts();
|
|
112
|
+
await updateTelegramIngressHealth(paths.receive_health, counts);
|
|
113
|
+
return print({
|
|
114
|
+
schema: 'sks.telegram-ingress-recovery.v1',
|
|
115
|
+
ok: true,
|
|
116
|
+
disposition: 'acknowledged',
|
|
117
|
+
acknowledged_dead_letters: acknowledged,
|
|
118
|
+
acknowledged_update_id: request.updateId,
|
|
119
|
+
replayed: false,
|
|
120
|
+
resend_required: acknowledged > 0,
|
|
121
|
+
next_action: acknowledged > 0
|
|
122
|
+
? 'Restart the Telegram Hub, then resend each failed request from the paired private chat.'
|
|
123
|
+
: 'No dead-lettered Telegram ingress remained.'
|
|
124
|
+
}, json);
|
|
125
|
+
}
|
|
39
126
|
if (action === 'hub') {
|
|
40
127
|
const hubAction = args[1] && !String(args[1]).startsWith('-') ? String(args[1]) : 'run';
|
|
41
128
|
const controllingRoot = path.resolve(readOption(args, '--project-root') ?? await projectRoot());
|
|
42
129
|
if (hubAction === 'start') {
|
|
43
130
|
await assertHubSetupReady(controllingRoot, root, configPath);
|
|
44
|
-
return print(await installAndStartTelegramHubService({ projectRoot: controllingRoot, globalRoot: root }), json);
|
|
131
|
+
return print(markTelegramHubMutationResult(await installAndStartTelegramHubService({ projectRoot: controllingRoot, globalRoot: root })), json);
|
|
45
132
|
}
|
|
46
133
|
if (hubAction === 'stop') {
|
|
47
|
-
return print(await stopTelegramHubService({ projectRoot: controllingRoot, globalRoot: root }), json);
|
|
134
|
+
return print(markTelegramHubMutationResult(await stopTelegramHubService({ projectRoot: controllingRoot, globalRoot: root })), json);
|
|
48
135
|
}
|
|
49
136
|
if (hubAction === 'restart') {
|
|
50
137
|
await assertHubSetupReady(controllingRoot, root, configPath);
|
|
51
|
-
return print(await restartTelegramHubService({ projectRoot: controllingRoot, globalRoot: root }), json);
|
|
138
|
+
return print(markTelegramHubMutationResult(await restartTelegramHubService({ projectRoot: controllingRoot, globalRoot: root })), json);
|
|
52
139
|
}
|
|
53
140
|
if (hubAction === 'status') {
|
|
54
141
|
return print(await telegramHubServiceStatus({ projectRoot: controllingRoot, globalRoot: root }), json);
|
|
@@ -59,7 +146,7 @@ export async function telegramCommand(args = []) {
|
|
|
59
146
|
}
|
|
60
147
|
}
|
|
61
148
|
catch (err) {
|
|
62
|
-
process.exitCode = 1;
|
|
149
|
+
process.exitCode = action === 'hub' && args.includes('--service-managed') ? 0 : 1;
|
|
63
150
|
return print({
|
|
64
151
|
schema: 'sks.telegram-command-error.v1',
|
|
65
152
|
ok: false,
|
|
@@ -68,8 +155,13 @@ export async function telegramCommand(args = []) {
|
|
|
68
155
|
}
|
|
69
156
|
return fail('unknown_action', [
|
|
70
157
|
'status',
|
|
158
|
+
'center-snapshot [--session-id <id>] [--cursor <opaque>] [--sort-direction asc|desc] --project-root <path>',
|
|
159
|
+
'center-live [--session-id <id>] --project-root <path>',
|
|
71
160
|
'setup --bot-token-stdin --project-root <path>',
|
|
72
161
|
'validate-config',
|
|
162
|
+
'dead-letters list|inspect --project-root <path>',
|
|
163
|
+
'dead-letters requeue --update-id <id> --yes --project-root <path>',
|
|
164
|
+
'dead-letters acknowledge [--update-id <id>] --yes --project-root <path>',
|
|
73
165
|
'hub run|start|stop|restart|status'
|
|
74
166
|
], json);
|
|
75
167
|
}
|
|
@@ -79,6 +171,7 @@ async function telegramStatus(args, root, paths, configPath) {
|
|
|
79
171
|
const owner = await readJson(paths.owner, null);
|
|
80
172
|
const topics = await new TelegramTopicRegistry(paths.topics).list();
|
|
81
173
|
const controllingRoot = path.resolve(readOption(args, '--project-root') ?? await projectRoot());
|
|
174
|
+
const servicePromise = telegramHubServiceStatus({ projectRoot: controllingRoot, globalRoot: root });
|
|
82
175
|
const machineRegistryRaw = await readJson(readOption(args, '--machines') ?? remoteMachineRegistryPath(root), null);
|
|
83
176
|
const machineValidation = validateRemoteMachineRegistry(machineRegistryRaw);
|
|
84
177
|
const sessionIndexRaw = await readJson(readOption(args, '--session-index') ?? remoteSessionIndexPath(controllingRoot), null);
|
|
@@ -90,29 +183,63 @@ async function telegramStatus(args, root, paths, configPath) {
|
|
|
90
183
|
const registered = bindings.filter((binding) => targets.some((target) => (binding.machine_id === target.machine_id
|
|
91
184
|
&& binding.project_id === target.project_id
|
|
92
185
|
&& path.resolve(binding.project_root) === path.resolve(target.project_root))));
|
|
93
|
-
const service = await telegramHubServiceStatus({ projectRoot: controllingRoot, globalRoot: root });
|
|
94
186
|
let tokenConfigured = false;
|
|
95
|
-
|
|
96
|
-
|
|
187
|
+
let liveProbe = telegramLiveProbeStatus(null);
|
|
188
|
+
const probeConfig = validation.config;
|
|
189
|
+
if (probeConfig) {
|
|
190
|
+
const token = await resolveTelegramBotToken(probeConfig.bot_token_ref, {
|
|
191
|
+
timeoutMs: TELEGRAM_STATUS_TOKEN_LOOKUP_TIMEOUT_MS
|
|
192
|
+
}).catch(() => null);
|
|
193
|
+
tokenConfigured = token !== null;
|
|
194
|
+
if (token) {
|
|
195
|
+
try {
|
|
196
|
+
liveProbe = telegramLiveProbeStatus(await probeTelegramBotReadiness(new TelegramBotApiClient(token, {
|
|
197
|
+
timeoutMs: TELEGRAM_STATUS_BOT_REQUEST_TIMEOUT_MS,
|
|
198
|
+
maxRetries: TELEGRAM_STATUS_BOT_MAX_RETRIES
|
|
199
|
+
})));
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
liveProbe = telegramLiveProbeStatus(null, error);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
97
205
|
}
|
|
206
|
+
const service = await servicePromise;
|
|
98
207
|
const pairing = telegramPairingReadiness(rawConfig);
|
|
99
208
|
const blockers = [
|
|
100
|
-
...validation.issues.map((issue) =>
|
|
209
|
+
...validation.issues.map((issue) => (issue === 'telegram_identity_migration_required'
|
|
210
|
+
? issue
|
|
211
|
+
: `config:${issue}`)),
|
|
101
212
|
...machineValidation.issues.map((issue) => `machine:${issue}`),
|
|
102
213
|
...sessionValidation.issues.map((issue) => `target:${issue}`),
|
|
103
214
|
...(tokenConfigured ? [] : ['telegram_token_not_available']),
|
|
215
|
+
...(liveProbe.blocker ? [liveProbe.blocker] : []),
|
|
104
216
|
...(pairing.blocker ? [pairing.blocker] : []),
|
|
105
217
|
...(registered.length ? [] : ['no_registered_codex_session']),
|
|
106
|
-
...(service.running
|
|
218
|
+
...(service.running
|
|
219
|
+
? service.dead_letter_ingress > 0
|
|
220
|
+
? ['telegram_ingress_dead_letter_present']
|
|
221
|
+
: service.receive_ready ? [] : ['telegram_receive_not_ready']
|
|
222
|
+
: ['telegram_hub_not_running'])
|
|
107
223
|
];
|
|
108
224
|
return {
|
|
109
225
|
schema: 'sks.telegram-status.v1',
|
|
110
226
|
ok: blockers.length === 0,
|
|
111
227
|
configured: rawConfig !== null,
|
|
112
228
|
token_configured: tokenConfigured,
|
|
229
|
+
bot_verified: liveProbe.bot_verified,
|
|
230
|
+
bot: liveProbe.bot,
|
|
231
|
+
webhook_configured: liveProbe.webhook_configured,
|
|
232
|
+
pending_update_count: liveProbe.pending_update_count,
|
|
233
|
+
long_poll_ready: liveProbe.long_poll_ready,
|
|
234
|
+
telegram_probe_error: liveProbe.telegram_probe_error,
|
|
113
235
|
pairing_valid: pairing.pairing_valid,
|
|
114
236
|
pairing_issues: pairing.pairing_issues,
|
|
115
237
|
hub_running: service.running,
|
|
238
|
+
hub_ready: service.receive_ready,
|
|
239
|
+
receive_state: service.receive_state,
|
|
240
|
+
last_successful_poll_at: service.last_successful_poll_at,
|
|
241
|
+
pending_ingress: service.pending_ingress,
|
|
242
|
+
dead_letter_ingress: service.dead_letter_ingress,
|
|
116
243
|
service,
|
|
117
244
|
config_issues: validation.issues,
|
|
118
245
|
owner: owner ? {
|
|
@@ -135,7 +262,8 @@ async function telegramStatus(args, root, paths, configPath) {
|
|
|
135
262
|
updated_at: binding.updated_at
|
|
136
263
|
})),
|
|
137
264
|
remote_config_issues: [...machineValidation.issues, ...sessionValidation.issues],
|
|
138
|
-
blockers
|
|
265
|
+
blockers,
|
|
266
|
+
next_actions: telegramNextActions(blockers)
|
|
139
267
|
};
|
|
140
268
|
}
|
|
141
269
|
function printTelegramUsage() {
|
|
@@ -143,49 +271,94 @@ function printTelegramUsage() {
|
|
|
143
271
|
|
|
144
272
|
Usage:
|
|
145
273
|
sks telegram status [--project-root <path>] [--json]
|
|
274
|
+
sks telegram center-snapshot --project-root <path> [--session-id <id>]
|
|
275
|
+
[--cursor <opaque>] [--sort-direction asc|desc] [--json]
|
|
276
|
+
sks telegram center-live --project-root <path> [--session-id <id>] [--json]
|
|
146
277
|
sks telegram setup --bot-token-stdin --project-root <path> [--paired-chat-id <id>]
|
|
147
278
|
[--paired-user-id <id>] [--new-session] [--json]
|
|
148
279
|
sks telegram validate-config [--config <path>] [--json]
|
|
280
|
+
sks telegram dead-letters list --project-root <path> [--json]
|
|
281
|
+
sks telegram dead-letters inspect --update-id <id> --project-root <path> [--json]
|
|
282
|
+
sks telegram dead-letters requeue --update-id <id> --yes --project-root <path> [--json]
|
|
283
|
+
sks telegram dead-letters acknowledge [--update-id <id>] --yes --project-root <path> [--json]
|
|
149
284
|
sks telegram hub start|stop|restart|status [--project-root <path>] [--json]
|
|
150
285
|
sks telegram hub run [--project-root <path>] [--json]
|
|
151
286
|
|
|
152
287
|
Pairing (do these first):
|
|
153
|
-
1.
|
|
288
|
+
1. Open @BotFather, send /newbot, choose a name and a unique username ending in "bot",
|
|
289
|
+
then copy the HTTP API token BotFather returns.
|
|
154
290
|
2. Send /start to that bot from the Telegram account you want to pair.
|
|
155
291
|
3. printf '%s' "<token>" | sks telegram setup --bot-token-stdin --project-root "$PWD" --json
|
|
156
292
|
The token is read from stdin only and stored in the macOS Keychain.
|
|
157
293
|
4. sks telegram hub start --project-root "$PWD" --json
|
|
158
294
|
|
|
295
|
+
If status reports telegram_webhook_conflict, remove the webhook in the service
|
|
296
|
+
that configured it, then retry. SKS does not delete external webhook state implicitly.
|
|
297
|
+
If setup or the hub reports telegram_409_conflict, stop the other poller using
|
|
298
|
+
this bot token before retrying.
|
|
299
|
+
Stop the SKS Telegram hub before rerunning setup or rotating its BotFather token.
|
|
300
|
+
If status reports telegram_ingress_dead_letter_present, inspect the bounded list,
|
|
301
|
+
stop the Hub, then explicitly requeue only entries marked requeue_safe or acknowledge
|
|
302
|
+
an ambiguous entry and resend it from the paired private chat. Requeue never executes
|
|
303
|
+
until the Hub is separately restarted, and ambiguous effects are never replayed.
|
|
304
|
+
|
|
159
305
|
The same flow is available in SKS Center → Remote & Telegram.
|
|
306
|
+
Center uses center-snapshot for stored conversation pages and center-live for the
|
|
307
|
+
bounded local event journal. Center checks the journal files every 2.5 seconds and
|
|
308
|
+
runs center-live only after a change; it never starts Codex App Server for that path.
|
|
309
|
+
The default page is Latest. Use Older, Newer, and Latest in Center, or pass a
|
|
310
|
+
returned cursor with its paired sort direction, to browse every stored turn
|
|
311
|
+
without resuming the Codex thread.
|
|
312
|
+
Reasoning text, raw tool output, and credentials are excluded from that projection.
|
|
160
313
|
See docs/telegram-and-center.md for the full guide and troubleshooting.
|
|
161
314
|
`);
|
|
162
315
|
}
|
|
163
316
|
export function telegramPairingReadiness(value) {
|
|
164
317
|
const pairing = validateTelegramPrivatePairing(value);
|
|
318
|
+
const multipleIds = hasMultipleTelegramPairingIds(value);
|
|
165
319
|
return {
|
|
166
320
|
pairing_valid: pairing.ok,
|
|
167
321
|
pairing_issues: pairing.issues,
|
|
168
322
|
blocker: pairing.ok
|
|
169
323
|
? null
|
|
170
|
-
:
|
|
171
|
-
? '
|
|
172
|
-
:
|
|
324
|
+
: multipleIds
|
|
325
|
+
? 'telegram_pairing_multiple_ids_requires_setup'
|
|
326
|
+
: pairing.missing
|
|
327
|
+
? 'telegram_pairing_missing'
|
|
328
|
+
: `telegram_pairing_invalid:${pairing.issues.join(',')}`
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
export function telegramLiveProbeStatus(readiness, error = null) {
|
|
332
|
+
const probeError = error === null ? null : telegramProbeFailure(error);
|
|
333
|
+
const webhookConfigured = readiness?.webhook_configured === true;
|
|
334
|
+
return {
|
|
335
|
+
bot_verified: readiness !== null,
|
|
336
|
+
bot: readiness?.bot ?? null,
|
|
337
|
+
webhook_configured: webhookConfigured,
|
|
338
|
+
pending_update_count: readiness?.pending_update_count ?? 0,
|
|
339
|
+
long_poll_ready: readiness !== null && !webhookConfigured,
|
|
340
|
+
telegram_probe_error: probeError,
|
|
341
|
+
blocker: probeError ?? (webhookConfigured ? 'telegram_webhook_conflict' : null)
|
|
173
342
|
};
|
|
174
343
|
}
|
|
175
344
|
async function runHub(args, root, paths, configPath, json) {
|
|
345
|
+
const serviceManaged = args.includes('--service-managed');
|
|
346
|
+
const centerSupervisorPid = telegramCenterSupervisorPid(args);
|
|
176
347
|
const config = await loadTelegramConfig(configPath);
|
|
177
348
|
const controllingRoot = path.resolve(readOption(args, '--project-root') ?? await projectRoot());
|
|
178
349
|
const machineRegistry = await loadRemoteMachineRegistry(readOption(args, '--machines') ?? remoteMachineRegistryPath(root));
|
|
179
350
|
const sessionIndex = await loadRemoteSessionIndex(readOption(args, '--session-index') ?? remoteSessionIndexPath(controllingRoot), machineRegistry);
|
|
180
351
|
const token = await resolveTelegramBotToken(config.bot_token_ref);
|
|
181
352
|
const fingerprint = telegramTokenFingerprint(token);
|
|
353
|
+
const identityFingerprint = telegramRuntimeIdentityFingerprint(config, fingerprint);
|
|
182
354
|
const owner = new TelegramOwnerLock({
|
|
183
355
|
lockPath: paths.owner,
|
|
184
356
|
tokenFingerprint: fingerprint,
|
|
357
|
+
reclaimDeadImmediately: centerSupervisorPid !== null,
|
|
185
358
|
...(config.owner_stale_ms === undefined ? {} : { staleMs: config.owner_stale_ms })
|
|
186
359
|
});
|
|
187
360
|
await owner.acquire();
|
|
188
|
-
const topics = new TelegramTopicRegistry(paths.topics);
|
|
361
|
+
const topics = new TelegramTopicRegistry(paths.topics, { identityFingerprint });
|
|
189
362
|
const actions = new TelegramActionBroker(paths.actions);
|
|
190
363
|
const audit = new TelegramAuditLedger(paths.audit, fingerprint);
|
|
191
364
|
const router = new TelegramHubRouter({
|
|
@@ -196,6 +369,10 @@ async function runHub(args, root, paths, configPath, json) {
|
|
|
196
369
|
audit
|
|
197
370
|
});
|
|
198
371
|
const api = new TelegramBotApiClient(token, { timeoutMs: (config.long_poll_timeout_sec ?? 25) * 1000 + 5_000 });
|
|
372
|
+
const ingress = new TelegramIngressSpool(paths.ingress, {
|
|
373
|
+
identityFingerprint,
|
|
374
|
+
legacyTokenFingerprint: fingerprint
|
|
375
|
+
});
|
|
199
376
|
const runtime = new TelegramHubRuntime({
|
|
200
377
|
config,
|
|
201
378
|
router,
|
|
@@ -213,37 +390,102 @@ async function runHub(args, root, paths, configPath, json) {
|
|
|
213
390
|
}),
|
|
214
391
|
machineRegistry,
|
|
215
392
|
sessionIndex,
|
|
216
|
-
projectionStatePath: paths.projection
|
|
393
|
+
projectionStatePath: paths.projection,
|
|
394
|
+
ingress
|
|
395
|
+
});
|
|
396
|
+
const polling = new TelegramPollingHub(api, runtime, owner, config.long_poll_timeout_sec ?? 25, {
|
|
397
|
+
ingress,
|
|
398
|
+
healthPath: paths.receive_health
|
|
217
399
|
});
|
|
218
|
-
const
|
|
400
|
+
const controller = new AbortController();
|
|
401
|
+
const stop = () => controller.abort();
|
|
402
|
+
const supervisorMonitor = centerSupervisorPid === null
|
|
403
|
+
? null
|
|
404
|
+
: setInterval(() => {
|
|
405
|
+
if (!telegramCenterSupervisorAlive(centerSupervisorPid))
|
|
406
|
+
controller.abort();
|
|
407
|
+
}, 250);
|
|
408
|
+
supervisorMonitor?.unref();
|
|
409
|
+
process.once('SIGINT', stop);
|
|
410
|
+
process.once('SIGTERM', stop);
|
|
219
411
|
try {
|
|
220
412
|
await polling.ensureLongPollingAllowed();
|
|
221
|
-
const
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
|
|
413
|
+
const receive = await polling.establishReadiness(controller.signal);
|
|
414
|
+
if (receive.stopped_reason === 'aborted') {
|
|
415
|
+
return print({
|
|
416
|
+
schema: 'sks.telegram-hub-run.v1',
|
|
417
|
+
ok: true,
|
|
418
|
+
processed: 0,
|
|
419
|
+
stopped_reason: 'aborted',
|
|
420
|
+
receive,
|
|
421
|
+
sync: null
|
|
422
|
+
}, json);
|
|
225
423
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
424
|
+
if (!receive.ok) {
|
|
425
|
+
return print(markTelegramHubMutationResult({
|
|
426
|
+
schema: 'sks.telegram-hub-run.v1',
|
|
427
|
+
ok: false,
|
|
428
|
+
processed: 0,
|
|
429
|
+
stopped_reason: receive.stopped_reason ?? 'receive_not_ready',
|
|
430
|
+
receive,
|
|
431
|
+
sync: null
|
|
432
|
+
}, { setExitCode: !serviceManaged }), json);
|
|
233
433
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
434
|
+
const sync = await runtime.initialize();
|
|
435
|
+
if (controller.signal.aborted) {
|
|
436
|
+
return print({
|
|
437
|
+
schema: 'sks.telegram-hub-run.v1',
|
|
438
|
+
ok: true,
|
|
439
|
+
processed: receive.received,
|
|
440
|
+
stopped_reason: 'aborted',
|
|
441
|
+
receive,
|
|
442
|
+
sync
|
|
443
|
+
}, json);
|
|
237
444
|
}
|
|
445
|
+
await polling.drainPending();
|
|
446
|
+
if (args.includes('--once')) {
|
|
447
|
+
await polling.waitForIdle();
|
|
448
|
+
return print({
|
|
449
|
+
schema: 'sks.telegram-hub-run.v1',
|
|
450
|
+
ok: true,
|
|
451
|
+
processed: receive.received,
|
|
452
|
+
stopped_reason: null,
|
|
453
|
+
receive,
|
|
454
|
+
sync
|
|
455
|
+
}, json);
|
|
456
|
+
}
|
|
457
|
+
const result = await polling.run(controller.signal);
|
|
458
|
+
return print(markTelegramHubMutationResult({
|
|
459
|
+
schema: 'sks.telegram-hub-run.v1',
|
|
460
|
+
...result,
|
|
461
|
+
receive,
|
|
462
|
+
sync
|
|
463
|
+
}, { setExitCode: !serviceManaged }), json);
|
|
238
464
|
}
|
|
239
465
|
finally {
|
|
466
|
+
if (supervisorMonitor)
|
|
467
|
+
clearInterval(supervisorMonitor);
|
|
468
|
+
process.off('SIGINT', stop);
|
|
469
|
+
process.off('SIGTERM', stop);
|
|
240
470
|
await runtime.close();
|
|
241
471
|
await owner.release();
|
|
242
472
|
}
|
|
243
473
|
}
|
|
244
474
|
async function assertHubSetupReady(controllingRoot, globalRoot, configPath) {
|
|
245
475
|
const config = await loadTelegramConfig(configPath);
|
|
246
|
-
await resolveTelegramBotToken(config.bot_token_ref);
|
|
476
|
+
const token = await resolveTelegramBotToken(config.bot_token_ref);
|
|
477
|
+
let readiness;
|
|
478
|
+
try {
|
|
479
|
+
readiness = await probeTelegramBotReadiness(new TelegramBotApiClient(token, {
|
|
480
|
+
timeoutMs: 8_000,
|
|
481
|
+
maxRetries: 1
|
|
482
|
+
}));
|
|
483
|
+
}
|
|
484
|
+
catch (error) {
|
|
485
|
+
throw new Error(telegramProbeFailure(error));
|
|
486
|
+
}
|
|
487
|
+
if (readiness.webhook_configured)
|
|
488
|
+
throw new Error('telegram_webhook_conflict');
|
|
247
489
|
const registry = await loadRemoteMachineRegistry(remoteMachineRegistryPath(globalRoot));
|
|
248
490
|
const index = await loadRemoteSessionIndex(remoteSessionIndexPath(controllingRoot), registry);
|
|
249
491
|
const bindings = await new RemoteCodexSessionBindingStore(remoteCodexSessionBindingsPath(controllingRoot)).list();
|
|
@@ -253,10 +495,82 @@ async function assertHubSetupReady(controllingRoot, globalRoot, configPath) {
|
|
|
253
495
|
throw new Error('no_registered_codex_session');
|
|
254
496
|
}
|
|
255
497
|
}
|
|
498
|
+
export function markTelegramHubMutationResult(result, options = {}) {
|
|
499
|
+
if (result.ok !== true && options.setExitCode !== false)
|
|
500
|
+
process.exitCode = 1;
|
|
501
|
+
return result;
|
|
502
|
+
}
|
|
503
|
+
export function telegramCenterSupervisorPid(args, actualParentPid = process.ppid) {
|
|
504
|
+
const supervisedCount = args.filter((value) => value === '--center-supervised').length;
|
|
505
|
+
const pidOptionCount = args.filter((value) => value === '--center-supervisor-pid').length;
|
|
506
|
+
if (supervisedCount === 0 && pidOptionCount === 0)
|
|
507
|
+
return null;
|
|
508
|
+
if (supervisedCount !== 1
|
|
509
|
+
|| pidOptionCount !== 1
|
|
510
|
+
|| args.includes('--service-managed')) {
|
|
511
|
+
throw new Error('telegram_center_supervisor_invalid');
|
|
512
|
+
}
|
|
513
|
+
const rawPid = readOption(args, '--center-supervisor-pid');
|
|
514
|
+
if (!rawPid || !/^[1-9]\d*$/.test(rawPid))
|
|
515
|
+
throw new Error('telegram_center_supervisor_invalid');
|
|
516
|
+
const pid = Number(rawPid);
|
|
517
|
+
if (!Number.isSafeInteger(pid) || pid !== actualParentPid) {
|
|
518
|
+
throw new Error('telegram_center_supervisor_invalid');
|
|
519
|
+
}
|
|
520
|
+
return pid;
|
|
521
|
+
}
|
|
522
|
+
export function telegramCenterSupervisorAlive(pid, probe = (candidate) => process.kill(candidate, 0)) {
|
|
523
|
+
try {
|
|
524
|
+
probe(pid);
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
catch (error) {
|
|
528
|
+
return error?.code !== 'ESRCH';
|
|
529
|
+
}
|
|
530
|
+
}
|
|
256
531
|
function readOption(args, name) {
|
|
257
532
|
const index = args.indexOf(name);
|
|
258
533
|
return index >= 0 && args[index + 1] ? String(args[index + 1]) : null;
|
|
259
534
|
}
|
|
535
|
+
export function telegramDeadLetterCommandRequest(args) {
|
|
536
|
+
const rawAction = args[1] && !String(args[1]).startsWith('-')
|
|
537
|
+
? String(args[1])
|
|
538
|
+
: 'list';
|
|
539
|
+
const action = rawAction === 'status' ? 'list' : rawAction;
|
|
540
|
+
if (!['list', 'inspect', 'requeue', 'acknowledge'].includes(action)) {
|
|
541
|
+
throw new Error('unknown_dead_letter_action');
|
|
542
|
+
}
|
|
543
|
+
const updateId = readTelegramUpdateId(args);
|
|
544
|
+
if ((action === 'inspect' || action === 'requeue') && updateId === null) {
|
|
545
|
+
throw new Error('telegram_dead_letter_update_id_required');
|
|
546
|
+
}
|
|
547
|
+
return {
|
|
548
|
+
action: action,
|
|
549
|
+
updateId,
|
|
550
|
+
confirmed: args.includes('--yes')
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
function readTelegramUpdateId(args) {
|
|
554
|
+
const optionIndexes = args.flatMap((value, index) => value === '--update-id' ? [index] : []);
|
|
555
|
+
if (optionIndexes.length === 0)
|
|
556
|
+
return null;
|
|
557
|
+
if (optionIndexes.length !== 1)
|
|
558
|
+
throw new Error('telegram_dead_letter_update_id_invalid');
|
|
559
|
+
const raw = args[optionIndexes[0] + 1];
|
|
560
|
+
if (!raw || !/^\d+$/.test(raw))
|
|
561
|
+
throw new Error('telegram_dead_letter_update_id_invalid');
|
|
562
|
+
const updateId = Number(raw);
|
|
563
|
+
if (!Number.isSafeInteger(updateId))
|
|
564
|
+
throw new Error('telegram_dead_letter_update_id_invalid');
|
|
565
|
+
return updateId;
|
|
566
|
+
}
|
|
567
|
+
function telegramCenterSortDirection(value) {
|
|
568
|
+
if (value === null)
|
|
569
|
+
return null;
|
|
570
|
+
if (value === 'asc' || value === 'desc')
|
|
571
|
+
return value;
|
|
572
|
+
throw new Error('telegram_center_sort_direction_invalid');
|
|
573
|
+
}
|
|
260
574
|
function redactConfig(config) {
|
|
261
575
|
const record = config;
|
|
262
576
|
return {
|
|
@@ -266,6 +580,17 @@ function redactConfig(config) {
|
|
|
266
580
|
: null
|
|
267
581
|
};
|
|
268
582
|
}
|
|
583
|
+
async function updateTelegramIngressHealth(healthPath, counts) {
|
|
584
|
+
const receiveHealth = await readJson(healthPath, null);
|
|
585
|
+
if (receiveHealth?.schema !== 'sks.telegram-receive-health.v1')
|
|
586
|
+
return;
|
|
587
|
+
await writeTextAtomic(healthPath, `${JSON.stringify({
|
|
588
|
+
...receiveHealth,
|
|
589
|
+
updated_at: nowIso(),
|
|
590
|
+
pending_ingress: counts.pending,
|
|
591
|
+
dead_letter_ingress: counts.dead_letters
|
|
592
|
+
}, null, 2)}\n`, { mode: 0o600 });
|
|
593
|
+
}
|
|
269
594
|
function print(value, json) {
|
|
270
595
|
if (json)
|
|
271
596
|
console.log(JSON.stringify(value, null, 2));
|
|
@@ -284,3 +609,46 @@ function publicError(err) {
|
|
|
284
609
|
.replace(/(?:\/Users|\/home)\/[^\s]+/g, '[path-redacted]')
|
|
285
610
|
.slice(0, 500);
|
|
286
611
|
}
|
|
612
|
+
function telegramProbeFailure(error) {
|
|
613
|
+
return error instanceof TelegramBotApiError && (error.errorCode === 401 || error.errorCode === 404)
|
|
614
|
+
? 'telegram_bot_auth_failed'
|
|
615
|
+
: 'telegram_bot_probe_failed';
|
|
616
|
+
}
|
|
617
|
+
export function telegramNextActions(blockers) {
|
|
618
|
+
const actions = [];
|
|
619
|
+
if (blockers.includes('telegram_identity_migration_required')) {
|
|
620
|
+
actions.push('Stop the Hub, then reconnect this project once so SKS writes the v2 bot/pairing identity contract.');
|
|
621
|
+
}
|
|
622
|
+
if (blockers.includes('telegram_token_not_available') || blockers.includes('telegram_bot_auth_failed')) {
|
|
623
|
+
actions.push('Create or regenerate the bot token in @BotFather, then rerun setup with --bot-token-stdin.');
|
|
624
|
+
}
|
|
625
|
+
if (blockers.includes('telegram_webhook_conflict')) {
|
|
626
|
+
actions.push('Remove the existing webhook in the service that configured it, then rerun telegram status.');
|
|
627
|
+
}
|
|
628
|
+
if (blockers.includes('telegram_bot_probe_failed')) {
|
|
629
|
+
actions.push('Check network access to api.telegram.org, then rerun telegram status.');
|
|
630
|
+
}
|
|
631
|
+
if (blockers.includes('telegram_ingress_dead_letter_present')) {
|
|
632
|
+
actions.push('Inspect dead letters, stop the Hub, then explicitly requeue only safe entries or acknowledge an ambiguous entry before resending it.');
|
|
633
|
+
}
|
|
634
|
+
if (blockers.includes('telegram_pairing_missing') || blockers.some((blocker) => blocker.startsWith('telegram_pairing_invalid:'))) {
|
|
635
|
+
actions.push('Send /start to the bot in a private chat, then rerun telegram setup.');
|
|
636
|
+
}
|
|
637
|
+
if (blockers.includes('telegram_pairing_multiple_ids_requires_setup')) {
|
|
638
|
+
actions.push('Stop the hub, then rerun telegram setup to replace the ambiguous multi-ID config with one verified private chat and user.');
|
|
639
|
+
}
|
|
640
|
+
if (blockers.includes('no_registered_codex_session')) {
|
|
641
|
+
actions.push('Rerun telegram setup for this project root.');
|
|
642
|
+
}
|
|
643
|
+
if (blockers.includes('telegram_hub_not_running')) {
|
|
644
|
+
actions.push('Run sks telegram hub start --project-root \"$PWD\" --json after other blockers are cleared.');
|
|
645
|
+
}
|
|
646
|
+
return actions;
|
|
647
|
+
}
|
|
648
|
+
function hasMultipleTelegramPairingIds(value) {
|
|
649
|
+
if (!value || typeof value !== 'object')
|
|
650
|
+
return false;
|
|
651
|
+
const record = value;
|
|
652
|
+
return (Array.isArray(record.paired_chat_ids) && record.paired_chat_ids.length > 1)
|
|
653
|
+
|| (Array.isArray(record.paired_user_ids) && record.paired_user_ids.length > 1);
|
|
654
|
+
}
|
|
@@ -5,11 +5,21 @@ import { computeTriWikiAffectedGraph } from '../triwiki/triwiki-affected-graph.j
|
|
|
5
5
|
import { buildTriWikiGateImpactMap } from '../triwiki/triwiki-gate-impact-map.js';
|
|
6
6
|
import { DEFAULT_TRIWIKI_MODULE_CARDS } from '../triwiki/triwiki-module-card.js';
|
|
7
7
|
import { summarizeTriWikiProofBank } from '../triwiki/triwiki-proof-bank.js';
|
|
8
|
+
import { isTriWikiGraphSubcommand, triwikiGraphCommand } from './triwiki-graph-command.js';
|
|
8
9
|
export async function triwikiCommand(args = []) {
|
|
9
10
|
const root = await projectRoot();
|
|
10
11
|
const sub = args[0] && !args[0].startsWith('-') ? args[0] : 'index';
|
|
11
12
|
const json = flag(args, '--json');
|
|
12
13
|
let result;
|
|
14
|
+
if (isTriWikiGraphSubcommand(sub)) {
|
|
15
|
+
const answer = await triwikiGraphCommand(root, sub, args.slice(1));
|
|
16
|
+
process.exitCode = answer.ok ? 0 : 2;
|
|
17
|
+
if (json)
|
|
18
|
+
return printJson(answer.result);
|
|
19
|
+
for (const line of answer.lines)
|
|
20
|
+
console.log(line);
|
|
21
|
+
return answer.result;
|
|
22
|
+
}
|
|
13
23
|
if (sub === 'index') {
|
|
14
24
|
result = {
|
|
15
25
|
schema: 'sks.triwiki-index.v1',
|
|
@@ -26,7 +36,7 @@ export async function triwikiCommand(args = []) {
|
|
|
26
36
|
result = summarizeTriWikiProofBank(root);
|
|
27
37
|
}
|
|
28
38
|
else {
|
|
29
|
-
console.error('Usage: sks triwiki index|affected|proof-bank [--json]');
|
|
39
|
+
console.error('Usage: sks triwiki index|affected|proof-bank|graph-status|graph-lint|graph-query [--json]');
|
|
30
40
|
process.exitCode = 1;
|
|
31
41
|
return null;
|
|
32
42
|
}
|