codexgpt 1.0.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/AGENTS.example.md +20 -0
- package/CHANGELOG.md +342 -0
- package/CHATGPT_PROMPT.md +12 -0
- package/CLOUDFLARED_VERIFIED_INSTALL.md +94 -0
- package/CODEX_PROMPT.md +12 -0
- package/CONTRIBUTING.md +62 -0
- package/DOMAIN_SETUP.md +275 -0
- package/FAQ.md +366 -0
- package/FAQ_ZH.md +329 -0
- package/LICENSE +21 -0
- package/PUBLIC_LAUNCH_CHECKLIST.md +111 -0
- package/README.md +490 -0
- package/README_ZH.md +747 -0
- package/SECURITY.md +295 -0
- package/config.example.env +103 -0
- package/dist/access/fullAccessLease.js +148 -0
- package/dist/access/fullAccessLease.js.map +1 -0
- package/dist/access/protectedRoots.js +122 -0
- package/dist/access/protectedRoots.js.map +1 -0
- package/dist/access/rootAdmission.js +347 -0
- package/dist/access/rootAdmission.js.map +1 -0
- package/dist/analysis/cache.js +27 -0
- package/dist/analysis/cache.js.map +1 -0
- package/dist/analysis/classify.js +102 -0
- package/dist/analysis/classify.js.map +1 -0
- package/dist/analysis/extract.js +139 -0
- package/dist/analysis/extract.js.map +1 -0
- package/dist/analysis/graph.js +22 -0
- package/dist/analysis/graph.js.map +1 -0
- package/dist/analysis/impact.js +167 -0
- package/dist/analysis/impact.js.map +1 -0
- package/dist/analysis/index.js +215 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/analysis/inventory.js +51 -0
- package/dist/analysis/inventory.js.map +1 -0
- package/dist/analysis/providers.js +24 -0
- package/dist/analysis/providers.js.map +1 -0
- package/dist/analysis/rank.js +27 -0
- package/dist/analysis/rank.js.map +1 -0
- package/dist/analysis/types.js +8 -0
- package/dist/analysis/types.js.map +1 -0
- package/dist/audit/canonicalJson.js +79 -0
- package/dist/audit/canonicalJson.js.map +1 -0
- package/dist/audit/diagnostics.js +128 -0
- package/dist/audit/diagnostics.js.map +1 -0
- package/dist/audit/index.js +12 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/lifecycleV3.js +53 -0
- package/dist/audit/lifecycleV3.js.map +1 -0
- package/dist/audit/lifecycleV4.js +70 -0
- package/dist/audit/lifecycleV4.js.map +1 -0
- package/dist/audit/lock.js +195 -0
- package/dist/audit/lock.js.map +1 -0
- package/dist/audit/queryTool.js +120 -0
- package/dist/audit/queryTool.js.map +1 -0
- package/dist/audit/runtime.js +48 -0
- package/dist/audit/runtime.js.map +1 -0
- package/dist/audit/schemas.js +640 -0
- package/dist/audit/schemas.js.map +1 -0
- package/dist/audit/store.js +1309 -0
- package/dist/audit/store.js.map +1 -0
- package/dist/audit/transactionParticipant.js +45 -0
- package/dist/audit/transactionParticipant.js.map +1 -0
- package/dist/audit/types.js +11 -0
- package/dist/audit/types.js.map +1 -0
- package/dist/auth/audit.js +44 -0
- package/dist/auth/audit.js.map +1 -0
- package/dist/auth/authorizationStore.js +333 -0
- package/dist/auth/authorizationStore.js.map +1 -0
- package/dist/auth/challenges.js +25 -0
- package/dist/auth/challenges.js.map +1 -0
- package/dist/auth/clientStore.js +469 -0
- package/dist/auth/clientStore.js.map +1 -0
- package/dist/auth/cloudflareConfig.js +112 -0
- package/dist/auth/cloudflareConfig.js.map +1 -0
- package/dist/auth/configuration.js +167 -0
- package/dist/auth/configuration.js.map +1 -0
- package/dist/auth/credentialStore.js +65 -0
- package/dist/auth/credentialStore.js.map +1 -0
- package/dist/auth/deploymentLock.js +220 -0
- package/dist/auth/deploymentLock.js.map +1 -0
- package/dist/auth/deploymentRegistry.js +149 -0
- package/dist/auth/deploymentRegistry.js.map +1 -0
- package/dist/auth/errors.js +26 -0
- package/dist/auth/errors.js.map +1 -0
- package/dist/auth/grantStore.js +307 -0
- package/dist/auth/grantStore.js.map +1 -0
- package/dist/auth/index.js +39 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/keyManager.js +148 -0
- package/dist/auth/keyManager.js.map +1 -0
- package/dist/auth/localAdminSession.js +173 -0
- package/dist/auth/localAdminSession.js.map +1 -0
- package/dist/auth/metadata.js +69 -0
- package/dist/auth/metadata.js.map +1 -0
- package/dist/auth/oauthProvider.js +589 -0
- package/dist/auth/oauthProvider.js.map +1 -0
- package/dist/auth/ownerApproval.js +51 -0
- package/dist/auth/ownerApproval.js.map +1 -0
- package/dist/auth/policyIdentity.js +189 -0
- package/dist/auth/policyIdentity.js.map +1 -0
- package/dist/auth/rateLimits.js +66 -0
- package/dist/auth/rateLimits.js.map +1 -0
- package/dist/auth/recovery.js +275 -0
- package/dist/auth/recovery.js.map +1 -0
- package/dist/auth/requestContext.js +34 -0
- package/dist/auth/requestContext.js.map +1 -0
- package/dist/auth/resourceMiddleware.js +147 -0
- package/dist/auth/resourceMiddleware.js.map +1 -0
- package/dist/auth/runtimeStatus.js +51 -0
- package/dist/auth/runtimeStatus.js.map +1 -0
- package/dist/auth/schemas.js +84 -0
- package/dist/auth/schemas.js.map +1 -0
- package/dist/auth/stateStore.js +468 -0
- package/dist/auth/stateStore.js.map +1 -0
- package/dist/auth/tokenService.js +631 -0
- package/dist/auth/tokenService.js.map +1 -0
- package/dist/auth/toolSecurity.js +108 -0
- package/dist/auth/toolSecurity.js.map +1 -0
- package/dist/auth/types.js +6 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/auth/windowsDpapi.js +193 -0
- package/dist/auth/windowsDpapi.js.map +1 -0
- package/dist/bashOps.js +292 -0
- package/dist/bashOps.js.map +1 -0
- package/dist/capabilitiesOps.js +412 -0
- package/dist/capabilitiesOps.js.map +1 -0
- package/dist/changesets/crypto.js +92 -0
- package/dist/changesets/crypto.js.map +1 -0
- package/dist/changesets/index.js +9 -0
- package/dist/changesets/index.js.map +1 -0
- package/dist/changesets/moveStore.js +237 -0
- package/dist/changesets/moveStore.js.map +1 -0
- package/dist/changesets/moveUndo.js +386 -0
- package/dist/changesets/moveUndo.js.map +1 -0
- package/dist/changesets/schemas.js +234 -0
- package/dist/changesets/schemas.js.map +1 -0
- package/dist/changesets/store.js +573 -0
- package/dist/changesets/store.js.map +1 -0
- package/dist/changesets/types.js +9 -0
- package/dist/changesets/types.js.map +1 -0
- package/dist/changesets/undo.js +525 -0
- package/dist/changesets/undo.js.map +1 -0
- package/dist/changesets/unifiedUndo.js +31 -0
- package/dist/changesets/unifiedUndo.js.map +1 -0
- package/dist/cliEnvironment.js +85 -0
- package/dist/cliEnvironment.js.map +1 -0
- package/dist/codexSessions.js +733 -0
- package/dist/codexSessions.js.map +1 -0
- package/dist/codexgptSupertool.js +216 -0
- package/dist/codexgptSupertool.js.map +1 -0
- package/dist/codexproSupertool.js +204 -0
- package/dist/codexproSupertool.js.map +1 -0
- package/dist/config.js +733 -0
- package/dist/config.js.map +1 -0
- package/dist/control/localApprovalClient.js +287 -0
- package/dist/control/localApprovalClient.js.map +1 -0
- package/dist/control/localApprovalServer.js +466 -0
- package/dist/control/localApprovalServer.js.map +1 -0
- package/dist/control/runtime.js +178 -0
- package/dist/control/runtime.js.map +1 -0
- package/dist/control/schemas.js +184 -0
- package/dist/control/schemas.js.map +1 -0
- package/dist/control/windowsLocalControl.js +348 -0
- package/dist/control/windowsLocalControl.js.map +1 -0
- package/dist/fsOps.js +541 -0
- package/dist/fsOps.js.map +1 -0
- package/dist/git/admission.js +26 -0
- package/dist/git/admission.js.map +1 -0
- package/dist/git/branchService.js +51 -0
- package/dist/git/branchService.js.map +1 -0
- package/dist/git/capabilities.js +143 -0
- package/dist/git/capabilities.js.map +1 -0
- package/dist/git/commitService.js +344 -0
- package/dist/git/commitService.js.map +1 -0
- package/dist/git/durableState.js +151 -0
- package/dist/git/durableState.js.map +1 -0
- package/dist/git/execution.js +451 -0
- package/dist/git/execution.js.map +1 -0
- package/dist/git/fileTransaction.js +50 -0
- package/dist/git/fileTransaction.js.map +1 -0
- package/dist/git/indexService.js +421 -0
- package/dist/git/indexService.js.map +1 -0
- package/dist/git/integrations.js +653 -0
- package/dist/git/integrations.js.map +1 -0
- package/dist/git/locks.js +287 -0
- package/dist/git/locks.js.map +1 -0
- package/dist/git/mutationContext.js +92 -0
- package/dist/git/mutationContext.js.map +1 -0
- package/dist/git/mutationJournal.js +91 -0
- package/dist/git/mutationJournal.js.map +1 -0
- package/dist/git/mutationService.js +352 -0
- package/dist/git/mutationService.js.map +1 -0
- package/dist/git/normalization.js +57 -0
- package/dist/git/normalization.js.map +1 -0
- package/dist/git/objectQuarantine.js +243 -0
- package/dist/git/objectQuarantine.js.map +1 -0
- package/dist/git/opaqueRecordStore.js +187 -0
- package/dist/git/opaqueRecordStore.js.map +1 -0
- package/dist/git/operationStore.js +287 -0
- package/dist/git/operationStore.js.map +1 -0
- package/dist/git/parsers.js +311 -0
- package/dist/git/parsers.js.map +1 -0
- package/dist/git/privateIndex.js +79 -0
- package/dist/git/privateIndex.js.map +1 -0
- package/dist/git/productionBootstrap.js +39 -0
- package/dist/git/productionBootstrap.js.map +1 -0
- package/dist/git/readService.js +846 -0
- package/dist/git/readService.js.map +1 -0
- package/dist/git/recovery.js +417 -0
- package/dist/git/recovery.js.map +1 -0
- package/dist/git/repositoryIdentity.js +611 -0
- package/dist/git/repositoryIdentity.js.map +1 -0
- package/dist/git/repositoryStore.js +237 -0
- package/dist/git/repositoryStore.js.map +1 -0
- package/dist/git/resources.js +113 -0
- package/dist/git/resources.js.map +1 -0
- package/dist/git/restoreService.js +178 -0
- package/dist/git/restoreService.js.map +1 -0
- package/dist/git/reviewToken.js +86 -0
- package/dist/git/reviewToken.js.map +1 -0
- package/dist/git/stashService.js +566 -0
- package/dist/git/stashService.js.map +1 -0
- package/dist/git/stateToken.js +139 -0
- package/dist/git/stateToken.js.map +1 -0
- package/dist/gitOps.js +147 -0
- package/dist/gitOps.js.map +1 -0
- package/dist/guard.js +608 -0
- package/dist/guard.js.map +1 -0
- package/dist/guidance/diagnostics.js +14 -0
- package/dist/guidance/diagnostics.js.map +1 -0
- package/dist/guidance/instructions.js +167 -0
- package/dist/guidance/instructions.js.map +1 -0
- package/dist/guidance/mode.js +18 -0
- package/dist/guidance/mode.js.map +1 -0
- package/dist/guidance/openaiSkillMetadata.js +29 -0
- package/dist/guidance/openaiSkillMetadata.js.map +1 -0
- package/dist/guidance/safeTextReader.js +171 -0
- package/dist/guidance/safeTextReader.js.map +1 -0
- package/dist/guidance/skillCatalog.js +70 -0
- package/dist/guidance/skillCatalog.js.map +1 -0
- package/dist/guidance/skillDiscovery.js +353 -0
- package/dist/guidance/skillDiscovery.js.map +1 -0
- package/dist/guidance/skillMetadata.js +115 -0
- package/dist/guidance/skillMetadata.js.map +1 -0
- package/dist/guidance/skillResources.js +146 -0
- package/dist/guidance/skillResources.js.map +1 -0
- package/dist/handoffOps.js +449 -0
- package/dist/handoffOps.js.map +1 -0
- package/dist/http/localAdminApp.js +203 -0
- package/dist/http/localAdminApp.js.map +1 -0
- package/dist/http/oauthMcpRuntime.js +294 -0
- package/dist/http/oauthMcpRuntime.js.map +1 -0
- package/dist/http/publicApp.js +350 -0
- package/dist/http/publicApp.js.map +1 -0
- package/dist/http/securityHeaders.js +11 -0
- package/dist/http/securityHeaders.js.map +1 -0
- package/dist/http.js +2076 -0
- package/dist/http.js.map +1 -0
- package/dist/moves/engine.js +1079 -0
- package/dist/moves/engine.js.map +1 -0
- package/dist/moves/index.js +6 -0
- package/dist/moves/index.js.map +1 -0
- package/dist/moves/planner.js +270 -0
- package/dist/moves/planner.js.map +1 -0
- package/dist/moves/recovery.js +557 -0
- package/dist/moves/recovery.js.map +1 -0
- package/dist/moves/service.js +300 -0
- package/dist/moves/service.js.map +1 -0
- package/dist/moves/types.js +11 -0
- package/dist/moves/types.js.map +1 -0
- package/dist/mutations/index.js +4 -0
- package/dist/mutations/index.js.map +1 -0
- package/dist/mutations/lifecycle.js +165 -0
- package/dist/mutations/lifecycle.js.map +1 -0
- package/dist/mutations/localService.js +163 -0
- package/dist/mutations/localService.js.map +1 -0
- package/dist/mutations/runtime.js +322 -0
- package/dist/mutations/runtime.js.map +1 -0
- package/dist/mutations/types.js +2 -0
- package/dist/mutations/types.js.map +1 -0
- package/dist/mutations/writers.js +360 -0
- package/dist/mutations/writers.js.map +1 -0
- package/dist/patchOps.js +420 -0
- package/dist/patchOps.js.map +1 -0
- package/dist/policy/approval.js +306 -0
- package/dist/policy/approval.js.map +1 -0
- package/dist/policy/audit.js +88 -0
- package/dist/policy/audit.js.map +1 -0
- package/dist/policy/authorizationFacts.js +187 -0
- package/dist/policy/authorizationFacts.js.map +1 -0
- package/dist/policy/compat.js +104 -0
- package/dist/policy/compat.js.map +1 -0
- package/dist/policy/context.js +31 -0
- package/dist/policy/context.js.map +1 -0
- package/dist/policy/enforcement.js +46 -0
- package/dist/policy/enforcement.js.map +1 -0
- package/dist/policy/evaluator.js +338 -0
- package/dist/policy/evaluator.js.map +1 -0
- package/dist/policy/executionCapabilities.js +80 -0
- package/dist/policy/executionCapabilities.js.map +1 -0
- package/dist/policy/executionResources.js +192 -0
- package/dist/policy/executionResources.js.map +1 -0
- package/dist/policy/fullAccessResources.js +23 -0
- package/dist/policy/fullAccessResources.js.map +1 -0
- package/dist/policy/hardPolicy.js +50 -0
- package/dist/policy/hardPolicy.js.map +1 -0
- package/dist/policy/identity.js +142 -0
- package/dist/policy/identity.js.map +1 -0
- package/dist/policy/integration.js +331 -0
- package/dist/policy/integration.js.map +1 -0
- package/dist/policy/pendingApprovals.js +301 -0
- package/dist/policy/pendingApprovals.js.map +1 -0
- package/dist/policy/profileStore.js +367 -0
- package/dist/policy/profileStore.js.map +1 -0
- package/dist/policy/resources.js +249 -0
- package/dist/policy/resources.js.map +1 -0
- package/dist/policy/runtime.js +713 -0
- package/dist/policy/runtime.js.map +1 -0
- package/dist/policy/schemas.js +625 -0
- package/dist/policy/schemas.js.map +1 -0
- package/dist/policy/toolPolicy.js +141 -0
- package/dist/policy/toolPolicy.js.map +1 -0
- package/dist/policy/transport.js +10 -0
- package/dist/policy/transport.js.map +1 -0
- package/dist/policy/types.js +31 -0
- package/dist/policy/types.js.map +1 -0
- package/dist/proContext.js +675 -0
- package/dist/proContext.js.map +1 -0
- package/dist/process/authority.js +18 -0
- package/dist/process/authority.js.map +1 -0
- package/dist/process/backendDiscovery.js +83 -0
- package/dist/process/backendDiscovery.js.map +1 -0
- package/dist/process/commandCompiler.js +71 -0
- package/dist/process/commandCompiler.js.map +1 -0
- package/dist/process/outputCursor.js +67 -0
- package/dist/process/outputCursor.js.map +1 -0
- package/dist/process/outputQuota.js +94 -0
- package/dist/process/outputQuota.js.map +1 -0
- package/dist/process/outputRing.js +138 -0
- package/dist/process/outputRing.js.map +1 -0
- package/dist/process/processAuditCoordinator.js +52 -0
- package/dist/process/processAuditCoordinator.js.map +1 -0
- package/dist/process/processManager.js +311 -0
- package/dist/process/processManager.js.map +1 -0
- package/dist/process/runCommand.js +332 -0
- package/dist/process/runCommand.js.map +1 -0
- package/dist/process/streamingRedactor.js +105 -0
- package/dist/process/streamingRedactor.js.map +1 -0
- package/dist/process/types.js +2 -0
- package/dist/process/types.js.map +1 -0
- package/dist/process/windowsHostClient.js +604 -0
- package/dist/process/windowsHostClient.js.map +1 -0
- package/dist/process/windowsHostProtocol.js +270 -0
- package/dist/process/windowsHostProtocol.js.map +1 -0
- package/dist/process/windowsPersistentBackend.js +263 -0
- package/dist/process/windowsPersistentBackend.js.map +1 -0
- package/dist/productionRuntime.js +907 -0
- package/dist/productionRuntime.js.map +1 -0
- package/dist/profileStore.js +231 -0
- package/dist/profileStore.js.map +1 -0
- package/dist/redact.js +124 -0
- package/dist/redact.js.map +1 -0
- package/dist/searchOps.js +186 -0
- package/dist/searchOps.js.map +1 -0
- package/dist/selfTestOps.js +786 -0
- package/dist/selfTestOps.js.map +1 -0
- package/dist/semantic/budgets.js +12 -0
- package/dist/semantic/budgets.js.map +1 -0
- package/dist/semantic/builtin/typescriptAssets.js +68 -0
- package/dist/semantic/builtin/typescriptAssets.js.map +1 -0
- package/dist/semantic/builtin/typescriptProvider.js +211 -0
- package/dist/semantic/builtin/typescriptProvider.js.map +1 -0
- package/dist/semantic/builtin/typescriptWorker.js +365 -0
- package/dist/semantic/builtin/typescriptWorker.js.map +1 -0
- package/dist/semantic/diffPreview.js +36 -0
- package/dist/semantic/diffPreview.js.map +1 -0
- package/dist/semantic/index.js +11 -0
- package/dist/semantic/index.js.map +1 -0
- package/dist/semantic/manager.js +522 -0
- package/dist/semantic/manager.js.map +1 -0
- package/dist/semantic/positions.js +86 -0
- package/dist/semantic/positions.js.map +1 -0
- package/dist/semantic/previewStore.js +276 -0
- package/dist/semantic/previewStore.js.map +1 -0
- package/dist/semantic/projectResolver.js +309 -0
- package/dist/semantic/projectResolver.js.map +1 -0
- package/dist/semantic/sourceSnapshot.js +54 -0
- package/dist/semantic/sourceSnapshot.js.map +1 -0
- package/dist/semantic/status.js +46 -0
- package/dist/semantic/status.js.map +1 -0
- package/dist/semantic/types.js +2 -0
- package/dist/semantic/types.js.map +1 -0
- package/dist/server.js +8676 -0
- package/dist/server.js.map +1 -0
- package/dist/stdio.js +64 -0
- package/dist/stdio.js.map +1 -0
- package/dist/toolCardWidget.js +2000 -0
- package/dist/toolCardWidget.js.map +1 -0
- package/dist/tools/contracts/catalog.js +111 -0
- package/dist/tools/contracts/catalog.js.map +1 -0
- package/dist/tools/contracts/index.js +8 -0
- package/dist/tools/contracts/index.js.map +1 -0
- package/dist/tools/contracts/registration.js +27 -0
- package/dist/tools/contracts/registration.js.map +1 -0
- package/dist/tools/contracts/schemas.js +11 -0
- package/dist/tools/contracts/schemas.js.map +1 -0
- package/dist/tools/contracts/types.js +2 -0
- package/dist/tools/contracts/types.js.map +1 -0
- package/dist/tools/contracts/v3.js +76 -0
- package/dist/tools/contracts/v3.js.map +1 -0
- package/dist/tools/contracts/v4.js +39 -0
- package/dist/tools/contracts/v4.js.map +1 -0
- package/dist/tools/contracts/v5.js +20 -0
- package/dist/tools/contracts/v5.js.map +1 -0
- package/dist/tools/phase3dServer.js +350 -0
- package/dist/tools/phase3dServer.js.map +1 -0
- package/dist/tools/schemas/applyPatch.js +388 -0
- package/dist/tools/schemas/applyPatch.js.map +1 -0
- package/dist/tools/schemas/bash.js +202 -0
- package/dist/tools/schemas/bash.js.map +1 -0
- package/dist/tools/schemas/closeWorkspace.js +105 -0
- package/dist/tools/schemas/closeWorkspace.js.map +1 -0
- package/dist/tools/schemas/codexContext.js +402 -0
- package/dist/tools/schemas/codexContext.js.map +1 -0
- package/dist/tools/schemas/codexSessions.js +328 -0
- package/dist/tools/schemas/codexSessions.js.map +1 -0
- package/dist/tools/schemas/codexgpt.js +1068 -0
- package/dist/tools/schemas/codexgpt.js.map +1 -0
- package/dist/tools/schemas/codexgptInventory.js +359 -0
- package/dist/tools/schemas/codexgptInventory.js.map +1 -0
- package/dist/tools/schemas/codexgptSelfTest.js +563 -0
- package/dist/tools/schemas/codexgptSelfTest.js.map +1 -0
- package/dist/tools/schemas/codexpro.js +917 -0
- package/dist/tools/schemas/codexpro.js.map +1 -0
- package/dist/tools/schemas/codexproInventory.js +352 -0
- package/dist/tools/schemas/codexproInventory.js.map +1 -0
- package/dist/tools/schemas/codexproSelfTest.js +546 -0
- package/dist/tools/schemas/codexproSelfTest.js.map +1 -0
- package/dist/tools/schemas/common.js +21 -0
- package/dist/tools/schemas/common.js.map +1 -0
- package/dist/tools/schemas/createTaskWorktree.js +51 -0
- package/dist/tools/schemas/createTaskWorktree.js.map +1 -0
- package/dist/tools/schemas/edit.js +321 -0
- package/dist/tools/schemas/edit.js.map +1 -0
- package/dist/tools/schemas/execution.js +330 -0
- package/dist/tools/schemas/execution.js.map +1 -0
- package/dist/tools/schemas/exportProContext.js +370 -0
- package/dist/tools/schemas/exportProContext.js.map +1 -0
- package/dist/tools/schemas/getTaskWorktree.js +19 -0
- package/dist/tools/schemas/getTaskWorktree.js.map +1 -0
- package/dist/tools/schemas/gitBranch.js +42 -0
- package/dist/tools/schemas/gitBranch.js.map +1 -0
- package/dist/tools/schemas/gitCommit.js +40 -0
- package/dist/tools/schemas/gitCommit.js.map +1 -0
- package/dist/tools/schemas/gitCreateBranch.js +22 -0
- package/dist/tools/schemas/gitCreateBranch.js.map +1 -0
- package/dist/tools/schemas/gitDiff.js +222 -0
- package/dist/tools/schemas/gitDiff.js.map +1 -0
- package/dist/tools/schemas/gitLog.js +41 -0
- package/dist/tools/schemas/gitLog.js.map +1 -0
- package/dist/tools/schemas/gitRestore.js +47 -0
- package/dist/tools/schemas/gitRestore.js.map +1 -0
- package/dist/tools/schemas/gitStage.js +33 -0
- package/dist/tools/schemas/gitStage.js.map +1 -0
- package/dist/tools/schemas/gitStash.js +102 -0
- package/dist/tools/schemas/gitStash.js.map +1 -0
- package/dist/tools/schemas/gitStatus.js +226 -0
- package/dist/tools/schemas/gitStatus.js.map +1 -0
- package/dist/tools/schemas/gitV4Common.js +205 -0
- package/dist/tools/schemas/gitV4Common.js.map +1 -0
- package/dist/tools/schemas/guidance.js +40 -0
- package/dist/tools/schemas/guidance.js.map +1 -0
- package/dist/tools/schemas/handoffToAgent.js +276 -0
- package/dist/tools/schemas/handoffToAgent.js.map +1 -0
- package/dist/tools/schemas/handoffToCodex.js +137 -0
- package/dist/tools/schemas/handoffToCodex.js.map +1 -0
- package/dist/tools/schemas/inspectWorkspace.js +379 -0
- package/dist/tools/schemas/inspectWorkspace.js.map +1 -0
- package/dist/tools/schemas/listTaskWorktrees.js +21 -0
- package/dist/tools/schemas/listTaskWorktrees.js.map +1 -0
- package/dist/tools/schemas/listWorkspaces.js +134 -0
- package/dist/tools/schemas/listWorkspaces.js.map +1 -0
- package/dist/tools/schemas/loadSkill.js +619 -0
- package/dist/tools/schemas/loadSkill.js.map +1 -0
- package/dist/tools/schemas/mergeTaskWorktree.js +105 -0
- package/dist/tools/schemas/mergeTaskWorktree.js.map +1 -0
- package/dist/tools/schemas/movePaths.js +175 -0
- package/dist/tools/schemas/movePaths.js.map +1 -0
- package/dist/tools/schemas/openCurrentWorkspace.js +174 -0
- package/dist/tools/schemas/openCurrentWorkspace.js.map +1 -0
- package/dist/tools/schemas/openFullAccessWorkspace.js +81 -0
- package/dist/tools/schemas/openFullAccessWorkspace.js.map +1 -0
- package/dist/tools/schemas/openWorkspace.js +193 -0
- package/dist/tools/schemas/openWorkspace.js.map +1 -0
- package/dist/tools/schemas/queryAuditEvents.js +193 -0
- package/dist/tools/schemas/queryAuditEvents.js.map +1 -0
- package/dist/tools/schemas/read.js +199 -0
- package/dist/tools/schemas/read.js.map +1 -0
- package/dist/tools/schemas/readCodexSession.js +353 -0
- package/dist/tools/schemas/readCodexSession.js.map +1 -0
- package/dist/tools/schemas/readHandoff.js +446 -0
- package/dist/tools/schemas/readHandoff.js.map +1 -0
- package/dist/tools/schemas/removeTaskWorktree.js +40 -0
- package/dist/tools/schemas/removeTaskWorktree.js.map +1 -0
- package/dist/tools/schemas/search.js +235 -0
- package/dist/tools/schemas/search.js.map +1 -0
- package/dist/tools/schemas/semantic.js +195 -0
- package/dist/tools/schemas/semantic.js.map +1 -0
- package/dist/tools/schemas/serverConfig.js +157 -0
- package/dist/tools/schemas/serverConfig.js.map +1 -0
- package/dist/tools/schemas/showChanges.js +283 -0
- package/dist/tools/schemas/showChanges.js.map +1 -0
- package/dist/tools/schemas/transactionResult.js +11 -0
- package/dist/tools/schemas/transactionResult.js.map +1 -0
- package/dist/tools/schemas/tree.js +136 -0
- package/dist/tools/schemas/tree.js.map +1 -0
- package/dist/tools/schemas/undoChangeSet.js +152 -0
- package/dist/tools/schemas/undoChangeSet.js.map +1 -0
- package/dist/tools/schemas/waitForHandoff.js +462 -0
- package/dist/tools/schemas/waitForHandoff.js.map +1 -0
- package/dist/tools/schemas/workspaceSnapshot.js +145 -0
- package/dist/tools/schemas/workspaceSnapshot.js.map +1 -0
- package/dist/tools/schemas/write.js +285 -0
- package/dist/tools/schemas/write.js.map +1 -0
- package/dist/transactions/atomicFs.js +438 -0
- package/dist/transactions/atomicFs.js.map +1 -0
- package/dist/transactions/atomicStateFile.js +190 -0
- package/dist/transactions/atomicStateFile.js.map +1 -0
- package/dist/transactions/engine.js +574 -0
- package/dist/transactions/engine.js.map +1 -0
- package/dist/transactions/index.js +12 -0
- package/dist/transactions/index.js.map +1 -0
- package/dist/transactions/installation.js +158 -0
- package/dist/transactions/installation.js.map +1 -0
- package/dist/transactions/manifestV2Store.js +96 -0
- package/dist/transactions/manifestV2Store.js.map +1 -0
- package/dist/transactions/participantRecovery.js +98 -0
- package/dist/transactions/participantRecovery.js.map +1 -0
- package/dist/transactions/recovery.js +513 -0
- package/dist/transactions/recovery.js.map +1 -0
- package/dist/transactions/schemas.js +365 -0
- package/dist/transactions/schemas.js.map +1 -0
- package/dist/transactions/stateRoot.js +63 -0
- package/dist/transactions/stateRoot.js.map +1 -0
- package/dist/transactions/types.js +11 -0
- package/dist/transactions/types.js.map +1 -0
- package/dist/transactions/workspaceLock.js +201 -0
- package/dist/transactions/workspaceLock.js.map +1 -0
- package/dist/workspaceOps.js +924 -0
- package/dist/workspaceOps.js.map +1 -0
- package/dist/worktrees/candidateWorkspace.js +566 -0
- package/dist/worktrees/candidateWorkspace.js.map +1 -0
- package/dist/worktrees/manager.js +258 -0
- package/dist/worktrees/manager.js.map +1 -0
- package/dist/worktrees/materializer.js +60 -0
- package/dist/worktrees/materializer.js.map +1 -0
- package/dist/worktrees/mergeExecute.js +498 -0
- package/dist/worktrees/mergeExecute.js.map +1 -0
- package/dist/worktrees/mergePlanStore.js +247 -0
- package/dist/worktrees/mergePlanStore.js.map +1 -0
- package/dist/worktrees/mergePrepare.js +709 -0
- package/dist/worktrees/mergePrepare.js.map +1 -0
- package/dist/worktrees/recovery.js +294 -0
- package/dist/worktrees/recovery.js.map +1 -0
- package/dist/worktrees/remove.js +296 -0
- package/dist/worktrees/remove.js.map +1 -0
- package/dist/worktrees/remover.js +57 -0
- package/dist/worktrees/remover.js.map +1 -0
- package/dist/worktrees/root.js +47 -0
- package/dist/worktrees/root.js.map +1 -0
- package/dist/worktrees/service.js +245 -0
- package/dist/worktrees/service.js.map +1 -0
- package/dist/worktrees/store.js +156 -0
- package/dist/worktrees/store.js.map +1 -0
- package/dist/worktrees/treeManifest.js +106 -0
- package/dist/worktrees/treeManifest.js.map +1 -0
- package/dist/worktrees/verificationReceipts.js +215 -0
- package/dist/worktrees/verificationReceipts.js.map +1 -0
- package/dist/worktrees/verificationTerminal.js +23 -0
- package/dist/worktrees/verificationTerminal.js.map +1 -0
- package/dist/worktrees/workspaceAuthority.js +64 -0
- package/dist/worktrees/workspaceAuthority.js.map +1 -0
- package/docs/favicon.svg +5 -0
- package/docs/index.html +624 -0
- package/docs/og.svg +52 -0
- package/docs/script.js +33 -0
- package/docs/star.svg +11 -0
- package/docs/styles.css +1571 -0
- package/docs/zh.html +428 -0
- package/package.json +121 -0
- package/scripts/analysis-cli-smoke.mjs +81 -0
- package/scripts/analysis-smoke.mjs +179 -0
- package/scripts/atomic-file.mjs +137 -0
- package/scripts/ci-change-classifier.mjs +59 -0
- package/scripts/ci-failure-summary.mjs +92 -0
- package/scripts/cloudflared-installer.mjs +215 -0
- package/scripts/cloudflared-release.mjs +63 -0
- package/scripts/codexgpt-entry.mjs +235 -0
- package/scripts/codexgpt.mjs +4987 -0
- package/scripts/connector-auth-output-shim.cjs +66 -0
- package/scripts/doctor-smoke.mjs +90 -0
- package/scripts/doctor.mjs +165 -0
- package/scripts/exact-head-ci.mjs +126 -0
- package/scripts/execute-handoff-smoke-platform-compat.mjs +38 -0
- package/scripts/execute-handoff-smoke.mjs +1117 -0
- package/scripts/g8-u-live-probe.mjs +208 -0
- package/scripts/http-smoke-compat.mjs +151 -0
- package/scripts/http-smoke.mjs +776 -0
- package/scripts/long-task-runner.mjs +1037 -0
- package/scripts/oauth-admin.mjs +1608 -0
- package/scripts/output-bounds.mjs +29 -0
- package/scripts/owned-temp-root.d.mts +50 -0
- package/scripts/owned-temp-root.mjs +567 -0
- package/scripts/phase-4a-smoke.mjs +36 -0
- package/scripts/policy-windows-spike.mjs +135 -0
- package/scripts/pro-apply.mjs +152 -0
- package/scripts/pro-bundle.mjs +121 -0
- package/scripts/pro-smoke.mjs +97 -0
- package/scripts/process-identity.mjs +67 -0
- package/scripts/repository-policy.mjs +166 -0
- package/scripts/run-and-summarize.mjs +134 -0
- package/scripts/run-smoke.mjs +52 -0
- package/scripts/run-with-cleanup.mjs +98 -0
- package/scripts/settings-smoke-platform-compat.mjs +85 -0
- package/scripts/settings-smoke.mjs +769 -0
- package/scripts/smoke-platform-compat.mjs +302 -0
- package/scripts/smoke.mjs +1180 -0
- package/scripts/stress-contract-compat.mjs +94 -0
- package/scripts/stress.mjs +882 -0
- package/scripts/test-domains.mjs +247 -0
- package/scripts/test-execution-profile-manifest.mjs +273 -0
- package/scripts/test-execution-profiles.mjs +147 -0
- package/scripts/test-performance-reporter.mjs +291 -0
- package/scripts/toolchain-manager.mjs +266 -0
- package/scripts/toolchains.json +19 -0
- package/scripts/windows-conpty-probe-child.mjs +82 -0
- package/scripts/windows-conpty-worker.ps1 +24 -0
- package/scripts/windows-credential-host-manifest.json +16 -0
- package/scripts/windows-credential-host-protocol-v1.json +16 -0
- package/scripts/windows-credential-host.cs +256 -0
- package/scripts/windows-credential-host.ps1 +26 -0
- package/scripts/windows-local-control-manifest.json +23 -0
- package/scripts/windows-local-control-spike.cs +738 -0
- package/scripts/windows-local-control-spike.mjs +415 -0
- package/scripts/windows-local-control-spike.ps1 +35 -0
- package/scripts/windows-local-control.cs +738 -0
- package/scripts/windows-local-control.ps1 +35 -0
- package/scripts/windows-native-api-inventory-v1.json +90 -0
- package/scripts/windows-process-host-manifest.json +20 -0
- package/scripts/windows-process-host-protocol-v1.json +59 -0
- package/scripts/windows-process-host.cs +2385 -0
- package/scripts/windows-process-host.ps1 +59 -0
- package/scripts/windows-realpath-shim.cjs +78 -0
|
@@ -0,0 +1,1037 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
3
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import fsp from "node:fs/promises";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { pathToFileURL } from "node:url";
|
|
9
|
+
import { writeJsonAtomicFile, writeJsonAtomicFileSync } from "./atomic-file.mjs";
|
|
10
|
+
import { createOwnedTempEnvironment, sweepStaleOwnedTempRoots } from "./owned-temp-root.mjs";
|
|
11
|
+
import { processCreationTime, processIsAlive } from "./process-identity.mjs";
|
|
12
|
+
|
|
13
|
+
export { processCreationTime } from "./process-identity.mjs";
|
|
14
|
+
|
|
15
|
+
export const RUNNER_SCHEMA_VERSION = 2;
|
|
16
|
+
export const DEFAULT_LOG_LIMIT_BYTES = 1024 * 1024;
|
|
17
|
+
export const MAX_LOG_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
18
|
+
export const DEFAULT_RUN_RETENTION_COUNT = 20;
|
|
19
|
+
export const DEFAULT_RUN_RETENTION_DAYS = 14;
|
|
20
|
+
export const WORKER_LEASE_MS = 60_000;
|
|
21
|
+
export const WORKER_LEASE_RENEW_MS = 15_000;
|
|
22
|
+
export const WORKER_LEASE_RETRY_MS = 1_000;
|
|
23
|
+
|
|
24
|
+
export function assertWorkerLeaseTarget(targetPath) {
|
|
25
|
+
try {
|
|
26
|
+
const stat = fs.lstatSync(targetPath, { bigint: true });
|
|
27
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1n) {
|
|
28
|
+
const error = new Error("Worker lease target is not a single-link ordinary file.");
|
|
29
|
+
error.code = "WORKER_LEASE_PATH_UNSAFE";
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
} catch (error) {
|
|
33
|
+
if (error?.code === "ENOENT") return;
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function startWorkerLeaseRenewal(options) {
|
|
39
|
+
const publish = options.publish;
|
|
40
|
+
const renewMs = options.renewMs ?? WORKER_LEASE_RENEW_MS;
|
|
41
|
+
const retryMs = options.retryMs ?? WORKER_LEASE_RETRY_MS;
|
|
42
|
+
const setTimer = options.setTimer ?? setTimeout;
|
|
43
|
+
const clearTimer = options.clearTimer ?? clearTimeout;
|
|
44
|
+
let stopped = false;
|
|
45
|
+
let timer;
|
|
46
|
+
|
|
47
|
+
const schedule = (delay) => {
|
|
48
|
+
timer = setTimer(async () => {
|
|
49
|
+
let succeeded = true;
|
|
50
|
+
try {
|
|
51
|
+
await publish();
|
|
52
|
+
} catch {
|
|
53
|
+
succeeded = false;
|
|
54
|
+
}
|
|
55
|
+
if (!stopped) schedule(succeeded ? renewMs : retryMs);
|
|
56
|
+
}, delay);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
schedule(renewMs);
|
|
60
|
+
return () => {
|
|
61
|
+
stopped = true;
|
|
62
|
+
if (timer !== undefined) clearTimer(timer);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const RUN_PRUNE_CLAIM_PATTERN = /^\.codexgpt-run-prune-[a-f0-9]{32}$/u;
|
|
67
|
+
|
|
68
|
+
export function createDetachedRunnerEnvironment({ hostEnv = process.env, platform = process.platform } = {}) {
|
|
69
|
+
const environment = { ...hostEnv };
|
|
70
|
+
if (platform !== "win32") return environment;
|
|
71
|
+
const userProfile = environment.USERPROFILE ||
|
|
72
|
+
(environment.HOMEDRIVE && environment.HOMEPATH
|
|
73
|
+
? `${environment.HOMEDRIVE}${environment.HOMEPATH}`
|
|
74
|
+
: undefined);
|
|
75
|
+
if (!userProfile) return environment;
|
|
76
|
+
environment.USERPROFILE ??= userProfile;
|
|
77
|
+
environment.APPDATA ??= path.win32.join(userProfile, "AppData", "Roaming");
|
|
78
|
+
environment.LOCALAPPDATA ??= path.win32.join(userProfile, "AppData", "Local");
|
|
79
|
+
return environment;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const argv = process.argv.slice(2);
|
|
83
|
+
const action = argv[0] ?? "list";
|
|
84
|
+
const commandSeparator = argv.indexOf("--");
|
|
85
|
+
const optionArgv = commandSeparator === -1 ? argv : argv.slice(0, commandSeparator);
|
|
86
|
+
|
|
87
|
+
function option(name, fallback) {
|
|
88
|
+
const index = optionArgv.indexOf(name);
|
|
89
|
+
return index === -1 ? fallback : optionArgv[index + 1];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function fail(message, code = 1) {
|
|
93
|
+
console.error(message);
|
|
94
|
+
process.exit(code);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function safeSegment(value, fallback) {
|
|
98
|
+
const normalized = String(value ?? "").trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
99
|
+
return normalized || fallback;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function looksSecret(value) {
|
|
103
|
+
return /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{20,}\b|\bgithub_pat_[A-Za-z0-9_]{20,}\b|Authorization\s*:\s*Bearer\s+\S+|[?&](?:token|access_token|key)=/i.test(String(value));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function positiveInteger(value, fallback, maximum = Number.MAX_SAFE_INTEGER) {
|
|
107
|
+
if (value === undefined) return fallback;
|
|
108
|
+
const parsed = Number(value);
|
|
109
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0 || parsed > maximum) {
|
|
110
|
+
fail(`Expected a positive integer no greater than ${maximum}.`, 2);
|
|
111
|
+
}
|
|
112
|
+
return parsed;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function stateRoot() {
|
|
116
|
+
return path.resolve(option("--root", path.join(".ai-bridge", "runs")));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function resolveRunDirectory(root, runId) {
|
|
120
|
+
const resolvedRoot = path.resolve(root);
|
|
121
|
+
if (
|
|
122
|
+
typeof runId !== "string" ||
|
|
123
|
+
runId.length < 1 ||
|
|
124
|
+
runId.length > 256 ||
|
|
125
|
+
path.basename(runId) !== runId ||
|
|
126
|
+
runId === "." ||
|
|
127
|
+
runId === ".."
|
|
128
|
+
) throw new Error("Invalid run id.");
|
|
129
|
+
const resolved = path.resolve(resolvedRoot, runId);
|
|
130
|
+
if (!sameResolvedPath(path.dirname(resolved), resolvedRoot)) throw new Error("Invalid run id.");
|
|
131
|
+
return resolved;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function runDirectory(root, runId) {
|
|
135
|
+
try {
|
|
136
|
+
return resolveRunDirectory(root, runId);
|
|
137
|
+
} catch {
|
|
138
|
+
fail("Invalid run id.", 2);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function digest(value) {
|
|
143
|
+
return createHash("sha256").update(value).digest("hex");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function commandDigest(command) {
|
|
147
|
+
return digest(JSON.stringify({
|
|
148
|
+
cwd: command.cwd,
|
|
149
|
+
argv: command.argv,
|
|
150
|
+
tempRoot: command.tempRoot,
|
|
151
|
+
logLimitBytes: command.logLimitBytes,
|
|
152
|
+
retentionCount: command.retentionCount,
|
|
153
|
+
retentionDays: command.retentionDays
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function workerCommandDigest({ executable, script, directory }) {
|
|
158
|
+
return digest(JSON.stringify({ executable: path.resolve(executable), script: path.resolve(script), directory: path.resolve(directory) }));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function readJson(file) {
|
|
162
|
+
try {
|
|
163
|
+
return JSON.parse(await fsp.readFile(file, "utf8"));
|
|
164
|
+
} catch (error) {
|
|
165
|
+
if (error?.code === "ENOENT") return undefined;
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function writeJsonAtomic(directory, filename, value) {
|
|
171
|
+
await writeJsonAtomicFile(path.join(directory, filename), value);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function statIdentity(stat) {
|
|
175
|
+
return {
|
|
176
|
+
dev: String(stat.dev),
|
|
177
|
+
ino: String(stat.ino)
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function ensureDirectoryWithoutLinks(target) {
|
|
182
|
+
const absolute = path.resolve(target);
|
|
183
|
+
const parsed = path.parse(absolute);
|
|
184
|
+
let current = parsed.root;
|
|
185
|
+
const remainder = absolute.slice(parsed.root.length).split(path.sep).filter(Boolean);
|
|
186
|
+
for (const segment of remainder) {
|
|
187
|
+
current = path.join(current, segment);
|
|
188
|
+
try {
|
|
189
|
+
const entry = await fsp.lstat(current, { bigint: true });
|
|
190
|
+
if (entry.isSymbolicLink()) throw new Error(`Runner state path contains a symbolic link or junction: ${current}`);
|
|
191
|
+
if (!entry.isDirectory()) throw new Error(`Runner state path is not a directory: ${current}`);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
if (error?.code !== "ENOENT") throw error;
|
|
194
|
+
await fsp.mkdir(current);
|
|
195
|
+
const entry = await fsp.lstat(current, { bigint: true });
|
|
196
|
+
if (entry.isSymbolicLink() || !entry.isDirectory()) throw new Error(`Runner state directory creation was replaced: ${current}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const real = await fsp.realpath(absolute);
|
|
200
|
+
const stat = await fsp.stat(absolute, { bigint: true });
|
|
201
|
+
return { path: path.resolve(real), identity: statIdentity(stat) };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function verifyDirectoryWithoutLinks(target, expectedIdentity) {
|
|
205
|
+
const checked = await ensureDirectoryWithoutLinks(target);
|
|
206
|
+
if (expectedIdentity && (checked.identity.dev !== expectedIdentity.dev || checked.identity.ino !== expectedIdentity.ino)) {
|
|
207
|
+
throw new Error(`Runner state directory identity changed: ${target}`);
|
|
208
|
+
}
|
|
209
|
+
return checked;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function boundedTaskkillError(result) {
|
|
213
|
+
return String(result?.stderr || result?.stdout || `exit ${result?.status ?? "unknown"}`)
|
|
214
|
+
.replace(/\s+/g, " ")
|
|
215
|
+
.trim()
|
|
216
|
+
.slice(0, 512);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export async function stopExactWindowsTree(options) {
|
|
220
|
+
const pid = options.pid;
|
|
221
|
+
const expectedCreationTime = options.expectedCreationTime;
|
|
222
|
+
const creationTimeFor = options.creationTimeFor ?? processCreationTime;
|
|
223
|
+
const taskkill = options.taskkill ?? ((targetPid) =>
|
|
224
|
+
spawnSync("taskkill.exe", ["/PID", String(targetPid), "/T", "/F"], {
|
|
225
|
+
encoding: "utf8",
|
|
226
|
+
windowsHide: true,
|
|
227
|
+
timeout: 5_000,
|
|
228
|
+
killSignal: "SIGKILL"
|
|
229
|
+
}));
|
|
230
|
+
const wait = options.wait ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
231
|
+
let current = await creationTimeFor(pid);
|
|
232
|
+
if (!current || current !== expectedCreationTime) {
|
|
233
|
+
throw new Error("Exact worker identity changed before termination.");
|
|
234
|
+
}
|
|
235
|
+
let lastError = "";
|
|
236
|
+
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
237
|
+
const result = taskkill(pid);
|
|
238
|
+
if (result?.status === 0) return;
|
|
239
|
+
lastError = boundedTaskkillError(result);
|
|
240
|
+
current = await creationTimeFor(pid);
|
|
241
|
+
if (!current || current !== expectedCreationTime) return;
|
|
242
|
+
if (attempt < 3) await wait(attempt * 100);
|
|
243
|
+
}
|
|
244
|
+
throw new Error(`taskkill failed after 3 exact-owned attempts: ${lastError}`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
class BoundedTail {
|
|
248
|
+
constructor(limitBytes) {
|
|
249
|
+
this.limitBytes = limitBytes;
|
|
250
|
+
this.chunks = [];
|
|
251
|
+
this.retainedBytes = 0;
|
|
252
|
+
this.totalBytes = 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
push(chunk) {
|
|
256
|
+
const buffer = Buffer.from(chunk);
|
|
257
|
+
this.totalBytes += buffer.length;
|
|
258
|
+
if (buffer.length >= this.limitBytes) {
|
|
259
|
+
this.chunks = [buffer.subarray(buffer.length - this.limitBytes)];
|
|
260
|
+
this.retainedBytes = this.limitBytes;
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
this.chunks.push(buffer);
|
|
264
|
+
this.retainedBytes += buffer.length;
|
|
265
|
+
while (this.retainedBytes > this.limitBytes && this.chunks.length > 0) {
|
|
266
|
+
const excess = this.retainedBytes - this.limitBytes;
|
|
267
|
+
const first = this.chunks[0];
|
|
268
|
+
if (first.length <= excess) {
|
|
269
|
+
this.chunks.shift();
|
|
270
|
+
this.retainedBytes -= first.length;
|
|
271
|
+
} else {
|
|
272
|
+
this.chunks[0] = first.subarray(excess);
|
|
273
|
+
this.retainedBytes -= excess;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
bytes() {
|
|
279
|
+
return Buffer.concat(this.chunks, this.retainedBytes);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
summary() {
|
|
283
|
+
return {
|
|
284
|
+
totalBytes: this.totalBytes,
|
|
285
|
+
retainedBytes: this.retainedBytes,
|
|
286
|
+
droppedBytes: Math.max(0, this.totalBytes - this.retainedBytes),
|
|
287
|
+
truncated: this.totalBytes > this.retainedBytes
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function workerEvidenceMatches(metadata, evidence) {
|
|
293
|
+
return Boolean(metadata && evidence &&
|
|
294
|
+
metadata.schemaVersion === RUNNER_SCHEMA_VERSION &&
|
|
295
|
+
evidence.schemaVersion === RUNNER_SCHEMA_VERSION &&
|
|
296
|
+
evidence.runId === metadata.runId &&
|
|
297
|
+
evidence.workerPid === metadata.workerPid &&
|
|
298
|
+
evidence.workerNonce === metadata.workerNonce &&
|
|
299
|
+
evidence.commandDigest === metadata.commandDigest &&
|
|
300
|
+
evidence.workerCommandDigest === metadata.workerCommandDigest &&
|
|
301
|
+
evidence.workerCreationTime === metadata.workerCreationTime);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function workerLeaseActive(metadata, evidence, lease, nowMs = Date.now()) {
|
|
305
|
+
if (!workerEvidenceMatches(metadata, evidence) || !workerEvidenceMatches(metadata, lease)) return false;
|
|
306
|
+
if (lease.phase !== "running" && lease.phase !== "finalizing") return false;
|
|
307
|
+
const publishedAt = Date.parse(lease.publishedAt);
|
|
308
|
+
const expiresAt = Date.parse(lease.expiresAt);
|
|
309
|
+
return Number.isFinite(publishedAt) &&
|
|
310
|
+
Number.isFinite(expiresAt) &&
|
|
311
|
+
publishedAt <= nowMs + 5_000 &&
|
|
312
|
+
expiresAt >= nowMs &&
|
|
313
|
+
expiresAt > publishedAt &&
|
|
314
|
+
expiresAt - publishedAt <= WORKER_LEASE_MS;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function workerLeaseIdentity(lease) {
|
|
318
|
+
return {
|
|
319
|
+
owned: false,
|
|
320
|
+
reason: lease?.phase === "finalizing"
|
|
321
|
+
? "terminal_publication_in_progress"
|
|
322
|
+
: "worker_lease_active"
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export async function verifyWorkerIdentity(metadata, evidence, options = {}) {
|
|
327
|
+
if (!metadata || metadata.schemaVersion !== RUNNER_SCHEMA_VERSION) return { owned: false, reason: "unsupported_metadata" };
|
|
328
|
+
if (!evidence || evidence.schemaVersion !== RUNNER_SCHEMA_VERSION) return { owned: false, reason: "missing_worker_evidence" };
|
|
329
|
+
if (!workerEvidenceMatches(metadata, evidence)) return { owned: false, reason: "worker_evidence_mismatch" };
|
|
330
|
+
const readCreationTime = options.processCreationTime ?? processCreationTime;
|
|
331
|
+
const isAlive = options.processIsAlive ?? processIsAlive;
|
|
332
|
+
const currentCreationTime = await readCreationTime(metadata.workerPid);
|
|
333
|
+
if (!currentCreationTime) {
|
|
334
|
+
return isAlive(metadata.workerPid)
|
|
335
|
+
? { owned: false, reason: "process_identity_unavailable" }
|
|
336
|
+
: { owned: false, reason: "process_identity_mismatch" };
|
|
337
|
+
}
|
|
338
|
+
if (currentCreationTime !== metadata.workerCreationTime) {
|
|
339
|
+
return { owned: false, reason: "process_identity_mismatch" };
|
|
340
|
+
}
|
|
341
|
+
return { owned: true, reason: "exact_worker_identity" };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async function readRunFiles(directory) {
|
|
345
|
+
const metadata = await readJson(path.join(directory, "metadata.json"));
|
|
346
|
+
if (!metadata) return undefined;
|
|
347
|
+
const expectedRunId = path.basename(directory);
|
|
348
|
+
if (metadata.runId !== expectedRunId) {
|
|
349
|
+
throw new Error("Run metadata id does not match its containing directory.");
|
|
350
|
+
}
|
|
351
|
+
await verifyDirectoryWithoutLinks(directory, metadata.directoryIdentity);
|
|
352
|
+
const [result, stopped, evidence, lease] = await Promise.all([
|
|
353
|
+
readJson(path.join(directory, "result.json")),
|
|
354
|
+
readJson(path.join(directory, "stopped.json")),
|
|
355
|
+
readJson(path.join(directory, "worker-evidence.json")),
|
|
356
|
+
readJson(path.join(directory, "worker-lease.json"))
|
|
357
|
+
]);
|
|
358
|
+
if (result && result.runId !== expectedRunId) {
|
|
359
|
+
throw new Error("Run result id does not match its containing directory.");
|
|
360
|
+
}
|
|
361
|
+
if (stopped && stopped.runId !== expectedRunId) {
|
|
362
|
+
throw new Error("Run stop record id does not match its containing directory.");
|
|
363
|
+
}
|
|
364
|
+
if (lease && lease.runId !== expectedRunId) {
|
|
365
|
+
throw new Error("Run worker lease id does not match its containing directory.");
|
|
366
|
+
}
|
|
367
|
+
return { metadata, result, stopped, evidence, lease };
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export async function waitForTerminalPublication(directory, deadlineMs = 5_000, observation = {}) {
|
|
371
|
+
const deadline = Date.now() + deadlineMs;
|
|
372
|
+
while (Date.now() < deadline) {
|
|
373
|
+
const [result, stopped, lease] = await Promise.all([
|
|
374
|
+
readJson(path.join(directory, "result.json")),
|
|
375
|
+
readJson(path.join(directory, "stopped.json")),
|
|
376
|
+
readJson(path.join(directory, "worker-lease.json"))
|
|
377
|
+
]);
|
|
378
|
+
if (
|
|
379
|
+
result ||
|
|
380
|
+
stopped ||
|
|
381
|
+
workerLeaseActive(observation.metadata, observation.evidence, lease)
|
|
382
|
+
) return { result, stopped, lease };
|
|
383
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
384
|
+
}
|
|
385
|
+
return { result: undefined, stopped: undefined, lease: undefined };
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export async function runState(directory) {
|
|
389
|
+
const files = await readRunFiles(directory);
|
|
390
|
+
if (!files) return undefined;
|
|
391
|
+
const { metadata, result, stopped, evidence, lease } = files;
|
|
392
|
+
let status;
|
|
393
|
+
let identity;
|
|
394
|
+
if (stopped) {
|
|
395
|
+
status = "stopped";
|
|
396
|
+
identity = { owned: false, reason: "stop_recorded" };
|
|
397
|
+
} else if (result) {
|
|
398
|
+
status = "completed";
|
|
399
|
+
identity = { owned: false, reason: "result_recorded" };
|
|
400
|
+
} else if (workerLeaseActive(metadata, evidence, lease)) {
|
|
401
|
+
status = "running";
|
|
402
|
+
identity = workerLeaseIdentity(lease);
|
|
403
|
+
} else {
|
|
404
|
+
identity = await verifyWorkerIdentity(metadata, evidence);
|
|
405
|
+
if (identity.owned || identity.reason === "process_identity_unavailable") {
|
|
406
|
+
status = "running";
|
|
407
|
+
} else {
|
|
408
|
+
const exactEvidence = workerEvidenceMatches(metadata, evidence);
|
|
409
|
+
const terminal = exactEvidence
|
|
410
|
+
? await waitForTerminalPublication(directory, 5_000, { metadata, evidence })
|
|
411
|
+
: {
|
|
412
|
+
result: await readJson(path.join(directory, "result.json")),
|
|
413
|
+
stopped: await readJson(path.join(directory, "stopped.json")),
|
|
414
|
+
lease: undefined
|
|
415
|
+
};
|
|
416
|
+
if (terminal.stopped) {
|
|
417
|
+
status = "stopped";
|
|
418
|
+
return { ...metadata, status, identity: { owned: false, reason: "stop_recorded" }, result: terminal.result ?? null, stopped: terminal.stopped };
|
|
419
|
+
}
|
|
420
|
+
if (terminal.result) {
|
|
421
|
+
status = "completed";
|
|
422
|
+
return { ...metadata, status, identity: { owned: false, reason: "result_recorded" }, result: terminal.result, stopped: null };
|
|
423
|
+
}
|
|
424
|
+
if (workerLeaseActive(metadata, evidence, terminal.lease)) {
|
|
425
|
+
status = "running";
|
|
426
|
+
return { ...metadata, status, identity: workerLeaseIdentity(terminal.lease), result: null, stopped: null };
|
|
427
|
+
}
|
|
428
|
+
status = "stale";
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return {
|
|
432
|
+
...metadata,
|
|
433
|
+
status,
|
|
434
|
+
identity,
|
|
435
|
+
result: result ?? null,
|
|
436
|
+
stopped: stopped ?? null
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async function listStates(root, options = {}) {
|
|
441
|
+
let entries = [];
|
|
442
|
+
try {
|
|
443
|
+
await verifyDirectoryWithoutLinks(root);
|
|
444
|
+
entries = await fsp.readdir(root, { withFileTypes: true });
|
|
445
|
+
} catch (error) {
|
|
446
|
+
if (error?.code === "ENOENT") return [];
|
|
447
|
+
throw error;
|
|
448
|
+
}
|
|
449
|
+
const states = [];
|
|
450
|
+
for (const entry of entries) {
|
|
451
|
+
if (
|
|
452
|
+
!entry.isDirectory() ||
|
|
453
|
+
entry.isSymbolicLink() ||
|
|
454
|
+
RUN_PRUNE_CLAIM_PATTERN.test(entry.name) ||
|
|
455
|
+
entry.name === options.excludeRunId
|
|
456
|
+
) continue;
|
|
457
|
+
try {
|
|
458
|
+
const state = await runState(path.join(root, entry.name));
|
|
459
|
+
if (state) states.push(state);
|
|
460
|
+
} catch (error) {
|
|
461
|
+
states.push({ runId: entry.name, status: "stale", identity: { owned: false, reason: "state_path_invalid" }, error: error.message });
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return states.sort((left, right) => String(right.startedAt ?? "").localeCompare(String(left.startedAt ?? "")));
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function identityEquals(left, right) {
|
|
468
|
+
return Boolean(left && right && String(left.dev) === String(right.dev) && String(left.ino) === String(right.ino));
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function sameResolvedPath(left, right) {
|
|
472
|
+
const a = path.resolve(left);
|
|
473
|
+
const b = path.resolve(right);
|
|
474
|
+
return process.platform === "win32"
|
|
475
|
+
? a.toLocaleLowerCase("en-US") === b.toLocaleLowerCase("en-US")
|
|
476
|
+
: a === b;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function terminalTimestamp(state) {
|
|
480
|
+
const value = state.result?.completedAt ?? state.stopped?.stoppedAt ?? state.startedAt;
|
|
481
|
+
const parsed = Date.parse(value ?? "");
|
|
482
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function terminalRecordMatches(metadata, result, stopped) {
|
|
486
|
+
if (metadata?.schemaVersion === RUNNER_SCHEMA_VERSION) {
|
|
487
|
+
return Boolean(
|
|
488
|
+
(result?.schemaVersion === RUNNER_SCHEMA_VERSION && result.runId === metadata.runId) ||
|
|
489
|
+
(stopped?.schemaVersion === RUNNER_SCHEMA_VERSION && stopped.runId === metadata.runId)
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
if (metadata?.schemaVersion === 1) {
|
|
493
|
+
return Boolean(result?.runId === metadata.runId || stopped?.runId === metadata.runId);
|
|
494
|
+
}
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
async function recoverClaimedRunDirectories(root) {
|
|
499
|
+
const summary = { scanned: 0, removed: 0, failed: 0, errors: [], missing: false };
|
|
500
|
+
let entries;
|
|
501
|
+
let canonicalRoot;
|
|
502
|
+
try {
|
|
503
|
+
const rootLexical = await fsp.lstat(root, { bigint: true });
|
|
504
|
+
if (!rootLexical.isDirectory() || rootLexical.isSymbolicLink()) {
|
|
505
|
+
throw new Error("Runner state root is not a safe directory.");
|
|
506
|
+
}
|
|
507
|
+
canonicalRoot = await fsp.realpath(root);
|
|
508
|
+
entries = await fsp.readdir(root, { withFileTypes: true });
|
|
509
|
+
} catch (error) {
|
|
510
|
+
if (error?.code === "ENOENT") {
|
|
511
|
+
summary.missing = true;
|
|
512
|
+
return summary;
|
|
513
|
+
}
|
|
514
|
+
throw error;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
for (const entry of entries) {
|
|
518
|
+
if (!entry.isDirectory() || entry.isSymbolicLink() || !RUN_PRUNE_CLAIM_PATTERN.test(entry.name)) continue;
|
|
519
|
+
summary.scanned += 1;
|
|
520
|
+
try {
|
|
521
|
+
const claimed = path.join(root, entry.name);
|
|
522
|
+
const lexical = await fsp.lstat(claimed, { bigint: true });
|
|
523
|
+
if (!lexical.isDirectory() || lexical.isSymbolicLink()) throw new Error("Claimed run path is unsafe.");
|
|
524
|
+
const canonical = await fsp.realpath(claimed);
|
|
525
|
+
if (!sameResolvedPath(canonical, path.join(canonicalRoot, entry.name))) {
|
|
526
|
+
throw new Error("Claimed run path is not canonical.");
|
|
527
|
+
}
|
|
528
|
+
const [metadata, result, stopped] = await Promise.all([
|
|
529
|
+
readJson(path.join(claimed, "metadata.json")),
|
|
530
|
+
readJson(path.join(claimed, "result.json")),
|
|
531
|
+
readJson(path.join(claimed, "stopped.json"))
|
|
532
|
+
]);
|
|
533
|
+
if (
|
|
534
|
+
!metadata ||
|
|
535
|
+
![1, RUNNER_SCHEMA_VERSION].includes(metadata.schemaVersion) ||
|
|
536
|
+
typeof metadata.runId !== "string" ||
|
|
537
|
+
(metadata.schemaVersion === RUNNER_SCHEMA_VERSION &&
|
|
538
|
+
!identityEquals(statIdentity(lexical), metadata.directoryIdentity))
|
|
539
|
+
) throw new Error("Claimed run metadata is invalid.");
|
|
540
|
+
let original;
|
|
541
|
+
try {
|
|
542
|
+
original = resolveRunDirectory(root, metadata.runId);
|
|
543
|
+
} catch {
|
|
544
|
+
throw new Error("Claimed run id is invalid.");
|
|
545
|
+
}
|
|
546
|
+
if (!terminalRecordMatches(metadata, result, stopped)) throw new Error("Claimed run is not terminal.");
|
|
547
|
+
await fsp.rm(claimed, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
548
|
+
summary.removed += 1;
|
|
549
|
+
} catch (error) {
|
|
550
|
+
summary.failed += 1;
|
|
551
|
+
if (summary.errors.length < 8) {
|
|
552
|
+
summary.errors.push({
|
|
553
|
+
claim: entry.name,
|
|
554
|
+
error: String(error?.code ?? error?.message ?? error).slice(0, 512)
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return summary;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
async function terminalRunWorkerActive(root, state) {
|
|
563
|
+
if (state.schemaVersion !== RUNNER_SCHEMA_VERSION) return false;
|
|
564
|
+
const directory = resolveRunDirectory(root, state.runId);
|
|
565
|
+
const evidence = await readJson(path.join(directory, "worker-evidence.json"));
|
|
566
|
+
const identity = await verifyWorkerIdentity(state, evidence);
|
|
567
|
+
return identity.owned || identity.reason === "process_identity_unavailable";
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
async function removeTerminalRun(root, state) {
|
|
571
|
+
if (state.status !== "completed" && state.status !== "stopped") {
|
|
572
|
+
throw new Error(`Refusing to prune non-terminal run ${state.runId}.`);
|
|
573
|
+
}
|
|
574
|
+
const directory = resolveRunDirectory(root, state.runId);
|
|
575
|
+
const lexical = await fsp.lstat(directory, { bigint: true });
|
|
576
|
+
const lexicalIdentity = statIdentity(lexical);
|
|
577
|
+
const expectedIdentity = state.schemaVersion === 1 ? lexicalIdentity : state.directoryIdentity;
|
|
578
|
+
if (
|
|
579
|
+
![1, RUNNER_SCHEMA_VERSION].includes(state.schemaVersion) ||
|
|
580
|
+
!lexical.isDirectory() ||
|
|
581
|
+
lexical.isSymbolicLink() ||
|
|
582
|
+
!identityEquals(lexicalIdentity, expectedIdentity) ||
|
|
583
|
+
!terminalRecordMatches(state, state.result, state.stopped)
|
|
584
|
+
) {
|
|
585
|
+
throw new Error(`Run directory identity or terminal evidence is invalid before pruning: ${state.runId}`);
|
|
586
|
+
}
|
|
587
|
+
const canonicalRoot = await fsp.realpath(root);
|
|
588
|
+
const canonical = await fsp.realpath(directory);
|
|
589
|
+
const expectedCanonical = path.join(canonicalRoot, path.basename(directory));
|
|
590
|
+
if (!sameResolvedPath(canonical, expectedCanonical)) throw new Error(`Run directory is not canonical: ${state.runId}`);
|
|
591
|
+
const stable = await fsp.lstat(directory, { bigint: true });
|
|
592
|
+
if (!identityEquals(statIdentity(stable), expectedIdentity)) {
|
|
593
|
+
throw new Error(`Run directory identity changed during pruning: ${state.runId}`);
|
|
594
|
+
}
|
|
595
|
+
const claimed = path.join(root, `.codexgpt-run-prune-${randomBytes(16).toString("hex")}`);
|
|
596
|
+
await fsp.rename(directory, claimed);
|
|
597
|
+
const claimedLexical = await fsp.lstat(claimed, { bigint: true });
|
|
598
|
+
if (!claimedLexical.isDirectory() || claimedLexical.isSymbolicLink() || !identityEquals(statIdentity(claimedLexical), expectedIdentity)) {
|
|
599
|
+
throw new Error(`Claimed run directory identity changed: ${state.runId}`);
|
|
600
|
+
}
|
|
601
|
+
const claimedCanonical = await fsp.realpath(claimed);
|
|
602
|
+
const expectedClaimedCanonical = path.join(canonicalRoot, path.basename(claimed));
|
|
603
|
+
if (!sameResolvedPath(claimedCanonical, expectedClaimedCanonical)) {
|
|
604
|
+
throw new Error(`Claimed run directory is not canonical: ${state.runId}`);
|
|
605
|
+
}
|
|
606
|
+
try {
|
|
607
|
+
await fsp.rm(claimed, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
608
|
+
} catch (error) {
|
|
609
|
+
try {
|
|
610
|
+
await fsp.rename(claimed, directory);
|
|
611
|
+
} catch {
|
|
612
|
+
// Preserve the claimed directory rather than deleting an identity-ambiguous replacement.
|
|
613
|
+
}
|
|
614
|
+
throw error;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
export async function pruneTerminalRuns(root, options = {}) {
|
|
619
|
+
const keepCount = positiveInteger(options.keepCount, DEFAULT_RUN_RETENTION_COUNT, 10_000);
|
|
620
|
+
const maxAgeDays = positiveInteger(options.maxAgeDays, DEFAULT_RUN_RETENTION_DAYS, 36_500);
|
|
621
|
+
const preserveRunId = options.preserveRunId;
|
|
622
|
+
const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000;
|
|
623
|
+
const claimed = await recoverClaimedRunDirectories(root);
|
|
624
|
+
if (claimed.missing) {
|
|
625
|
+
return { scanned: 0, removed: 0, retained: 0, failed: 0, invalid: 0, keepCount, maxAgeDays, errors: [], claimed };
|
|
626
|
+
}
|
|
627
|
+
const states = await listStates(root, { excludeRunId: preserveRunId });
|
|
628
|
+
const invalidStates = states.filter((state) => state.status === "stale" && typeof state.error === "string");
|
|
629
|
+
const terminal = states
|
|
630
|
+
.filter((state) => state.status === "completed" || state.status === "stopped")
|
|
631
|
+
.filter((state) => state.runId !== preserveRunId)
|
|
632
|
+
.sort((left, right) => terminalTimestamp(right) - terminalTimestamp(left));
|
|
633
|
+
const retainedAllowance = Math.max(0, keepCount - (preserveRunId ? 1 : 0));
|
|
634
|
+
const result = {
|
|
635
|
+
scanned: terminal.length,
|
|
636
|
+
removed: 0,
|
|
637
|
+
retained: 0,
|
|
638
|
+
failed: 0,
|
|
639
|
+
invalid: invalidStates.length,
|
|
640
|
+
keepCount,
|
|
641
|
+
maxAgeDays,
|
|
642
|
+
errors: invalidStates.slice(0, 8).map((state) => ({
|
|
643
|
+
runId: state.runId,
|
|
644
|
+
error: String(state.error).slice(0, 512)
|
|
645
|
+
})),
|
|
646
|
+
claimed
|
|
647
|
+
};
|
|
648
|
+
for (let index = 0; index < terminal.length; index += 1) {
|
|
649
|
+
const state = terminal[index];
|
|
650
|
+
const expiredByCount = index >= retainedAllowance;
|
|
651
|
+
const expiredByAge = terminalTimestamp(state) < cutoff;
|
|
652
|
+
if (!expiredByCount && !expiredByAge) {
|
|
653
|
+
result.retained += 1;
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
try {
|
|
657
|
+
if (await terminalRunWorkerActive(root, state)) {
|
|
658
|
+
result.retained += 1;
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
await removeTerminalRun(root, state);
|
|
662
|
+
result.removed += 1;
|
|
663
|
+
} catch (error) {
|
|
664
|
+
result.failed += 1;
|
|
665
|
+
if (result.errors.length < 8) {
|
|
666
|
+
result.errors.push({
|
|
667
|
+
runId: state.runId,
|
|
668
|
+
error: String(error?.code ?? error?.message ?? error).slice(0, 512)
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return result;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
async function waitForWorkerEvidence(directory, expectedNonce, deadlineMs = 10_000) {
|
|
677
|
+
const deadline = Date.now() + deadlineMs;
|
|
678
|
+
while (Date.now() < deadline) {
|
|
679
|
+
const evidence = await readJson(path.join(directory, "worker-evidence.json"));
|
|
680
|
+
if (evidence?.workerNonce === expectedNonce) return evidence;
|
|
681
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
682
|
+
}
|
|
683
|
+
throw new Error("Detached runner worker did not publish identity evidence.");
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
async function worker() {
|
|
687
|
+
const directory = path.resolve(option("--run-dir"));
|
|
688
|
+
const command = await readJson(path.join(directory, "command.json"));
|
|
689
|
+
if (
|
|
690
|
+
!command ||
|
|
691
|
+
command.schemaVersion !== RUNNER_SCHEMA_VERSION ||
|
|
692
|
+
!Array.isArray(command.argv) ||
|
|
693
|
+
command.argv.length === 0 ||
|
|
694
|
+
typeof command.cwd !== "string" ||
|
|
695
|
+
typeof command.tempRoot !== "string" ||
|
|
696
|
+
!Number.isSafeInteger(command.logLimitBytes) ||
|
|
697
|
+
!Number.isSafeInteger(command.retentionCount) ||
|
|
698
|
+
!Number.isSafeInteger(command.retentionDays) ||
|
|
699
|
+
typeof command.commandDigest !== "string" ||
|
|
700
|
+
!/^[a-f0-9]{64}$/u.test(command.commandDigest) ||
|
|
701
|
+
command.commandDigest !== commandDigest(command)
|
|
702
|
+
) {
|
|
703
|
+
fail("Worker command file or digest is invalid.", 2);
|
|
704
|
+
}
|
|
705
|
+
await verifyDirectoryWithoutLinks(directory, command.directoryIdentity);
|
|
706
|
+
const workerCreationTime = await processCreationTime(process.pid);
|
|
707
|
+
if (!workerCreationTime) fail("Worker process creation time is unavailable.", 2);
|
|
708
|
+
const workerScript = path.resolve(process.argv[1]);
|
|
709
|
+
const evidence = {
|
|
710
|
+
schemaVersion: RUNNER_SCHEMA_VERSION,
|
|
711
|
+
runId: command.runId,
|
|
712
|
+
workerPid: process.pid,
|
|
713
|
+
workerNonce: command.workerNonce,
|
|
714
|
+
workerCreationTime,
|
|
715
|
+
commandDigest: command.commandDigest,
|
|
716
|
+
workerCommandDigest: workerCommandDigest({ executable: process.execPath, script: workerScript, directory }),
|
|
717
|
+
publishedAt: new Date().toISOString()
|
|
718
|
+
};
|
|
719
|
+
await writeJsonAtomic(directory, "worker-evidence.json", evidence);
|
|
720
|
+
let leasePhase = "running";
|
|
721
|
+
let leaseWrite = Promise.resolve();
|
|
722
|
+
const publishWorkerLease = (phase = leasePhase) => {
|
|
723
|
+
leasePhase = phase;
|
|
724
|
+
leaseWrite = leaseWrite.catch(() => {}).then(async () => {
|
|
725
|
+
const publishedAtMs = Date.now();
|
|
726
|
+
const leasePath = path.join(directory, "worker-lease.json");
|
|
727
|
+
assertWorkerLeaseTarget(leasePath);
|
|
728
|
+
writeJsonAtomicFileSync(leasePath, {
|
|
729
|
+
...evidence,
|
|
730
|
+
phase: leasePhase,
|
|
731
|
+
publishedAt: new Date(publishedAtMs).toISOString(),
|
|
732
|
+
expiresAt: new Date(publishedAtMs + WORKER_LEASE_MS).toISOString()
|
|
733
|
+
});
|
|
734
|
+
});
|
|
735
|
+
return leaseWrite;
|
|
736
|
+
};
|
|
737
|
+
// The initial lease is observational too; failure must not prevent task execution or terminal publication.
|
|
738
|
+
await publishWorkerLease("running").catch(() => {});
|
|
739
|
+
const stopLeaseRenewal = startWorkerLeaseRenewal({ publish: publishWorkerLease });
|
|
740
|
+
|
|
741
|
+
const stdoutTail = new BoundedTail(command.logLimitBytes);
|
|
742
|
+
const stderrTail = new BoundedTail(command.logLimitBytes);
|
|
743
|
+
const startedAt = new Date().toISOString();
|
|
744
|
+
let taskTemp;
|
|
745
|
+
let child;
|
|
746
|
+
let childOutcome;
|
|
747
|
+
let spawnError;
|
|
748
|
+
let outcome;
|
|
749
|
+
let cleanupError;
|
|
750
|
+
try {
|
|
751
|
+
try {
|
|
752
|
+
taskTemp = await createOwnedTempEnvironment("detached-task", {
|
|
753
|
+
baseRoot: command.tempRoot,
|
|
754
|
+
hostEnvironment: createDetachedRunnerEnvironment()
|
|
755
|
+
});
|
|
756
|
+
child = spawn(command.argv[0], command.argv.slice(1), {
|
|
757
|
+
cwd: command.cwd,
|
|
758
|
+
env: taskTemp.environment,
|
|
759
|
+
shell: false,
|
|
760
|
+
detached: false,
|
|
761
|
+
windowsHide: true,
|
|
762
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
763
|
+
});
|
|
764
|
+
child.stdout.on("data", (chunk) => stdoutTail.push(chunk));
|
|
765
|
+
child.stderr.on("data", (chunk) => stderrTail.push(chunk));
|
|
766
|
+
childOutcome = new Promise((resolve) => {
|
|
767
|
+
let settled = false;
|
|
768
|
+
const finish = (value) => {
|
|
769
|
+
if (settled) return;
|
|
770
|
+
settled = true;
|
|
771
|
+
resolve(value);
|
|
772
|
+
};
|
|
773
|
+
child.once("error", (error) => finish({ code: 127, signal: null, error: error.stack ?? error.message }));
|
|
774
|
+
child.once("close", (code, signal) => finish({ code: code ?? 1, signal, error: null }));
|
|
775
|
+
});
|
|
776
|
+
await writeJsonAtomic(directory, "child.json", { schemaVersion: RUNNER_SCHEMA_VERSION, pid: child.pid, workerNonce: command.workerNonce });
|
|
777
|
+
} catch (error) {
|
|
778
|
+
spawnError = error;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
outcome = spawnError
|
|
782
|
+
? { code: 127, signal: null, error: spawnError.stack ?? spawnError.message }
|
|
783
|
+
: await childOutcome;
|
|
784
|
+
// Lease refresh is observational; failure must not suppress terminal result publication.
|
|
785
|
+
await publishWorkerLease("finalizing").catch(() => {});
|
|
786
|
+
} finally {
|
|
787
|
+
if (taskTemp) {
|
|
788
|
+
try {
|
|
789
|
+
await taskTemp.cleanup();
|
|
790
|
+
} catch (error) {
|
|
791
|
+
cleanupError = error;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
if (cleanupError) {
|
|
797
|
+
const detail = String(cleanupError?.code ?? cleanupError?.message ?? "unknown").slice(0, 512);
|
|
798
|
+
outcome = {
|
|
799
|
+
code: outcome?.code === 0 ? 1 : outcome?.code ?? 1,
|
|
800
|
+
signal: outcome?.signal ?? null,
|
|
801
|
+
error: [outcome?.error, `TASK_TEMP_CLEANUP_FAILED: ${detail}`].filter(Boolean).join("\n")
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
await verifyDirectoryWithoutLinks(directory, command.directoryIdentity);
|
|
806
|
+
const stdoutPath = path.join(directory, "stdout.log");
|
|
807
|
+
const stderrPath = path.join(directory, "stderr.log");
|
|
808
|
+
await Promise.all([
|
|
809
|
+
fsp.writeFile(stdoutPath, stdoutTail.bytes()),
|
|
810
|
+
fsp.writeFile(stderrPath, stderrTail.bytes())
|
|
811
|
+
]);
|
|
812
|
+
await publishWorkerLease("finalizing").catch(() => {});
|
|
813
|
+
let retention;
|
|
814
|
+
try {
|
|
815
|
+
retention = await pruneTerminalRuns(path.dirname(directory), {
|
|
816
|
+
keepCount: command.retentionCount,
|
|
817
|
+
maxAgeDays: command.retentionDays,
|
|
818
|
+
preserveRunId: command.runId
|
|
819
|
+
});
|
|
820
|
+
} catch (error) {
|
|
821
|
+
retention = {
|
|
822
|
+
scanned: 0,
|
|
823
|
+
removed: 0,
|
|
824
|
+
retained: 0,
|
|
825
|
+
failed: 1,
|
|
826
|
+
keepCount: command.retentionCount,
|
|
827
|
+
maxAgeDays: command.retentionDays,
|
|
828
|
+
error: String(error?.message ?? error).slice(0, 512)
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
const result = {
|
|
832
|
+
schemaVersion: RUNNER_SCHEMA_VERSION,
|
|
833
|
+
runId: command.runId,
|
|
834
|
+
kind: command.kind,
|
|
835
|
+
startedAt,
|
|
836
|
+
completedAt: new Date().toISOString(),
|
|
837
|
+
exitCode: outcome.code,
|
|
838
|
+
signal: outcome.signal,
|
|
839
|
+
error: outcome.error,
|
|
840
|
+
temporaryState: {
|
|
841
|
+
cleaned: cleanupError === undefined
|
|
842
|
+
},
|
|
843
|
+
retention,
|
|
844
|
+
stdoutPath,
|
|
845
|
+
stderrPath,
|
|
846
|
+
stdout: stdoutTail.summary(),
|
|
847
|
+
stderr: stderrTail.summary()
|
|
848
|
+
};
|
|
849
|
+
try {
|
|
850
|
+
await writeJsonAtomic(directory, "result.json", result);
|
|
851
|
+
} finally {
|
|
852
|
+
stopLeaseRenewal();
|
|
853
|
+
await leaseWrite.catch(() => {});
|
|
854
|
+
}
|
|
855
|
+
process.exitCode = outcome.code;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
async function main() {
|
|
859
|
+
if (action === "__worker") {
|
|
860
|
+
await worker();
|
|
861
|
+
} else if (action === "start") {
|
|
862
|
+
const separator = argv.indexOf("--");
|
|
863
|
+
if (separator === -1 || separator === argv.length - 1) {
|
|
864
|
+
fail("Usage: node scripts/long-task-runner.mjs start --kind <kind> [--root <dir>] [--temp-root <dir>] [--cwd <dir>] [--log-limit-bytes N] [--retention-count N] [--retention-days N] -- <command> [args...]", 2);
|
|
865
|
+
}
|
|
866
|
+
const root = stateRoot();
|
|
867
|
+
await ensureDirectoryWithoutLinks(root);
|
|
868
|
+
const kind = safeSegment(option("--kind", "task"), "task");
|
|
869
|
+
const existing = (await listStates(root)).find((state) => state.kind === kind && state.status === "running");
|
|
870
|
+
if (existing) fail(`A ${kind} runner is already active: ${existing.runId}. Check it before retrying.`);
|
|
871
|
+
|
|
872
|
+
const commandArgv = argv.slice(separator + 1);
|
|
873
|
+
if (commandArgv.some(looksSecret)) {
|
|
874
|
+
fail("Refusing to persist a secret-looking command argument. Pass credentials through an approved runtime mechanism, not the detached runner command line.", 2);
|
|
875
|
+
}
|
|
876
|
+
const runId = `${new Date().toISOString().replace(/[:.]/g, "-")}-${kind}-${randomBytes(4).toString("hex")}`;
|
|
877
|
+
const directory = runDirectory(root, runId);
|
|
878
|
+
const checkedDirectory = await ensureDirectoryWithoutLinks(directory);
|
|
879
|
+
const workerNonce = randomBytes(32).toString("hex");
|
|
880
|
+
const command = {
|
|
881
|
+
schemaVersion: RUNNER_SCHEMA_VERSION,
|
|
882
|
+
runId,
|
|
883
|
+
kind,
|
|
884
|
+
cwd: path.resolve(option("--cwd", process.cwd())),
|
|
885
|
+
argv: commandArgv,
|
|
886
|
+
workerNonce,
|
|
887
|
+
commandDigest: "",
|
|
888
|
+
directoryIdentity: checkedDirectory.identity,
|
|
889
|
+
tempRoot: path.resolve(option("--temp-root", os.tmpdir())),
|
|
890
|
+
logLimitBytes: positiveInteger(option("--log-limit-bytes"), DEFAULT_LOG_LIMIT_BYTES, MAX_LOG_LIMIT_BYTES),
|
|
891
|
+
retentionCount: positiveInteger(option("--retention-count"), DEFAULT_RUN_RETENTION_COUNT, 10_000),
|
|
892
|
+
retentionDays: positiveInteger(option("--retention-days"), DEFAULT_RUN_RETENTION_DAYS, 36_500)
|
|
893
|
+
};
|
|
894
|
+
command.commandDigest = commandDigest(command);
|
|
895
|
+
await writeJsonAtomic(directory, "command.json", command);
|
|
896
|
+
|
|
897
|
+
const scriptPath = path.resolve(process.argv[1]);
|
|
898
|
+
const workerProcess = spawn(process.execPath, [scriptPath, "__worker", "--run-dir", directory], {
|
|
899
|
+
cwd: process.cwd(),
|
|
900
|
+
env: createDetachedRunnerEnvironment(),
|
|
901
|
+
detached: true,
|
|
902
|
+
windowsHide: true,
|
|
903
|
+
stdio: "ignore"
|
|
904
|
+
});
|
|
905
|
+
workerProcess.unref();
|
|
906
|
+
const observedCreationTimePromise = processCreationTime(workerProcess.pid);
|
|
907
|
+
const evidence = await waitForWorkerEvidence(directory, workerNonce);
|
|
908
|
+
const observedCreationTime = await observedCreationTimePromise;
|
|
909
|
+
if (!observedCreationTime || observedCreationTime !== evidence.workerCreationTime) {
|
|
910
|
+
fail("Worker creation-time handshake could not be verified.");
|
|
911
|
+
}
|
|
912
|
+
const metadata = {
|
|
913
|
+
schemaVersion: RUNNER_SCHEMA_VERSION,
|
|
914
|
+
runId,
|
|
915
|
+
kind,
|
|
916
|
+
workerPid: workerProcess.pid,
|
|
917
|
+
workerNonce,
|
|
918
|
+
workerCreationTime: observedCreationTime,
|
|
919
|
+
workerCommandDigest: evidence.workerCommandDigest,
|
|
920
|
+
commandDigest: command.commandDigest,
|
|
921
|
+
startedAt: new Date().toISOString(),
|
|
922
|
+
directory,
|
|
923
|
+
directoryIdentity: checkedDirectory.identity,
|
|
924
|
+
command: command.argv,
|
|
925
|
+
cwd: command.cwd,
|
|
926
|
+
logLimitBytes: command.logLimitBytes,
|
|
927
|
+
retentionCount: command.retentionCount,
|
|
928
|
+
retentionDays: command.retentionDays,
|
|
929
|
+
host: os.hostname()
|
|
930
|
+
};
|
|
931
|
+
if (!workerEvidenceMatches(metadata, evidence)) fail("Worker evidence handshake did not match the exact command identity.");
|
|
932
|
+
await writeJsonAtomic(directory, "metadata.json", metadata);
|
|
933
|
+
const [result, stopped] = await Promise.all([
|
|
934
|
+
readJson(path.join(directory, "result.json")),
|
|
935
|
+
readJson(path.join(directory, "stopped.json"))
|
|
936
|
+
]);
|
|
937
|
+
const state = {
|
|
938
|
+
...metadata,
|
|
939
|
+
status: stopped ? "stopped" : result ? "completed" : "running",
|
|
940
|
+
identity: stopped
|
|
941
|
+
? { owned: false, reason: "stop_recorded" }
|
|
942
|
+
: result
|
|
943
|
+
? { owned: false, reason: "result_recorded" }
|
|
944
|
+
: { owned: true, reason: "exact_startup_identity" },
|
|
945
|
+
result: result ?? null,
|
|
946
|
+
stopped: stopped ?? null
|
|
947
|
+
};
|
|
948
|
+
console.log(JSON.stringify(state, null, 2));
|
|
949
|
+
} else if (action === "status") {
|
|
950
|
+
const root = stateRoot();
|
|
951
|
+
const runId = option("--run");
|
|
952
|
+
if (!runId) fail("status requires --run <run-id>.", 2);
|
|
953
|
+
await verifyDirectoryWithoutLinks(root);
|
|
954
|
+
const state = await runState(runDirectory(root, runId));
|
|
955
|
+
if (!state) fail(`Unknown run: ${runId}.`);
|
|
956
|
+
console.log(JSON.stringify(state, null, 2));
|
|
957
|
+
} else if (action === "list") {
|
|
958
|
+
const root = stateRoot();
|
|
959
|
+
try {
|
|
960
|
+
console.log(JSON.stringify(await listStates(root), null, 2));
|
|
961
|
+
} catch (error) {
|
|
962
|
+
if (error?.code === "ENOENT") console.log("[]");
|
|
963
|
+
else throw error;
|
|
964
|
+
}
|
|
965
|
+
} else if (action === "stop") {
|
|
966
|
+
const root = stateRoot();
|
|
967
|
+
const runId = option("--run");
|
|
968
|
+
if (!runId) fail("stop requires --run <run-id>.", 2);
|
|
969
|
+
await verifyDirectoryWithoutLinks(root);
|
|
970
|
+
const directory = runDirectory(root, runId);
|
|
971
|
+
const state = await runState(directory);
|
|
972
|
+
if (!state) fail(`Unknown run: ${runId}.`);
|
|
973
|
+
if (state.status !== "running") fail(`Run ${runId} is ${state.status}; no process tree will be terminated.`);
|
|
974
|
+
const files = await readRunFiles(directory);
|
|
975
|
+
const identity = await verifyWorkerIdentity(files.metadata, files.evidence);
|
|
976
|
+
if (!identity.owned) fail(`Refusing to stop run ${runId}: ${identity.reason}.`);
|
|
977
|
+
|
|
978
|
+
if (process.platform === "win32") {
|
|
979
|
+
try {
|
|
980
|
+
await stopExactWindowsTree({
|
|
981
|
+
pid: state.workerPid,
|
|
982
|
+
expectedCreationTime: state.workerCreationTime
|
|
983
|
+
});
|
|
984
|
+
} catch (error) {
|
|
985
|
+
fail(`Unable to stop exact run ${runId}: ${error instanceof Error ? error.message : "taskkill failed"}`);
|
|
986
|
+
}
|
|
987
|
+
} else {
|
|
988
|
+
try {
|
|
989
|
+
process.kill(-state.workerPid, "SIGTERM");
|
|
990
|
+
} catch {
|
|
991
|
+
process.kill(state.workerPid, "SIGTERM");
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
await verifyDirectoryWithoutLinks(directory, state.directoryIdentity);
|
|
995
|
+
await writeJsonAtomic(directory, "stopped.json", {
|
|
996
|
+
schemaVersion: RUNNER_SCHEMA_VERSION,
|
|
997
|
+
runId,
|
|
998
|
+
workerPid: state.workerPid,
|
|
999
|
+
workerCreationTime: state.workerCreationTime,
|
|
1000
|
+
workerNonce: state.workerNonce,
|
|
1001
|
+
stoppedAt: new Date().toISOString()
|
|
1002
|
+
});
|
|
1003
|
+
console.log(JSON.stringify({ runId, status: "stop_requested" }, null, 2));
|
|
1004
|
+
} else if (action === "clean") {
|
|
1005
|
+
const root = stateRoot();
|
|
1006
|
+
const tempRoot = path.resolve(option("--temp-root", os.tmpdir()));
|
|
1007
|
+
const staleTemporaryState = await sweepStaleOwnedTempRoots({
|
|
1008
|
+
baseRoot: tempRoot,
|
|
1009
|
+
limit: positiveInteger(option("--sweep-limit"), 1024, 10_000)
|
|
1010
|
+
});
|
|
1011
|
+
const runEvidence = await pruneTerminalRuns(root, {
|
|
1012
|
+
keepCount: positiveInteger(option("--retention-count"), DEFAULT_RUN_RETENTION_COUNT, 10_000),
|
|
1013
|
+
maxAgeDays: positiveInteger(option("--retention-days"), DEFAULT_RUN_RETENTION_DAYS, 36_500)
|
|
1014
|
+
});
|
|
1015
|
+
console.log(JSON.stringify({
|
|
1016
|
+
schemaVersion: 1,
|
|
1017
|
+
tempRoot,
|
|
1018
|
+
runRoot: root,
|
|
1019
|
+
staleTemporaryState,
|
|
1020
|
+
runEvidence
|
|
1021
|
+
}, null, 2));
|
|
1022
|
+
if (
|
|
1023
|
+
staleTemporaryState.limited ||
|
|
1024
|
+
staleTemporaryState.invalid > 0 ||
|
|
1025
|
+
runEvidence.invalid > 0 ||
|
|
1026
|
+
runEvidence.failed > 0 ||
|
|
1027
|
+
runEvidence.claimed.failed > 0
|
|
1028
|
+
) process.exitCode = 1;
|
|
1029
|
+
} else {
|
|
1030
|
+
fail("Usage: node scripts/long-task-runner.mjs <start|status|list|stop|clean> ...", 2);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
const invokedDirectly = process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href;
|
|
1035
|
+
if (invokedDirectly) {
|
|
1036
|
+
await main();
|
|
1037
|
+
}
|