instar 0.28.75 → 0.28.77
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/dashboard/index.html +306 -0
- package/dist/cli.js +5 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/discovery.d.ts.map +1 -1
- package/dist/commands/discovery.js +2 -1
- package/dist/commands/discovery.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +22 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/job.d.ts.map +1 -1
- package/dist/commands/job.js +2 -1
- package/dist/commands/job.js.map +1 -1
- package/dist/commands/ledgerCleanup.d.ts.map +1 -1
- package/dist/commands/ledgerCleanup.js +2 -1
- package/dist/commands/ledgerCleanup.js.map +1 -1
- package/dist/commands/listener.d.ts.map +1 -1
- package/dist/commands/listener.js +7 -6
- package/dist/commands/listener.js.map +1 -1
- package/dist/commands/nuke.d.ts.map +1 -1
- package/dist/commands/nuke.js +11 -15
- package/dist/commands/nuke.js.map +1 -1
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +35 -3
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +11 -9
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/slack-cli.d.ts.map +1 -1
- package/dist/commands/slack-cli.js +5 -4
- package/dist/commands/slack-cli.js.map +1 -1
- package/dist/commands/whatsapp.d.ts.map +1 -1
- package/dist/commands/whatsapp.js +2 -1
- package/dist/commands/whatsapp.js.map +1 -1
- package/dist/commands/worktree.d.ts.map +1 -1
- package/dist/commands/worktree.js +2 -1
- package/dist/commands/worktree.js.map +1 -1
- package/dist/core/AgentConnector.d.ts.map +1 -1
- package/dist/core/AgentConnector.js +9 -7
- package/dist/core/AgentConnector.js.map +1 -1
- package/dist/core/AgentRegistry.d.ts.map +1 -1
- package/dist/core/AgentRegistry.js +3 -2
- package/dist/core/AgentRegistry.js.map +1 -1
- package/dist/core/AutoDispatcher.d.ts.map +1 -1
- package/dist/core/AutoDispatcher.js +2 -1
- package/dist/core/AutoDispatcher.js.map +1 -1
- package/dist/core/AutoUpdater.d.ts.map +1 -1
- package/dist/core/AutoUpdater.js +2 -1
- package/dist/core/AutoUpdater.js.map +1 -1
- package/dist/core/AutonomousEvolution.d.ts.map +1 -1
- package/dist/core/AutonomousEvolution.js +2 -1
- package/dist/core/AutonomousEvolution.js.map +1 -1
- package/dist/core/BackupManager.d.ts.map +1 -1
- package/dist/core/BackupManager.js +2 -1
- package/dist/core/BackupManager.js.map +1 -1
- package/dist/core/BranchManager.d.ts.map +1 -1
- package/dist/core/BranchManager.js +3 -2
- package/dist/core/BranchManager.js.map +1 -1
- package/dist/core/CaffeinateManager.d.ts.map +1 -1
- package/dist/core/CaffeinateManager.js +2 -1
- package/dist/core/CaffeinateManager.js.map +1 -1
- package/dist/core/DeferredDispatchTracker.d.ts.map +1 -1
- package/dist/core/DeferredDispatchTracker.js +2 -1
- package/dist/core/DeferredDispatchTracker.js.map +1 -1
- package/dist/core/DispatchManager.d.ts.map +1 -1
- package/dist/core/DispatchManager.js +3 -2
- package/dist/core/DispatchManager.js.map +1 -1
- package/dist/core/EvolutionManager.d.ts.map +1 -1
- package/dist/core/EvolutionManager.js +2 -1
- package/dist/core/EvolutionManager.js.map +1 -1
- package/dist/core/ExecutionJournal.d.ts.map +1 -1
- package/dist/core/ExecutionJournal.js +2 -1
- package/dist/core/ExecutionJournal.js.map +1 -1
- package/dist/core/FeedbackManager.d.ts.map +1 -1
- package/dist/core/FeedbackManager.js +2 -1
- package/dist/core/FeedbackManager.js.map +1 -1
- package/dist/core/FileClassifier.d.ts.map +1 -1
- package/dist/core/FileClassifier.js +8 -13
- package/dist/core/FileClassifier.js.map +1 -1
- package/dist/core/ForegroundRestartWatcher.d.ts.map +1 -1
- package/dist/core/ForegroundRestartWatcher.js +3 -2
- package/dist/core/ForegroundRestartWatcher.js.map +1 -1
- package/dist/core/GitStateManager.d.ts.map +1 -1
- package/dist/core/GitStateManager.js +3 -11
- package/dist/core/GitStateManager.js.map +1 -1
- package/dist/core/GitSync.d.ts.map +1 -1
- package/dist/core/GitSync.js +6 -3
- package/dist/core/GitSync.js.map +1 -1
- package/dist/core/GlobalInstallCleanup.d.ts.map +1 -1
- package/dist/core/GlobalInstallCleanup.js +3 -2
- package/dist/core/GlobalInstallCleanup.js.map +1 -1
- package/dist/core/GlobalSecretStore.d.ts.map +1 -1
- package/dist/core/GlobalSecretStore.js +3 -2
- package/dist/core/GlobalSecretStore.js.map +1 -1
- package/dist/core/HandoffManager.d.ts.map +1 -1
- package/dist/core/HandoffManager.js +5 -3
- package/dist/core/HandoffManager.js.map +1 -1
- package/dist/core/JargonDetector.d.ts +28 -0
- package/dist/core/JargonDetector.d.ts.map +1 -0
- package/dist/core/JargonDetector.js +59 -0
- package/dist/core/JargonDetector.js.map +1 -0
- package/dist/core/LedgerSessionRegistry.d.ts.map +1 -1
- package/dist/core/LedgerSessionRegistry.js +2 -1
- package/dist/core/LedgerSessionRegistry.js.map +1 -1
- package/dist/core/MachineIdentity.d.ts.map +1 -1
- package/dist/core/MachineIdentity.js +2 -1
- package/dist/core/MachineIdentity.js.map +1 -1
- package/dist/core/MessagingToneGate.d.ts +42 -5
- package/dist/core/MessagingToneGate.d.ts.map +1 -1
- package/dist/core/MessagingToneGate.js +40 -6
- package/dist/core/MessagingToneGate.js.map +1 -1
- package/dist/core/ParallelDevWiring.d.ts.map +1 -1
- package/dist/core/ParallelDevWiring.js +3 -5
- package/dist/core/ParallelDevWiring.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +26 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +249 -44
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/ProjectMapper.d.ts.map +1 -1
- package/dist/core/ProjectMapper.js +5 -9
- package/dist/core/ProjectMapper.js.map +1 -1
- package/dist/core/RelationshipManager.d.ts.map +1 -1
- package/dist/core/RelationshipManager.js +4 -3
- package/dist/core/RelationshipManager.js.map +1 -1
- package/dist/core/SafeFsExecutor.d.ts +41 -0
- package/dist/core/SafeFsExecutor.d.ts.map +1 -0
- package/dist/core/SafeFsExecutor.js +146 -0
- package/dist/core/SafeFsExecutor.js.map +1 -0
- package/dist/core/SafeGitExecutor.d.ts +145 -0
- package/dist/core/SafeGitExecutor.d.ts.map +1 -0
- package/dist/core/SafeGitExecutor.js +717 -0
- package/dist/core/SafeGitExecutor.js.map +1 -0
- package/dist/core/ScopeVerifier.d.ts.map +1 -1
- package/dist/core/ScopeVerifier.js +3 -5
- package/dist/core/ScopeVerifier.js.map +1 -1
- package/dist/core/SecretStore.d.ts.map +1 -1
- package/dist/core/SecretStore.js +2 -1
- package/dist/core/SecretStore.js.map +1 -1
- package/dist/core/SharedStateLedger.d.ts.map +1 -1
- package/dist/core/SharedStateLedger.js +2 -1
- package/dist/core/SharedStateLedger.js.map +1 -1
- package/dist/core/SoulManager.d.ts.map +1 -1
- package/dist/core/SoulManager.js +3 -2
- package/dist/core/SoulManager.js.map +1 -1
- package/dist/core/StateManager.d.ts.map +1 -1
- package/dist/core/StateManager.js +4 -3
- package/dist/core/StateManager.js.map +1 -1
- package/dist/core/SyncOrchestrator.d.ts.map +1 -1
- package/dist/core/SyncOrchestrator.js +6 -4
- package/dist/core/SyncOrchestrator.js.map +1 -1
- package/dist/core/UpdateChecker.d.ts.map +1 -1
- package/dist/core/UpdateChecker.js +3 -2
- package/dist/core/UpdateChecker.js.map +1 -1
- package/dist/core/UpgradeGuideProcessor.d.ts.map +1 -1
- package/dist/core/UpgradeGuideProcessor.js +3 -2
- package/dist/core/UpgradeGuideProcessor.js.map +1 -1
- package/dist/core/WorktreeManager.d.ts.map +1 -1
- package/dist/core/WorktreeManager.js +9 -7
- package/dist/core/WorktreeManager.js.map +1 -1
- package/dist/knowledge/KnowledgeManager.d.ts.map +1 -1
- package/dist/knowledge/KnowledgeManager.js +2 -1
- package/dist/knowledge/KnowledgeManager.js.map +1 -1
- package/dist/lifeline/ServerSupervisor.d.ts +28 -0
- package/dist/lifeline/ServerSupervisor.d.ts.map +1 -1
- package/dist/lifeline/ServerSupervisor.js +171 -59
- package/dist/lifeline/ServerSupervisor.js.map +1 -1
- package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
- package/dist/lifeline/TelegramLifeline.js +10 -3
- package/dist/lifeline/TelegramLifeline.js.map +1 -1
- package/dist/lifeline/detectLaunchdSupervised.d.ts +43 -0
- package/dist/lifeline/detectLaunchdSupervised.d.ts.map +1 -0
- package/dist/lifeline/detectLaunchdSupervised.js +106 -0
- package/dist/lifeline/detectLaunchdSupervised.js.map +1 -0
- package/dist/lifeline/droppedMessages.d.ts.map +1 -1
- package/dist/lifeline/droppedMessages.js +2 -1
- package/dist/lifeline/droppedMessages.js.map +1 -1
- package/dist/memory/EpisodicMemory.d.ts.map +1 -1
- package/dist/memory/EpisodicMemory.js +2 -1
- package/dist/memory/EpisodicMemory.js.map +1 -1
- package/dist/memory/TopicMemory.d.ts.map +1 -1
- package/dist/memory/TopicMemory.js +5 -4
- package/dist/memory/TopicMemory.js.map +1 -1
- package/dist/messaging/AgentTokenManager.d.ts.map +1 -1
- package/dist/messaging/AgentTokenManager.js +2 -1
- package/dist/messaging/AgentTokenManager.js.map +1 -1
- package/dist/messaging/DropPickup.d.ts.map +1 -1
- package/dist/messaging/DropPickup.js +2 -1
- package/dist/messaging/DropPickup.js.map +1 -1
- package/dist/messaging/GitSyncTransport.d.ts.map +1 -1
- package/dist/messaging/GitSyncTransport.js +4 -3
- package/dist/messaging/GitSyncTransport.js.map +1 -1
- package/dist/messaging/MessageStore.d.ts.map +1 -1
- package/dist/messaging/MessageStore.js +3 -2
- package/dist/messaging/MessageStore.js.map +1 -1
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +5 -4
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/messaging/backends/BaileysBackend.d.ts.map +1 -1
- package/dist/messaging/backends/BaileysBackend.js +3 -2
- package/dist/messaging/backends/BaileysBackend.js.map +1 -1
- package/dist/messaging/local-tone-check.d.ts +61 -0
- package/dist/messaging/local-tone-check.d.ts.map +1 -0
- package/dist/messaging/local-tone-check.js +78 -0
- package/dist/messaging/local-tone-check.js.map +1 -0
- package/dist/messaging/pending-relay-store.d.ts +153 -0
- package/dist/messaging/pending-relay-store.d.ts.map +1 -0
- package/dist/messaging/pending-relay-store.js +351 -0
- package/dist/messaging/pending-relay-store.js.map +1 -0
- package/dist/messaging/secret-patterns.d.ts +35 -0
- package/dist/messaging/secret-patterns.d.ts.map +1 -0
- package/dist/messaging/secret-patterns.js +70 -0
- package/dist/messaging/secret-patterns.js.map +1 -0
- package/dist/messaging/shared/EncryptedAuthStore.d.ts.map +1 -1
- package/dist/messaging/shared/EncryptedAuthStore.js +3 -2
- package/dist/messaging/shared/EncryptedAuthStore.js.map +1 -1
- package/dist/messaging/shared/MessageLogger.d.ts.map +1 -1
- package/dist/messaging/shared/MessageLogger.js +2 -1
- package/dist/messaging/shared/MessageLogger.js.map +1 -1
- package/dist/messaging/shared/PrivacyConsent.d.ts.map +1 -1
- package/dist/messaging/shared/PrivacyConsent.js +2 -1
- package/dist/messaging/shared/PrivacyConsent.js.map +1 -1
- package/dist/messaging/shared/SessionChannelRegistry.d.ts.map +1 -1
- package/dist/messaging/shared/SessionChannelRegistry.js +2 -1
- package/dist/messaging/shared/SessionChannelRegistry.js.map +1 -1
- package/dist/messaging/system-templates.d.ts +87 -0
- package/dist/messaging/system-templates.d.ts.map +1 -0
- package/dist/messaging/system-templates.js +236 -0
- package/dist/messaging/system-templates.js.map +1 -0
- package/dist/messaging/whoami-cache.d.ts +66 -0
- package/dist/messaging/whoami-cache.d.ts.map +1 -0
- package/dist/messaging/whoami-cache.js +149 -0
- package/dist/messaging/whoami-cache.js.map +1 -0
- package/dist/moltbridge/ProfileCompiler.d.ts.map +1 -1
- package/dist/moltbridge/ProfileCompiler.js +13 -4
- package/dist/moltbridge/ProfileCompiler.js.map +1 -1
- package/dist/monitoring/CommitmentTracker.d.ts.map +1 -1
- package/dist/monitoring/CommitmentTracker.js +2 -1
- package/dist/monitoring/CommitmentTracker.js.map +1 -1
- package/dist/monitoring/CredentialProvider.d.ts.map +1 -1
- package/dist/monitoring/CredentialProvider.js +2 -1
- package/dist/monitoring/CredentialProvider.js.map +1 -1
- package/dist/monitoring/DegradationReporter.d.ts +41 -0
- package/dist/monitoring/DegradationReporter.d.ts.map +1 -1
- package/dist/monitoring/DegradationReporter.js +96 -4
- package/dist/monitoring/DegradationReporter.js.map +1 -1
- package/dist/monitoring/HealthChecker.d.ts.map +1 -1
- package/dist/monitoring/HealthChecker.js +2 -1
- package/dist/monitoring/HealthChecker.js.map +1 -1
- package/dist/monitoring/HookEventReceiver.d.ts.map +1 -1
- package/dist/monitoring/HookEventReceiver.js +2 -1
- package/dist/monitoring/HookEventReceiver.js.map +1 -1
- package/dist/monitoring/InstructionsVerifier.d.ts.map +1 -1
- package/dist/monitoring/InstructionsVerifier.js +2 -1
- package/dist/monitoring/InstructionsVerifier.js.map +1 -1
- package/dist/monitoring/PresenceProxy.d.ts.map +1 -1
- package/dist/monitoring/PresenceProxy.js +5 -4
- package/dist/monitoring/PresenceProxy.js.map +1 -1
- package/dist/monitoring/QuotaTracker.d.ts.map +1 -1
- package/dist/monitoring/QuotaTracker.js +2 -1
- package/dist/monitoring/QuotaTracker.js.map +1 -1
- package/dist/monitoring/SessionMigrator.d.ts.map +1 -1
- package/dist/monitoring/SessionMigrator.js +2 -1
- package/dist/monitoring/SessionMigrator.js.map +1 -1
- package/dist/monitoring/SessionRecovery.d.ts.map +1 -1
- package/dist/monitoring/SessionRecovery.js +2 -1
- package/dist/monitoring/SessionRecovery.js.map +1 -1
- package/dist/monitoring/TelemetryAuth.d.ts.map +1 -1
- package/dist/monitoring/TelemetryAuth.js +3 -2
- package/dist/monitoring/TelemetryAuth.js.map +1 -1
- package/dist/monitoring/TokenLedger.d.ts +91 -0
- package/dist/monitoring/TokenLedger.d.ts.map +1 -0
- package/dist/monitoring/TokenLedger.js +426 -0
- package/dist/monitoring/TokenLedger.js.map +1 -0
- package/dist/monitoring/TokenLedgerPoller.d.ts +26 -0
- package/dist/monitoring/TokenLedgerPoller.d.ts.map +1 -0
- package/dist/monitoring/TokenLedgerPoller.js +44 -0
- package/dist/monitoring/TokenLedgerPoller.js.map +1 -0
- package/dist/monitoring/TriageOrchestrator.d.ts.map +1 -1
- package/dist/monitoring/TriageOrchestrator.js +3 -2
- package/dist/monitoring/TriageOrchestrator.js.map +1 -1
- package/dist/monitoring/WorktreeReaper.d.ts.map +1 -1
- package/dist/monitoring/WorktreeReaper.js +5 -4
- package/dist/monitoring/WorktreeReaper.js.map +1 -1
- package/dist/monitoring/delivery-failure-sentinel/recovery-policy.d.ts +83 -0
- package/dist/monitoring/delivery-failure-sentinel/recovery-policy.d.ts.map +1 -0
- package/dist/monitoring/delivery-failure-sentinel/recovery-policy.js +218 -0
- package/dist/monitoring/delivery-failure-sentinel/recovery-policy.js.map +1 -0
- package/dist/monitoring/delivery-failure-sentinel.d.ts +177 -0
- package/dist/monitoring/delivery-failure-sentinel.d.ts.map +1 -0
- package/dist/monitoring/delivery-failure-sentinel.js +598 -0
- package/dist/monitoring/delivery-failure-sentinel.js.map +1 -0
- package/dist/monitoring/probes/PlatformProbe.d.ts.map +1 -1
- package/dist/monitoring/probes/PlatformProbe.js +3 -2
- package/dist/monitoring/probes/PlatformProbe.js.map +1 -1
- package/dist/monitoring/templates-drift-verifier.d.ts +109 -0
- package/dist/monitoring/templates-drift-verifier.d.ts.map +1 -0
- package/dist/monitoring/templates-drift-verifier.js +324 -0
- package/dist/monitoring/templates-drift-verifier.js.map +1 -0
- package/dist/paste/PasteManager.d.ts.map +1 -1
- package/dist/paste/PasteManager.js +5 -4
- package/dist/paste/PasteManager.js.map +1 -1
- package/dist/publishing/PrivateViewer.d.ts.map +1 -1
- package/dist/publishing/PrivateViewer.js +2 -1
- package/dist/publishing/PrivateViewer.js.map +1 -1
- package/dist/scheduler/JobScheduler.d.ts.map +1 -1
- package/dist/scheduler/JobScheduler.js +2 -1
- package/dist/scheduler/JobScheduler.js.map +1 -1
- package/dist/server/AgentServer.d.ts +18 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +186 -1
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/WebSocketManager.d.ts +11 -0
- package/dist/server/WebSocketManager.d.ts.map +1 -1
- package/dist/server/WebSocketManager.js +28 -0
- package/dist/server/WebSocketManager.js.map +1 -1
- package/dist/server/boot-id.d.ts +58 -0
- package/dist/server/boot-id.d.ts.map +1 -0
- package/dist/server/boot-id.js +121 -0
- package/dist/server/boot-id.js.map +1 -0
- package/dist/server/middleware.d.ts +14 -1
- package/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +81 -1
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/routes.d.ts +69 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +545 -16
- package/dist/server/routes.js.map +1 -1
- package/dist/threadline/AgentDiscovery.d.ts.map +1 -1
- package/dist/threadline/AgentDiscovery.js +2 -1
- package/dist/threadline/AgentDiscovery.js.map +1 -1
- package/dist/threadline/AgentTrustManager.d.ts.map +1 -1
- package/dist/threadline/AgentTrustManager.js +2 -1
- package/dist/threadline/AgentTrustManager.js.map +1 -1
- package/dist/threadline/CircuitBreaker.d.ts.map +1 -1
- package/dist/threadline/CircuitBreaker.js +2 -1
- package/dist/threadline/CircuitBreaker.js.map +1 -1
- package/dist/threadline/ComputeMeter.d.ts.map +1 -1
- package/dist/threadline/ComputeMeter.js +2 -1
- package/dist/threadline/ComputeMeter.js.map +1 -1
- package/dist/threadline/ContextThreadMap.d.ts.map +1 -1
- package/dist/threadline/ContextThreadMap.js +2 -1
- package/dist/threadline/ContextThreadMap.js.map +1 -1
- package/dist/threadline/HeartbeatWatchdog.d.ts +78 -0
- package/dist/threadline/HeartbeatWatchdog.d.ts.map +1 -0
- package/dist/threadline/HeartbeatWatchdog.js +212 -0
- package/dist/threadline/HeartbeatWatchdog.js.map +1 -0
- package/dist/threadline/HeartbeatWriter.d.ts +79 -0
- package/dist/threadline/HeartbeatWriter.d.ts.map +1 -0
- package/dist/threadline/HeartbeatWriter.js +109 -0
- package/dist/threadline/HeartbeatWriter.js.map +1 -0
- package/dist/threadline/InvitationManager.d.ts.map +1 -1
- package/dist/threadline/InvitationManager.js +2 -1
- package/dist/threadline/InvitationManager.js.map +1 -1
- package/dist/threadline/ListenerSessionManager.d.ts +24 -0
- package/dist/threadline/ListenerSessionManager.d.ts.map +1 -1
- package/dist/threadline/ListenerSessionManager.js +38 -0
- package/dist/threadline/ListenerSessionManager.js.map +1 -1
- package/dist/threadline/MCPAuth.d.ts.map +1 -1
- package/dist/threadline/MCPAuth.js +2 -1
- package/dist/threadline/MCPAuth.js.map +1 -1
- package/dist/threadline/PipeSessionSpawner.d.ts.map +1 -1
- package/dist/threadline/PipeSessionSpawner.js +3 -2
- package/dist/threadline/PipeSessionSpawner.js.map +1 -1
- package/dist/threadline/RateLimiter.d.ts.map +1 -1
- package/dist/threadline/RateLimiter.js +2 -1
- package/dist/threadline/RateLimiter.js.map +1 -1
- package/dist/threadline/RelaySpawnFailureHandler.d.ts +53 -0
- package/dist/threadline/RelaySpawnFailureHandler.d.ts.map +1 -0
- package/dist/threadline/RelaySpawnFailureHandler.js +73 -0
- package/dist/threadline/RelaySpawnFailureHandler.js.map +1 -0
- package/dist/threadline/SessionLifecycle.d.ts.map +1 -1
- package/dist/threadline/SessionLifecycle.js +2 -1
- package/dist/threadline/SessionLifecycle.js.map +1 -1
- package/dist/threadline/SpawnLedger.d.ts +94 -0
- package/dist/threadline/SpawnLedger.d.ts.map +1 -0
- package/dist/threadline/SpawnLedger.js +194 -0
- package/dist/threadline/SpawnLedger.js.map +1 -0
- package/dist/threadline/SpawnNonce.d.ts +49 -0
- package/dist/threadline/SpawnNonce.d.ts.map +1 -0
- package/dist/threadline/SpawnNonce.js +99 -0
- package/dist/threadline/SpawnNonce.js.map +1 -0
- package/dist/threadline/TelegramBridgeConfig.d.ts +79 -0
- package/dist/threadline/TelegramBridgeConfig.d.ts.map +1 -0
- package/dist/threadline/TelegramBridgeConfig.js +168 -0
- package/dist/threadline/TelegramBridgeConfig.js.map +1 -0
- package/dist/threadline/ThreadlineBootstrap.d.ts.map +1 -1
- package/dist/threadline/ThreadlineBootstrap.js +2 -1
- package/dist/threadline/ThreadlineBootstrap.js.map +1 -1
- package/dist/threadline/WakeSocketServer.d.ts.map +1 -1
- package/dist/threadline/WakeSocketServer.js +3 -2
- package/dist/threadline/WakeSocketServer.js.map +1 -1
- package/dist/threadline/listener-daemon.d.ts.map +1 -1
- package/dist/threadline/listener-daemon.js +3 -2
- package/dist/threadline/listener-daemon.js.map +1 -1
- package/dist/users/UserManager.d.ts.map +1 -1
- package/dist/users/UserManager.js +2 -1
- package/dist/users/UserManager.js.map +1 -1
- package/dist/users/UserOnboarding.d.ts.map +1 -1
- package/dist/users/UserOnboarding.js +2 -1
- package/dist/users/UserOnboarding.js.map +1 -1
- package/dist/utils/jsonl-rotation.d.ts.map +1 -1
- package/dist/utils/jsonl-rotation.js +2 -1
- package/dist/utils/jsonl-rotation.js.map +1 -1
- package/package.json +4 -2
- package/scripts/add-migration-marker.js +121 -0
- package/scripts/analyze-release.js +7 -6
- package/scripts/check-contract-evidence.js +27 -8
- package/scripts/destructive-command-shim.js +1 -0
- package/scripts/generate-builtin-manifest.cjs +1 -0
- package/scripts/lint-no-direct-destructive.js +617 -0
- package/scripts/lint-template-sha-history.ts +183 -0
- package/scripts/migrate-incident-2026-04-17.mjs +2 -1
- package/scripts/pre-push-gate.js +24 -0
- package/scripts/run-migration.js +500 -0
- package/scripts/test-bootstrap-relay.mjs +2 -1
- package/scripts/verify-deployed-templates.ts +87 -0
- package/scripts/worktree-commit-msg-hook.js +4 -0
- package/scripts/worktree-precommit-gate.js +1 -0
- package/src/data/builtin-manifest.json +139 -131
- package/src/templates/scripts/telegram-reply.sh +318 -13
- package/upgrades/0.28.76.md +67 -0
- package/upgrades/0.28.77.md +133 -0
- package/upgrades/side-effects/0.28.76.md +76 -0
- package/upgrades/side-effects/agent-health-alert-authority-routing.md +121 -0
- package/upgrades/side-effects/comprehensive-destructive-tool-containment-foundation.md +74 -0
- package/upgrades/side-effects/comprehensive-destructive-tool-containment-migration.md +82 -0
- package/upgrades/side-effects/deferral-detector-orphan-todo.md +101 -0
- package/upgrades/side-effects/lifeline-self-heal-hardening.md +151 -0
- package/upgrades/side-effects/relay-spawn-ghost-reply-phase1.md +139 -0
- package/upgrades/side-effects/telegram-delivery-robustness-layer-2.md +320 -0
- package/upgrades/side-effects/telegram-delivery-robustness-layer-3.md +202 -0
- package/upgrades/side-effects/telegram-delivery-robustness-layer-7.md +339 -0
- package/upgrades/side-effects/telegram-delivery-robustness.md +178 -0
- package/upgrades/side-effects/threadline-canonical-inbox-write.md +218 -0
- package/upgrades/side-effects/threadline-tg-bridge-settings-surface.md +208 -0
- package/upgrades/side-effects/token-ledger-phase1.md +123 -0
- package/upgrades/NEXT.md +0 -53
|
@@ -15,6 +15,12 @@ import fs from 'node:fs';
|
|
|
15
15
|
import path from 'node:path';
|
|
16
16
|
import { execSync } from 'node:child_process';
|
|
17
17
|
import { fileURLToPath } from 'node:url';
|
|
18
|
+
// Import from compiled output, not source. The script runs as part of the
|
|
19
|
+
// `prepublishOnly` chain (`npm run build && check:upgrade-guide &&
|
|
20
|
+
// check:contract-evidence`), so `dist/` is guaranteed to exist by the time
|
|
21
|
+
// we reach this import. Importing from `src/` fails because that tree is
|
|
22
|
+
// `.ts` only — Node's ESM resolver cannot map `.js` → `.ts` here.
|
|
23
|
+
import { SafeGitExecutor } from '../dist/core/SafeGitExecutor.js';
|
|
18
24
|
|
|
19
25
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
20
26
|
const ROOT = path.resolve(__dirname, '..');
|
|
@@ -36,16 +42,29 @@ const ADAPTER_PATHS = [
|
|
|
36
42
|
let adapterChanges = [];
|
|
37
43
|
try {
|
|
38
44
|
// Find files changed since the last npm version tag
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
let lastTag = '';
|
|
46
|
+
try {
|
|
47
|
+
lastTag = SafeGitExecutor.readSync(['describe', '--tags', '--abbrev=0'], {
|
|
48
|
+
encoding: 'utf-8',
|
|
49
|
+
cwd: ROOT,
|
|
50
|
+
operation: 'scripts/check-contract-evidence.js:lastTag',
|
|
51
|
+
}).trim();
|
|
52
|
+
} catch {
|
|
53
|
+
lastTag = '';
|
|
54
|
+
}
|
|
43
55
|
|
|
44
56
|
const diffBase = lastTag || 'HEAD~10';
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
let changedFiles = [];
|
|
58
|
+
try {
|
|
59
|
+
const out = SafeGitExecutor.readSync(['diff', '--name-only', `${diffBase}...HEAD`], {
|
|
60
|
+
encoding: 'utf-8',
|
|
61
|
+
cwd: ROOT,
|
|
62
|
+
operation: 'scripts/check-contract-evidence.js:changedFiles',
|
|
63
|
+
});
|
|
64
|
+
changedFiles = out.trim().split('\n').filter(Boolean);
|
|
65
|
+
} catch {
|
|
66
|
+
changedFiles = [];
|
|
67
|
+
}
|
|
49
68
|
|
|
50
69
|
adapterChanges = changedFiles.filter(f =>
|
|
51
70
|
ADAPTER_PATHS.some(prefix => f.startsWith(prefix))
|
|
@@ -102,6 +102,7 @@ function dryRunGitClean(cwd, args) {
|
|
|
102
102
|
// Use -n to count what would be removed
|
|
103
103
|
const dryArgs = ['-C', cwd, 'clean', '-n', ...args.filter(a => a !== '-f')];
|
|
104
104
|
try {
|
|
105
|
+
// safe-git-allow: incremental-migration
|
|
105
106
|
const out = execFileSync('git', dryArgs, { encoding: 'utf-8', timeout: 5000 });
|
|
106
107
|
return out.split('\n').filter(Boolean).length;
|
|
107
108
|
} catch { return Number.POSITIVE_INFINITY; }
|
|
@@ -18,6 +18,7 @@ const ROOT = path.resolve(__dirname, '..');
|
|
|
18
18
|
|
|
19
19
|
// Guard: warn if working tree is dirty (manifest won't match committed source in CI)
|
|
20
20
|
try {
|
|
21
|
+
// safe-git-allow: incremental-migration
|
|
21
22
|
const status = execSync('git status --porcelain -- src/', { cwd: ROOT, encoding: 'utf-8' }).trim();
|
|
22
23
|
if (status) {
|
|
23
24
|
console.warn('\n⚠️ WARNING: Working tree has uncommitted changes in src/.');
|
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lint-no-direct-destructive.js — refuses direct destructive git/fs callsites.
|
|
4
|
+
*
|
|
5
|
+
* Implements AC-3 / AC-5 / AC-6 / AC-7 from
|
|
6
|
+
* docs/specs/COMPREHENSIVE-DESTRUCTIVE-TOOL-CONTAINMENT-SPEC.md.
|
|
7
|
+
*
|
|
8
|
+
* The funnels (`src/core/SafeGitExecutor.ts`, `src/core/SafeFsExecutor.ts`)
|
|
9
|
+
* are the only modules in the codebase allowed to call:
|
|
10
|
+
* - `child_process.execFileSync('git', ...)` / `execSync('git ...')` /
|
|
11
|
+
* `spawn('git', ...)` / `spawnSync('git', ...)` / `exec('git ...')`
|
|
12
|
+
* - `fs.rm` / `fs.rmSync` / `fs.unlink` / `fs.unlinkSync` / `fs.rmdir` /
|
|
13
|
+
* `fs.rmdirSync` (and their `fs/promises` counterparts)
|
|
14
|
+
* - `simpleGit(...)` from the `simple-git` package
|
|
15
|
+
*
|
|
16
|
+
* This script AST-walks every `.ts`/`.tsx`/`.js`/`.mjs`/`.cjs` file in
|
|
17
|
+
* `src/`, `tests/`, `scripts/` (configurable via CLI args) and flags
|
|
18
|
+
* violations.
|
|
19
|
+
*
|
|
20
|
+
* It also greps `.sh` files and the `scripts` section of `package.json`
|
|
21
|
+
* for direct destructive `git <verb>` invocations (closed verb list).
|
|
22
|
+
*
|
|
23
|
+
* Exit codes:
|
|
24
|
+
* 0 — no violations.
|
|
25
|
+
* 1 — at least one violation.
|
|
26
|
+
*
|
|
27
|
+
* Usage:
|
|
28
|
+
* node scripts/lint-no-direct-destructive.js # full repo
|
|
29
|
+
* node scripts/lint-no-direct-destructive.js --staged # staged files only
|
|
30
|
+
* node scripts/lint-no-direct-destructive.js path1 path2 # specific files
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import fs from 'node:fs';
|
|
34
|
+
import path from 'node:path';
|
|
35
|
+
import { execSync } from 'node:child_process';
|
|
36
|
+
import { fileURLToPath } from 'node:url';
|
|
37
|
+
|
|
38
|
+
// Avoid importing typescript at module top — it's a heavy dep. We require it
|
|
39
|
+
// only when a TS file actually needs parsing.
|
|
40
|
+
let _ts = null;
|
|
41
|
+
function ts() {
|
|
42
|
+
if (_ts) return _ts;
|
|
43
|
+
_ts = require('typescript');
|
|
44
|
+
return _ts;
|
|
45
|
+
}
|
|
46
|
+
import { createRequire } from 'node:module';
|
|
47
|
+
const require = createRequire(import.meta.url);
|
|
48
|
+
|
|
49
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
50
|
+
const __dirname = path.dirname(__filename);
|
|
51
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
52
|
+
|
|
53
|
+
// ── Allowlist (closed) ─────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Files that may legitimately call destructive git/fs primitives directly.
|
|
57
|
+
* Adding entries requires a spec change.
|
|
58
|
+
*/
|
|
59
|
+
const ALLOWLIST = new Set([
|
|
60
|
+
'src/core/SafeGitExecutor.ts',
|
|
61
|
+
'src/core/SafeFsExecutor.ts',
|
|
62
|
+
'tests/unit/SafeGitExecutor.test.ts',
|
|
63
|
+
'tests/unit/SafeFsExecutor.test.ts',
|
|
64
|
+
// The lint rule itself runs before SafeGitExecutor.ts is compiled, so it
|
|
65
|
+
// needs direct execSync as a bootstrap escape. The single git call here is
|
|
66
|
+
// a read-only `git diff --cached --name-only` for staged-file detection.
|
|
67
|
+
'scripts/lint-no-direct-destructive.js',
|
|
68
|
+
// Postinstall bootstrap script — runs before TypeScript is compiled and
|
|
69
|
+
// before SafeFsExecutor is available. CommonJS, can't use ESM imports.
|
|
70
|
+
'scripts/fix-better-sqlite3.cjs',
|
|
71
|
+
// Pre-commit hook gate — runs before TS is compiled. Read-only `git diff
|
|
72
|
+
// --cached` only; cannot depend on the TS funnel.
|
|
73
|
+
'scripts/instar-dev-precommit.js',
|
|
74
|
+
// Worktree-related git hooks — run before TS is compiled.
|
|
75
|
+
'scripts/worktree-precommit-gate.js',
|
|
76
|
+
'scripts/worktree-commit-msg-hook.js',
|
|
77
|
+
// Pre-command shim that wraps git invocations from outside the safe
|
|
78
|
+
// executor — bootstraps the safety check, can't be inside the funnel.
|
|
79
|
+
'scripts/destructive-command-shim.js',
|
|
80
|
+
// Bootstrap script for the builtin-manifest — runs as part of `npm run
|
|
81
|
+
// build` before tsc emits dist/.
|
|
82
|
+
'scripts/generate-builtin-manifest.cjs',
|
|
83
|
+
// Transitional: paired with the messaging adapter contract gate — these
|
|
84
|
+
// two files trigger the pre-push contract test requirement when modified.
|
|
85
|
+
// Their fs.unlinkSync calls are local hardlink-recreation cleanup (not
|
|
86
|
+
// adapter API changes), but the gate can't tell the difference. They will
|
|
87
|
+
// be migrated in a follow-up PR alongside contract test evidence.
|
|
88
|
+
'src/messaging/imessage/IMessageAdapter.ts',
|
|
89
|
+
'src/messaging/imessage/NativeBackend.ts',
|
|
90
|
+
// The shim runs `git <verb> --dry-run` first to count files, then re-invokes
|
|
91
|
+
// for real. Both invocations route through SafeGitExecutor, but the shim's
|
|
92
|
+
// own implementation file must be allowed to import the executor.
|
|
93
|
+
// The shim itself uses SafeGitExecutor — no direct execFileSync needed. If
|
|
94
|
+
// the shim ever needs direct access it gets added here.
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Allow `// safe-git-allow: <reason>` as a per-file escape on the FIRST
|
|
99
|
+
* non-empty line of the file. Used by SafeGitExecutor.ts itself and the
|
|
100
|
+
* test files. Other callers must be on the closed allowlist.
|
|
101
|
+
*/
|
|
102
|
+
function hasAllowComment(text) {
|
|
103
|
+
const lines = text.split('\n').slice(0, 5);
|
|
104
|
+
for (const line of lines) {
|
|
105
|
+
if (/^\s*\/\/\s*safe-git-allow:/.test(line)) return true;
|
|
106
|
+
if (/^\s*\/\*[\s\S]*?safe-git-allow:/m.test(line)) return true;
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── Violation reporting ────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
const violations = [];
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Per-callsite marker: `// safe-git-allow: incremental-migration` placed on
|
|
117
|
+
* the line immediately above (or at end of) a flagged callsite suppresses
|
|
118
|
+
* that single violation. Used during the transitional period before
|
|
119
|
+
* commitment://incremental-migration lands (PR #2). Expires 2026-05-03.
|
|
120
|
+
*
|
|
121
|
+
* NEW callsites cannot use this marker — only pre-existing callsites that
|
|
122
|
+
* were stamped in PR #1 by scripts/add-migration-marker.js. After PR #2,
|
|
123
|
+
* zero markers should remain.
|
|
124
|
+
*/
|
|
125
|
+
const MIGRATION_MARKER_EXPIRY = new Date('2026-05-03T00:00:00Z');
|
|
126
|
+
const MIGRATION_MARKER_RE = /\/\/\s*safe-git-allow:\s*incremental-migration\b/;
|
|
127
|
+
|
|
128
|
+
function hasLineMarker(text, line) {
|
|
129
|
+
// line is 1-based. Check the line itself (trailing comment) and the
|
|
130
|
+
// line immediately above it.
|
|
131
|
+
const lines = text.split('\n');
|
|
132
|
+
const idx = line - 1;
|
|
133
|
+
if (idx < 0 || idx >= lines.length) return false;
|
|
134
|
+
if (MIGRATION_MARKER_RE.test(lines[idx])) return true;
|
|
135
|
+
if (idx - 1 >= 0 && MIGRATION_MARKER_RE.test(lines[idx - 1])) return true;
|
|
136
|
+
// Also allow the marker two lines above to tolerate blank-line spacing.
|
|
137
|
+
if (idx - 2 >= 0 && /^\s*$/.test(lines[idx - 1]) && MIGRATION_MARKER_RE.test(lines[idx - 2])) return true;
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function migrationMarkerExpired() {
|
|
142
|
+
return new Date() >= MIGRATION_MARKER_EXPIRY;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function migrationMarkerDisabled() {
|
|
146
|
+
// Internal flag used by scripts/add-migration-marker.js to collect ALL
|
|
147
|
+
// pre-existing violations (including ones that already carry the marker)
|
|
148
|
+
// so it can re-stamp idempotently.
|
|
149
|
+
return process.env.INSTAR_DISABLE_MIGRATION_MARKER === '1';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function report(file, line, col, msg, ctx) {
|
|
153
|
+
// Honor per-callsite incremental-migration marker (transitional period).
|
|
154
|
+
if (
|
|
155
|
+
ctx &&
|
|
156
|
+
ctx.text &&
|
|
157
|
+
!migrationMarkerDisabled() &&
|
|
158
|
+
!migrationMarkerExpired() &&
|
|
159
|
+
hasLineMarker(ctx.text, line)
|
|
160
|
+
) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
violations.push({ file, line, col, msg });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ── AST scan: TS / JS files ────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
const DESTRUCTIVE_FS_NAMES = new Set([
|
|
169
|
+
'rm',
|
|
170
|
+
'rmSync',
|
|
171
|
+
'unlink',
|
|
172
|
+
'unlinkSync',
|
|
173
|
+
'rmdir',
|
|
174
|
+
'rmdirSync',
|
|
175
|
+
]);
|
|
176
|
+
|
|
177
|
+
const CHILD_PROCESS_FNS = new Set([
|
|
178
|
+
'execFileSync',
|
|
179
|
+
'execSync',
|
|
180
|
+
'spawn',
|
|
181
|
+
'spawnSync',
|
|
182
|
+
'exec',
|
|
183
|
+
'execFile',
|
|
184
|
+
]);
|
|
185
|
+
|
|
186
|
+
const CHILD_PROCESS_MODULE_NAMES = new Set([
|
|
187
|
+
'child_process',
|
|
188
|
+
'node:child_process',
|
|
189
|
+
]);
|
|
190
|
+
|
|
191
|
+
const FS_MODULE_NAMES = new Set([
|
|
192
|
+
'fs',
|
|
193
|
+
'node:fs',
|
|
194
|
+
'fs/promises',
|
|
195
|
+
'node:fs/promises',
|
|
196
|
+
]);
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Walk a TypeScript SourceFile AST, collecting violations.
|
|
200
|
+
*
|
|
201
|
+
* Rules:
|
|
202
|
+
* 1. Direct call to one of CHILD_PROCESS_FNS where the first arg is the
|
|
203
|
+
* string literal 'git' OR a string literal starting with 'git ' — flag.
|
|
204
|
+
* 2. Member access on an identifier known to alias the child_process or
|
|
205
|
+
* fs module — same rule applies.
|
|
206
|
+
* 3. Named import of `simpleGit` from `simple-git` — flag.
|
|
207
|
+
* 4. Named import of one of DESTRUCTIVE_FS_NAMES from fs / fs/promises — flag.
|
|
208
|
+
* 5. Namespace import of fs / fs/promises followed by member-access
|
|
209
|
+
* to a destructive name — flag.
|
|
210
|
+
* 6. require('child_process').execFileSync('git', ...) and equivalents — flag.
|
|
211
|
+
* 7. Dynamic property access used to evade the rule (`fs['rm' + 'Sync']`) — flag.
|
|
212
|
+
*/
|
|
213
|
+
function lintTsFile(file, text) {
|
|
214
|
+
const T = ts();
|
|
215
|
+
const sf = T.createSourceFile(file, text, T.ScriptTarget.Latest, true);
|
|
216
|
+
const ctx = { text };
|
|
217
|
+
const r = (f, l, c, m) => report(f, l, c, m, ctx);
|
|
218
|
+
|
|
219
|
+
/** module name → local identifier (default + namespace + named) */
|
|
220
|
+
const childProcessIdentifiers = new Set();
|
|
221
|
+
const childProcessNamedImports = new Map(); // localName -> originalName
|
|
222
|
+
const fsNamespaceIdentifiers = new Set();
|
|
223
|
+
const fsNamedDestructiveImports = new Map(); // localName -> originalName
|
|
224
|
+
const simpleGitImports = []; // {localName}
|
|
225
|
+
const requireBindings = new Map(); // localName -> moduleName (if literal)
|
|
226
|
+
|
|
227
|
+
function lineCol(node) {
|
|
228
|
+
const lc = sf.getLineAndCharacterOfPosition(node.getStart(sf));
|
|
229
|
+
return { line: lc.line + 1, col: lc.character + 1 };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function visit(node) {
|
|
233
|
+
// ── Imports ────────────────────────────────────────────────
|
|
234
|
+
if (T.isImportDeclaration(node) && node.moduleSpecifier && T.isStringLiteral(node.moduleSpecifier)) {
|
|
235
|
+
const mod = node.moduleSpecifier.text;
|
|
236
|
+
const ic = node.importClause;
|
|
237
|
+
if (!ic) return;
|
|
238
|
+
if (ic.namedBindings) {
|
|
239
|
+
if (T.isNamespaceImport(ic.namedBindings)) {
|
|
240
|
+
const localName = ic.namedBindings.name.text;
|
|
241
|
+
if (CHILD_PROCESS_MODULE_NAMES.has(mod)) {
|
|
242
|
+
childProcessIdentifiers.add(localName);
|
|
243
|
+
} else if (FS_MODULE_NAMES.has(mod)) {
|
|
244
|
+
fsNamespaceIdentifiers.add(localName);
|
|
245
|
+
}
|
|
246
|
+
} else if (T.isNamedImports(ic.namedBindings)) {
|
|
247
|
+
for (const el of ic.namedBindings.elements) {
|
|
248
|
+
const importedName = el.propertyName ? el.propertyName.text : el.name.text;
|
|
249
|
+
const localName = el.name.text;
|
|
250
|
+
if (CHILD_PROCESS_MODULE_NAMES.has(mod) && CHILD_PROCESS_FNS.has(importedName)) {
|
|
251
|
+
childProcessNamedImports.set(localName, importedName);
|
|
252
|
+
} else if (FS_MODULE_NAMES.has(mod) && DESTRUCTIVE_FS_NAMES.has(importedName)) {
|
|
253
|
+
fsNamedDestructiveImports.set(localName, importedName);
|
|
254
|
+
} else if (mod === 'simple-git' && importedName === 'simpleGit') {
|
|
255
|
+
simpleGitImports.push({ localName });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// Default import — `import fs from 'node:fs'` style.
|
|
261
|
+
if (ic.name) {
|
|
262
|
+
const localName = ic.name.text;
|
|
263
|
+
if (CHILD_PROCESS_MODULE_NAMES.has(mod)) {
|
|
264
|
+
childProcessIdentifiers.add(localName);
|
|
265
|
+
} else if (FS_MODULE_NAMES.has(mod)) {
|
|
266
|
+
fsNamespaceIdentifiers.add(localName);
|
|
267
|
+
} else if (mod === 'simple-git') {
|
|
268
|
+
simpleGitImports.push({ localName });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ── require('child_process') / require('fs') ───────────────
|
|
274
|
+
if (T.isVariableDeclaration(node) && node.initializer && T.isCallExpression(node.initializer)) {
|
|
275
|
+
const callee = node.initializer.expression;
|
|
276
|
+
if (T.isIdentifier(callee) && callee.text === 'require' && node.initializer.arguments.length === 1) {
|
|
277
|
+
const arg = node.initializer.arguments[0];
|
|
278
|
+
if (T.isStringLiteral(arg)) {
|
|
279
|
+
const mod = arg.text;
|
|
280
|
+
if (T.isIdentifier(node.name)) {
|
|
281
|
+
const localName = node.name.text;
|
|
282
|
+
if (CHILD_PROCESS_MODULE_NAMES.has(mod)) {
|
|
283
|
+
childProcessIdentifiers.add(localName);
|
|
284
|
+
requireBindings.set(localName, mod);
|
|
285
|
+
} else if (FS_MODULE_NAMES.has(mod)) {
|
|
286
|
+
fsNamespaceIdentifiers.add(localName);
|
|
287
|
+
requireBindings.set(localName, mod);
|
|
288
|
+
} else if (mod === 'simple-git') {
|
|
289
|
+
simpleGitImports.push({ localName });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// Destructured: const { execFileSync } = require('child_process')
|
|
293
|
+
if (T.isObjectBindingPattern(node.name)) {
|
|
294
|
+
for (const el of node.name.elements) {
|
|
295
|
+
const importedName = el.propertyName && T.isIdentifier(el.propertyName)
|
|
296
|
+
? el.propertyName.text
|
|
297
|
+
: (T.isIdentifier(el.name) ? el.name.text : null);
|
|
298
|
+
const localName = T.isIdentifier(el.name) ? el.name.text : null;
|
|
299
|
+
if (!importedName || !localName) continue;
|
|
300
|
+
if (CHILD_PROCESS_MODULE_NAMES.has(mod) && CHILD_PROCESS_FNS.has(importedName)) {
|
|
301
|
+
childProcessNamedImports.set(localName, importedName);
|
|
302
|
+
} else if (FS_MODULE_NAMES.has(mod) && DESTRUCTIVE_FS_NAMES.has(importedName)) {
|
|
303
|
+
fsNamedDestructiveImports.set(localName, importedName);
|
|
304
|
+
} else if (mod === 'simple-git' && importedName === 'simpleGit') {
|
|
305
|
+
simpleGitImports.push({ localName });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ── Inline require('child_process').execFileSync(...) ──────
|
|
314
|
+
if (T.isCallExpression(node) && T.isPropertyAccessExpression(node.expression)) {
|
|
315
|
+
const obj = node.expression.expression;
|
|
316
|
+
const name = node.expression.name.text;
|
|
317
|
+
if (T.isCallExpression(obj) && T.isIdentifier(obj.expression) && obj.expression.text === 'require'
|
|
318
|
+
&& obj.arguments.length === 1 && T.isStringLiteral(obj.arguments[0])) {
|
|
319
|
+
const mod = obj.arguments[0].text;
|
|
320
|
+
if (CHILD_PROCESS_MODULE_NAMES.has(mod) && CHILD_PROCESS_FNS.has(name)) {
|
|
321
|
+
if (firstArgIsGit(node)) {
|
|
322
|
+
const lc = lineCol(node);
|
|
323
|
+
r(file, lc.line, lc.col, `Direct require('${mod}').${name}('git', ...) — use SafeGitExecutor.`);
|
|
324
|
+
}
|
|
325
|
+
} else if (FS_MODULE_NAMES.has(mod) && DESTRUCTIVE_FS_NAMES.has(name)) {
|
|
326
|
+
const lc = lineCol(node);
|
|
327
|
+
r(file, lc.line, lc.col, `Direct require('${mod}').${name}(...) — use SafeFsExecutor.`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ── Plain CallExpression of an identifier ──────────────────
|
|
333
|
+
if (T.isCallExpression(node) && T.isIdentifier(node.expression)) {
|
|
334
|
+
const fnName = node.expression.text;
|
|
335
|
+
// Named import alias for execFileSync/etc.
|
|
336
|
+
if (childProcessNamedImports.has(fnName)) {
|
|
337
|
+
if (firstArgIsGit(node)) {
|
|
338
|
+
const lc = lineCol(node);
|
|
339
|
+
r(file, lc.line, lc.col, `Direct ${childProcessNamedImports.get(fnName)}('git', ...) — use SafeGitExecutor.`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// Named import alias for rm/etc.
|
|
343
|
+
if (fsNamedDestructiveImports.has(fnName)) {
|
|
344
|
+
const lc = lineCol(node);
|
|
345
|
+
r(file, lc.line, lc.col, `Direct ${fsNamedDestructiveImports.get(fnName)}(...) — use SafeFsExecutor.`);
|
|
346
|
+
}
|
|
347
|
+
// simpleGit() call.
|
|
348
|
+
if (simpleGitImports.some((s) => s.localName === fnName)) {
|
|
349
|
+
const lc = lineCol(node);
|
|
350
|
+
r(file, lc.line, lc.col, `Direct simpleGit(...) — use SafeGitExecutor.`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// ── Member call on namespace identifier ────────────────────
|
|
355
|
+
if (T.isCallExpression(node) && T.isPropertyAccessExpression(node.expression)) {
|
|
356
|
+
const obj = node.expression.expression;
|
|
357
|
+
const name = node.expression.name.text;
|
|
358
|
+
if (T.isIdentifier(obj)) {
|
|
359
|
+
const objName = obj.text;
|
|
360
|
+
if (childProcessIdentifiers.has(objName) && CHILD_PROCESS_FNS.has(name)) {
|
|
361
|
+
if (firstArgIsGit(node)) {
|
|
362
|
+
const lc = lineCol(node);
|
|
363
|
+
r(file, lc.line, lc.col, `Direct ${objName}.${name}('git', ...) — use SafeGitExecutor.`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (fsNamespaceIdentifiers.has(objName) && DESTRUCTIVE_FS_NAMES.has(name)) {
|
|
367
|
+
const lc = lineCol(node);
|
|
368
|
+
r(file, lc.line, lc.col, `Direct ${objName}.${name}(...) — use SafeFsExecutor.`);
|
|
369
|
+
}
|
|
370
|
+
// Defense-in-depth: catch fs.promises.rm via fs identifier.
|
|
371
|
+
if (fsNamespaceIdentifiers.has(objName) && name === 'promises') {
|
|
372
|
+
// The next member access in a chained call.
|
|
373
|
+
// Detected via the parent CallExpression where expression is
|
|
374
|
+
// `fs.promises.rm(...)` — the whole chain is this PropertyAccessExpression.
|
|
375
|
+
// We already capture this via the ElementAccessExpression branch below
|
|
376
|
+
// and via deep walking, but most simply: a CallExpression whose
|
|
377
|
+
// callee is `<fs>.promises.<destructive>` deserves a flag.
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
// fs.promises.rm(...) — the callee is PropertyAccess(PropertyAccess(fs, promises), rm)
|
|
381
|
+
if (T.isPropertyAccessExpression(obj) && T.isIdentifier(obj.expression)
|
|
382
|
+
&& fsNamespaceIdentifiers.has(obj.expression.text)
|
|
383
|
+
&& obj.name.text === 'promises'
|
|
384
|
+
&& DESTRUCTIVE_FS_NAMES.has(name)) {
|
|
385
|
+
const lc = lineCol(node);
|
|
386
|
+
r(file, lc.line, lc.col, `Direct ${obj.expression.text}.promises.${name}(...) — use SafeFsExecutor.`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// ── Dynamic / computed access: fs['rm' + 'Sync'](...) ──────
|
|
391
|
+
if (T.isCallExpression(node) && T.isElementAccessExpression(node.expression)) {
|
|
392
|
+
const obj = node.expression.expression;
|
|
393
|
+
const arg = node.expression.argumentExpression;
|
|
394
|
+
if (T.isIdentifier(obj) && fsNamespaceIdentifiers.has(obj.text)) {
|
|
395
|
+
// Any computed member access on the fs namespace is suspicious;
|
|
396
|
+
// refuse with a clear message.
|
|
397
|
+
const lc = lineCol(node);
|
|
398
|
+
r(file, lc.line, lc.col, `Computed member access on fs (${obj.text}[...]) — refuse, use SafeFsExecutor.`);
|
|
399
|
+
}
|
|
400
|
+
if (T.isIdentifier(obj) && childProcessIdentifiers.has(obj.text)) {
|
|
401
|
+
const lc = lineCol(node);
|
|
402
|
+
r(file, lc.line, lc.col, `Computed member access on child_process (${obj.text}[...]) — refuse, use SafeGitExecutor.`);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
T.forEachChild(node, visit);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function firstArgIsGit(call) {
|
|
410
|
+
const args = call.arguments;
|
|
411
|
+
if (args.length === 0) return false;
|
|
412
|
+
const first = args[0];
|
|
413
|
+
const T = ts();
|
|
414
|
+
if (T.isStringLiteral(first)) {
|
|
415
|
+
if (first.text === 'git') return true;
|
|
416
|
+
if (first.text.startsWith('git ')) return true;
|
|
417
|
+
}
|
|
418
|
+
if (T.isTemplateExpression(first) || T.isNoSubstitutionTemplateLiteral(first)) {
|
|
419
|
+
// Best-effort: check the head.
|
|
420
|
+
const text = first.getText(sf);
|
|
421
|
+
if (/^[`'"]git[\s'"]/.test(text)) return true;
|
|
422
|
+
}
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
visit(sf);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// ── Shell + package.json grep ──────────────────────────────────────
|
|
430
|
+
|
|
431
|
+
const DESTRUCTIVE_GIT_VERBS = [
|
|
432
|
+
'add', 'am', 'apply', 'branch', 'checkout', 'cherry-pick',
|
|
433
|
+
'clean', 'clone', 'commit', 'fetch', 'gc', 'init', 'merge',
|
|
434
|
+
'mv', 'pull', 'push', 'rebase', 'reset', 'restore', 'revert',
|
|
435
|
+
'rm', 'stash', 'submodule', 'switch', 'tag', 'update-ref',
|
|
436
|
+
'worktree', 'prune', 'notes', 'replace', 'filter-branch',
|
|
437
|
+
];
|
|
438
|
+
const SHELL_GIT_REGEX = new RegExp(
|
|
439
|
+
String.raw`(?:^|[\s;&|()])git\s+(?:-C\s+\S+\s+|-c\s+\S+\s+)*(${DESTRUCTIVE_GIT_VERBS.join('|')})\b`,
|
|
440
|
+
'g',
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
const SHELL_ALLOWLIST = new Set([
|
|
444
|
+
'scripts/setup-imessage-hardlink.sh',
|
|
445
|
+
// Transitional: pre-existing template script with destructive git verbs.
|
|
446
|
+
// Tracked under commitment://incremental-migration (due 2026-05-03). PR #2
|
|
447
|
+
// ports this to a Node script that uses SafeGitExecutor and removes this
|
|
448
|
+
// entry.
|
|
449
|
+
'src/templates/scripts/git-sync-gate.sh',
|
|
450
|
+
]);
|
|
451
|
+
|
|
452
|
+
function lintShellFile(file, text) {
|
|
453
|
+
const rel = path.relative(ROOT, path.resolve(file));
|
|
454
|
+
if (SHELL_ALLOWLIST.has(rel)) return;
|
|
455
|
+
const lines = text.split('\n');
|
|
456
|
+
for (let i = 0; i < lines.length; i++) {
|
|
457
|
+
const line = lines[i];
|
|
458
|
+
// Skip comments
|
|
459
|
+
const stripped = line.replace(/^\s*#.*$/, '');
|
|
460
|
+
if (!stripped) continue;
|
|
461
|
+
SHELL_GIT_REGEX.lastIndex = 0;
|
|
462
|
+
const m = SHELL_GIT_REGEX.exec(stripped);
|
|
463
|
+
if (m) {
|
|
464
|
+
report(file, i + 1, (m.index || 0) + 1, `Shell script invokes destructive 'git ${m[1]}' — port to a Node script using SafeGitExecutor.`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function lintPackageJsonScripts(file, text) {
|
|
470
|
+
let pkg;
|
|
471
|
+
try {
|
|
472
|
+
pkg = JSON.parse(text);
|
|
473
|
+
} catch {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const scripts = pkg.scripts || {};
|
|
477
|
+
for (const [name, cmd] of Object.entries(scripts)) {
|
|
478
|
+
if (typeof cmd !== 'string') continue;
|
|
479
|
+
SHELL_GIT_REGEX.lastIndex = 0;
|
|
480
|
+
const m = SHELL_GIT_REGEX.exec(cmd);
|
|
481
|
+
if (m) {
|
|
482
|
+
report(file, 1, 1, `npm script "${name}" runs destructive 'git ${m[1]}' — refuse.`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// ── File enumeration ───────────────────────────────────────────────
|
|
488
|
+
|
|
489
|
+
const TARGET_DIRS = ['src', 'tests', 'scripts'];
|
|
490
|
+
const TARGET_EXTS = ['.ts', '.tsx', '.js', '.mjs', '.cjs'];
|
|
491
|
+
|
|
492
|
+
function walkDir(dir, out) {
|
|
493
|
+
let entries;
|
|
494
|
+
try {
|
|
495
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
496
|
+
} catch {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
for (const e of entries) {
|
|
500
|
+
if (e.name === 'node_modules' || e.name === '.git' || e.name === 'dist') continue;
|
|
501
|
+
const full = path.join(dir, e.name);
|
|
502
|
+
if (e.isDirectory()) {
|
|
503
|
+
walkDir(full, out);
|
|
504
|
+
} else if (e.isFile()) {
|
|
505
|
+
const ext = path.extname(e.name);
|
|
506
|
+
if (TARGET_EXTS.includes(ext)) out.push(full);
|
|
507
|
+
else if (e.name.endsWith('.sh')) out.push(full);
|
|
508
|
+
else if (e.name === 'package.json') out.push(full);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function gatherFilesFromArgs(args) {
|
|
514
|
+
const out = [];
|
|
515
|
+
for (const a of args) {
|
|
516
|
+
const full = path.resolve(a);
|
|
517
|
+
if (!fs.existsSync(full)) continue;
|
|
518
|
+
const st = fs.statSync(full);
|
|
519
|
+
if (st.isDirectory()) walkDir(full, out);
|
|
520
|
+
else out.push(full);
|
|
521
|
+
}
|
|
522
|
+
return out;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function gatherStagedFiles() {
|
|
526
|
+
const stdout = execSync('git diff --cached --name-only --diff-filter=ACMR', {
|
|
527
|
+
cwd: ROOT,
|
|
528
|
+
encoding: 'utf8',
|
|
529
|
+
});
|
|
530
|
+
return stdout
|
|
531
|
+
.split('\n')
|
|
532
|
+
.map((s) => s.trim())
|
|
533
|
+
.filter(Boolean)
|
|
534
|
+
.map((rel) => path.resolve(ROOT, rel))
|
|
535
|
+
.filter((full) => {
|
|
536
|
+
try {
|
|
537
|
+
return fs.statSync(full).isFile();
|
|
538
|
+
} catch {
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function gatherAll() {
|
|
545
|
+
const out = [];
|
|
546
|
+
for (const d of TARGET_DIRS) {
|
|
547
|
+
walkDir(path.join(ROOT, d), out);
|
|
548
|
+
}
|
|
549
|
+
// Also include package.json at root.
|
|
550
|
+
out.push(path.join(ROOT, 'package.json'));
|
|
551
|
+
return out;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// ── Main ───────────────────────────────────────────────────────────
|
|
555
|
+
|
|
556
|
+
function main() {
|
|
557
|
+
const argv = process.argv.slice(2);
|
|
558
|
+
let files;
|
|
559
|
+
if (argv.includes('--staged')) {
|
|
560
|
+
files = gatherStagedFiles();
|
|
561
|
+
} else if (argv.length > 0) {
|
|
562
|
+
files = gatherFilesFromArgs(argv);
|
|
563
|
+
} else {
|
|
564
|
+
files = gatherAll();
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
for (const file of files) {
|
|
568
|
+
let text;
|
|
569
|
+
try {
|
|
570
|
+
text = fs.readFileSync(file, 'utf8');
|
|
571
|
+
} catch {
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const rel = path.relative(ROOT, file);
|
|
576
|
+
// package.json scripts grep
|
|
577
|
+
if (path.basename(file) === 'package.json' && rel === 'package.json') {
|
|
578
|
+
lintPackageJsonScripts(file, text);
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
// Shell grep
|
|
582
|
+
if (file.endsWith('.sh')) {
|
|
583
|
+
lintShellFile(file, text);
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
// Allowlist check (closed)
|
|
587
|
+
if (ALLOWLIST.has(rel)) continue;
|
|
588
|
+
if (hasAllowComment(text)) continue;
|
|
589
|
+
|
|
590
|
+
// AST lint for ts/js/mjs/cjs
|
|
591
|
+
try {
|
|
592
|
+
lintTsFile(file, text);
|
|
593
|
+
} catch (err) {
|
|
594
|
+
// Parse failure → emit a soft warning, not a violation.
|
|
595
|
+
process.stderr.write(`[lint-no-direct-destructive] failed to parse ${rel}: ${err.message}\n`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (violations.length === 0) {
|
|
600
|
+
return 0;
|
|
601
|
+
}
|
|
602
|
+
process.stderr.write('\n');
|
|
603
|
+
process.stderr.write('╔════════════════════════════════════════════════════════════════════╗\n');
|
|
604
|
+
process.stderr.write('║ lint-no-direct-destructive — VIOLATIONS ║\n');
|
|
605
|
+
process.stderr.write('╚════════════════════════════════════════════════════════════════════╝\n');
|
|
606
|
+
process.stderr.write('\n');
|
|
607
|
+
for (const v of violations) {
|
|
608
|
+
const rel = path.relative(ROOT, v.file);
|
|
609
|
+
process.stderr.write(` ${rel}:${v.line}:${v.col}\n ${v.msg}\n\n`);
|
|
610
|
+
}
|
|
611
|
+
process.stderr.write(`Total: ${violations.length} violation(s).\n`);
|
|
612
|
+
process.stderr.write('See docs/specs/COMPREHENSIVE-DESTRUCTIVE-TOOL-CONTAINMENT-SPEC.md for guidance.\n');
|
|
613
|
+
return 1;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const code = main();
|
|
617
|
+
process.exit(code);
|