stageflow 0.25.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -3
- package/builtin-skills/browser/SKILL.md +42 -0
- package/dist/a2a/contracts.d.ts +1 -0
- package/dist/a2a/contracts.js +1 -0
- package/dist/a2a/registry.d.ts +2 -0
- package/dist/a2a/registry.js +20 -1
- package/dist/a2a/server.d.ts +7 -0
- package/dist/a2a/server.js +5 -2
- package/dist/a2a/service.d.ts +1 -1
- package/dist/a2a/service.js +57 -8
- package/dist/a2a/store.d.ts +8 -5
- package/dist/a2a/store.js +24 -67
- package/dist/agent/activity.d.ts +3 -0
- package/dist/agent/bootProviderConfig.d.ts +21 -0
- package/dist/agent/bootProviderConfig.js +74 -0
- package/dist/agent/claudeAdapter.js +2 -2
- package/dist/agent/cursorProvider.d.ts +2 -0
- package/dist/agent/cursorProvider.js +30 -5
- package/dist/agent/fakeAgent.d.ts +1 -0
- package/dist/agent/fakeAgent.js +13 -0
- package/dist/agent/piAdapter.d.ts +11 -24
- package/dist/agent/piAdapter.js +107 -50
- package/dist/agent/piIsolatedMcp.d.ts +2 -0
- package/dist/agent/piIsolatedMcp.js +10 -2
- package/dist/agent/piSessionFactory.d.ts +38 -0
- package/dist/agent/piSessionFactory.js +60 -0
- package/dist/agent/port.d.ts +8 -0
- package/dist/agent/streamLogRedact.d.ts +7 -1
- package/dist/agent/streamLogRedact.js +26 -11
- package/dist/browser/anchor.d.ts +27 -0
- package/dist/browser/anchor.js +127 -0
- package/dist/browser/auditSink.d.ts +38 -0
- package/dist/browser/auditSink.js +34 -0
- package/dist/browser/browserHost.d.ts +86 -0
- package/dist/browser/browserHost.js +7 -0
- package/dist/browser/browserSweep.d.ts +14 -0
- package/dist/browser/browserSweep.js +54 -0
- package/dist/browser/browserTeardown.d.ts +56 -0
- package/dist/browser/browserTeardown.js +235 -0
- package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -0
- package/dist/browser/fakeRemoteBrowserHost.js +34 -0
- package/dist/browser/gateHandoff.d.ts +25 -0
- package/dist/browser/gateHandoff.js +83 -0
- package/dist/browser/humanLogin.d.ts +18 -0
- package/dist/browser/humanLogin.js +34 -0
- package/dist/browser/keyProvider.d.ts +10 -0
- package/dist/browser/keyProvider.js +39 -0
- package/dist/browser/localBrowserHost.d.ts +10 -0
- package/dist/browser/localBrowserHost.js +152 -0
- package/dist/browser/localProfileLock.d.ts +2 -0
- package/dist/browser/localProfileLock.js +218 -0
- package/dist/browser/localProfileStore.d.ts +5 -0
- package/dist/browser/localProfileStore.js +91 -0
- package/dist/browser/loginCheck.d.ts +53 -0
- package/dist/browser/loginCheck.js +147 -0
- package/dist/browser/memoryProfileLock.d.ts +2 -0
- package/dist/browser/memoryProfileLock.js +53 -0
- package/dist/browser/memoryProfileStore.d.ts +2 -0
- package/dist/browser/memoryProfileStore.js +31 -0
- package/dist/browser/navigationAudit.d.ts +16 -0
- package/dist/browser/navigationAudit.js +53 -0
- package/dist/browser/persistedEnv.d.ts +3 -0
- package/dist/browser/persistedEnv.js +15 -0
- package/dist/browser/profileLock.d.ts +40 -0
- package/dist/browser/profileLock.js +10 -0
- package/dist/browser/profileStore.d.ts +23 -0
- package/dist/browser/profileStore.js +39 -0
- package/dist/browser/runLiveness.d.ts +13 -0
- package/dist/browser/runLiveness.js +39 -0
- package/dist/browser/sitePolicy.d.ts +10 -0
- package/dist/browser/sitePolicy.js +61 -0
- package/dist/browser/stageBrowserEnv.d.ts +22 -0
- package/dist/browser/stageBrowserEnv.js +147 -0
- package/dist/browser/stageProfileLock.d.ts +17 -0
- package/dist/browser/stageProfileLock.js +39 -0
- package/dist/cli/artifactCommand.js +9 -16
- package/dist/cli/backupCommand.d.ts +9 -0
- package/dist/cli/backupCommand.js +111 -0
- package/dist/cli/browserCommand.d.ts +44 -0
- package/dist/cli/browserCommand.js +472 -0
- package/dist/cli/debugRunCommand.d.ts +10 -0
- package/dist/cli/debugRunCommand.js +109 -0
- package/dist/cli/doctorCommand.d.ts +32 -0
- package/dist/cli/doctorCommand.js +453 -0
- package/dist/cli/envelopeCommand.js +9 -2
- package/dist/cli/exportAllCommand.d.ts +55 -0
- package/dist/cli/exportAllCommand.js +218 -0
- package/dist/cli/exportRunCommand.js +23 -21
- package/dist/cli/hostClient.d.ts +41 -3
- package/dist/cli/hostClient.js +129 -14
- package/dist/cli/initCommand.d.ts +1 -0
- package/dist/cli/initCommand.js +23 -7
- package/dist/cli/initTemplates.d.ts +5 -2
- package/dist/cli/initTemplates.js +32 -2
- package/dist/cli/resolveSafeOutPath.d.ts +2 -0
- package/dist/cli/resolveSafeOutPath.js +40 -0
- package/dist/cli/restoreCommand.d.ts +10 -0
- package/dist/cli/restoreCommand.js +90 -0
- package/dist/cli/runCommand.d.ts +3 -2
- package/dist/cli/runCommand.js +93 -11
- package/dist/cli/runOutput.d.ts +4 -0
- package/dist/cli/runOutput.js +33 -7
- package/dist/cli/runStageCommand.d.ts +1 -0
- package/dist/cli/runStageCommand.js +44 -25
- package/dist/cli/runsCommand.d.ts +1 -1
- package/dist/cli/runsCommand.js +168 -10
- package/dist/cli/triggerCommand.d.ts +12 -0
- package/dist/cli/triggerCommand.js +183 -0
- package/dist/cli/validateCommand.d.ts +1 -1
- package/dist/cli/validateCommand.js +23 -6
- package/dist/cli.js +202 -32
- package/dist/config/browseCatalog.d.ts +1 -0
- package/dist/config/browseCatalog.js +5 -4
- package/dist/config/builtinSkills.d.ts +4 -0
- package/dist/config/builtinSkills.js +23 -0
- package/dist/config/catalogRelativePath.d.ts +63 -0
- package/dist/config/catalogRelativePath.js +126 -0
- package/dist/config/configOrigin.d.ts +27 -0
- package/dist/config/configOrigin.js +59 -0
- package/dist/config/createTrigger.d.ts +27 -0
- package/dist/config/createTrigger.js +232 -0
- package/dist/config/draftPackage.d.ts +117 -0
- package/dist/config/draftPackage.js +550 -0
- package/dist/config/ensureCatalogScanRoot.d.ts +16 -0
- package/dist/config/ensureCatalogScanRoot.js +73 -0
- package/dist/config/hostConfig.d.ts +57 -0
- package/dist/config/hostConfig.js +360 -0
- package/dist/config/listSkills.d.ts +26 -0
- package/dist/config/listSkills.js +206 -5
- package/dist/config/loadOutcome.d.ts +7 -1
- package/dist/config/loadPipeline.js +39 -2
- package/dist/config/loadStage.js +25 -0
- package/dist/config/loadStageflowManifest.js +25 -0
- package/dist/config/loadTask.d.ts +2 -0
- package/dist/config/loadTask.js +57 -18
- package/dist/config/loadTrigger.d.ts +9 -0
- package/dist/config/loadTrigger.js +120 -0
- package/dist/config/materializeStageModels.js +5 -1
- package/dist/config/mergePipelineIncludes.d.ts +3 -0
- package/dist/config/mergePipelineIncludes.js +19 -0
- package/dist/config/migrateYaml.js +6 -11
- package/dist/config/multiProjectCatalog.d.ts +32 -0
- package/dist/config/multiProjectCatalog.js +150 -0
- package/dist/config/normalizePipelineStageEntry.js +27 -1
- package/dist/config/parseCompletionContract.js +6 -0
- package/dist/config/pipelineStageKeys.js +3 -0
- package/dist/config/publishDraftPackage.d.ts +7 -0
- package/dist/config/publishDraftPackage.js +25 -0
- package/dist/config/resolveCatalogContext.js +1 -1
- package/dist/config/resolveCatalogRoots.d.ts +27 -0
- package/dist/config/resolveCatalogRoots.js +76 -0
- package/dist/config/resolveModel.d.ts +7 -1
- package/dist/config/resolveModel.js +11 -1
- package/dist/config/resolvePipelineDag.js +1 -1
- package/dist/config/resolveStageMcpServers.d.ts +9 -1
- package/dist/config/resolveStageMcpServers.js +36 -9
- package/dist/config/scanCatalogPaths.d.ts +1 -1
- package/dist/config/scanCatalogPaths.js +6 -2
- package/dist/config/secretFromEnvOrFile.d.ts +9 -0
- package/dist/config/secretFromEnvOrFile.js +34 -0
- package/dist/config/seededCatalog.d.ts +11 -0
- package/dist/config/seededCatalog.js +29 -0
- package/dist/config/stageBrowser.d.ts +12 -0
- package/dist/config/stageBrowser.js +118 -0
- package/dist/config/toolRequires.d.ts +24 -0
- package/dist/config/toolRequires.js +115 -0
- package/dist/config/triggerCatalog.d.ts +10 -0
- package/dist/config/triggerCatalog.js +37 -0
- package/dist/config/validateCatalog.d.ts +17 -3
- package/dist/config/validateCatalog.js +223 -3
- package/dist/config/validateCompletionContract.js +8 -0
- package/dist/diagnostics/checks.d.ts +39 -0
- package/dist/diagnostics/checks.js +135 -0
- package/dist/envelope/preEmitChecks.d.ts +1 -1
- package/dist/envelope/preEmitChecks.js +8 -0
- package/dist/errors/codes.d.ts +14 -0
- package/dist/errors/codes.js +59 -0
- package/dist/git/cache.d.ts +14 -0
- package/dist/git/cache.js +150 -0
- package/dist/git/credentials.d.ts +9 -0
- package/dist/git/credentials.js +88 -0
- package/dist/git/errors.d.ts +20 -0
- package/dist/git/errors.js +94 -0
- package/dist/git/exec.d.ts +16 -0
- package/dist/git/exec.js +135 -0
- package/dist/git/operations.d.ts +29 -0
- package/dist/git/operations.js +153 -0
- package/dist/git/version.d.ts +1 -0
- package/dist/git/version.js +10 -0
- package/dist/logging/audit.d.ts +16 -0
- package/dist/logging/audit.js +19 -0
- package/dist/logging/commonWords.d.ts +2 -0
- package/dist/logging/commonWords.js +13 -0
- package/dist/logging/logger.d.ts +49 -0
- package/dist/logging/logger.js +193 -0
- package/dist/logging/namedSecrets.d.ts +6 -0
- package/dist/logging/namedSecrets.js +58 -0
- package/dist/logging/redact.d.ts +17 -0
- package/dist/logging/redact.js +105 -0
- package/dist/mcp/catalogTools.js +492 -81
- package/dist/mcp/checkoutTools.d.ts +52 -0
- package/dist/mcp/checkoutTools.js +332 -0
- package/dist/mcp/controlTools.js +182 -6
- package/dist/mcp/deps.d.ts +3 -0
- package/dist/mcp/getStartedTool.d.ts +3 -0
- package/dist/mcp/getStartedTool.js +62 -0
- package/dist/mcp/readArtifact.d.ts +11 -0
- package/dist/mcp/readArtifact.js +46 -30
- package/dist/mcp/server.js +2 -0
- package/dist/mcp/tools.js +6 -0
- package/dist/mcp/triggerTools.d.ts +3 -0
- package/dist/mcp/triggerTools.js +176 -0
- package/dist/net/proxy.d.ts +14 -0
- package/dist/net/proxy.js +78 -0
- package/dist/operatorAgent/docsRetrieval.d.ts +40 -0
- package/dist/operatorAgent/docsRetrieval.js +269 -0
- package/dist/operatorAgent/draftContext.d.ts +16 -0
- package/dist/operatorAgent/draftContext.js +68 -0
- package/dist/operatorAgent/fakeHost.d.ts +39 -0
- package/dist/operatorAgent/fakeHost.js +92 -0
- package/dist/operatorAgent/host.d.ts +40 -0
- package/dist/operatorAgent/host.js +179 -0
- package/dist/operatorAgent/index.d.ts +9 -0
- package/dist/operatorAgent/index.js +8 -0
- package/dist/operatorAgent/piModel.d.ts +80 -0
- package/dist/operatorAgent/piModel.js +528 -0
- package/dist/operatorAgent/profiles/workshopAuthor.d.ts +23 -0
- package/dist/operatorAgent/profiles/workshopAuthor.js +338 -0
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +6 -0
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +103 -0
- package/dist/operatorAgent/profiles/workshopAuthorTools.d.ts +57 -0
- package/dist/operatorAgent/profiles/workshopAuthorTools.js +623 -0
- package/dist/operatorAgent/proposals.d.ts +32 -0
- package/dist/operatorAgent/proposals.js +144 -0
- package/dist/operatorAgent/types.d.ts +128 -0
- package/dist/operatorAgent/types.js +1 -0
- package/dist/package-meta.d.ts +2 -1
- package/dist/package-meta.js +2 -1
- package/dist/preflight/bash.d.ts +8 -0
- package/dist/preflight/bash.js +34 -0
- package/dist/preflight/claudeRoot.d.ts +11 -0
- package/dist/preflight/claudeRoot.js +23 -0
- package/dist/preflight/mcpCommands.d.ts +8 -0
- package/dist/preflight/mcpCommands.js +36 -0
- package/dist/preflight/pipelinePreflight.d.ts +40 -0
- package/dist/preflight/pipelinePreflight.js +192 -0
- package/dist/preflight/tls.d.ts +6 -0
- package/dist/preflight/tls.js +18 -0
- package/dist/preflight/toolchain.d.ts +45 -0
- package/dist/preflight/toolchain.js +219 -0
- package/dist/project/findManifestRoot.d.ts +3 -0
- package/dist/project/findManifestRoot.js +35 -0
- package/dist/project/findProjectRoot.js +3 -4
- package/dist/project/globalHome.d.ts +7 -0
- package/dist/project/globalHome.js +63 -2
- package/dist/project/resolveProjectContext.js +2 -2
- package/dist/project/resolveStageflowContext.d.ts +7 -0
- package/dist/project/resolveStageflowContext.js +46 -30
- package/dist/projection/projectRun.d.ts +2 -1
- package/dist/projection/projectRun.js +1 -0
- package/dist/runstore/assertTmpdir.d.ts +1 -0
- package/dist/runstore/assertTmpdir.js +25 -0
- package/dist/runstore/backup.d.ts +52 -0
- package/dist/runstore/backup.js +292 -0
- package/dist/runstore/createStore.d.ts +18 -1
- package/dist/runstore/createStore.js +64 -5
- package/dist/runstore/debugBundle.d.ts +60 -0
- package/dist/runstore/debugBundle.js +143 -0
- package/dist/runstore/diskUsage.d.ts +50 -0
- package/dist/runstore/diskUsage.js +214 -0
- package/dist/runstore/exportRunPayload.d.ts +8 -0
- package/dist/runstore/exportRunPayload.js +23 -0
- package/dist/runstore/normalizeCatalogPath.d.ts +1 -0
- package/dist/runstore/normalizeCatalogPath.js +10 -0
- package/dist/runstore/paths.d.ts +5 -0
- package/dist/runstore/paths.js +145 -1
- package/dist/runstore/port.d.ts +139 -13
- package/dist/runstore/port.js +8 -1
- package/dist/runstore/restore.d.ts +81 -0
- package/dist/runstore/restore.js +355 -0
- package/dist/runstore/retention.d.ts +27 -0
- package/dist/runstore/retention.js +74 -0
- package/dist/runstore/runManifest.d.ts +146 -0
- package/dist/runstore/runManifest.js +288 -0
- package/dist/runstore/runProjection.d.ts +3 -1
- package/dist/runstore/runProjection.js +63 -1
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +35 -3
- package/dist/runstore/sqlite/SqliteRunStore.js +442 -219
- package/dist/runstore/sqlite/applyStorePragmas.d.ts +39 -0
- package/dist/runstore/sqlite/applyStorePragmas.js +145 -0
- package/dist/runstore/sqlite/migrations/001-baseline.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/001-baseline.js +10 -0
- package/dist/runstore/sqlite/migrations/002-repository-binding.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/002-repository-binding.js +29 -0
- package/dist/runstore/sqlite/migrations/003-run-lifecycle.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/003-run-lifecycle.js +28 -0
- package/dist/runstore/sqlite/migrations/004-auto-resume-count.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/004-auto-resume-count.js +19 -0
- package/dist/runstore/sqlite/migrations/005-config-origins.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/005-config-origins.js +19 -0
- package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/006-pipeline-body-and-caller.js +28 -0
- package/dist/runstore/sqlite/migrations/007-projects-registry.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/007-projects-registry.js +31 -0
- package/dist/runstore/sqlite/migrations/008-triggers-table.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/008-triggers-table.js +19 -0
- package/dist/runstore/sqlite/migrations/009-trigger-next-run.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/009-trigger-next-run.js +19 -0
- package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/010-trigger-adapter-state.js +17 -0
- package/dist/runstore/sqlite/migrations/index.d.ts +21 -0
- package/dist/runstore/sqlite/migrations/index.js +78 -0
- package/dist/runstore/sqlite/schema.d.ts +20 -1
- package/dist/runstore/sqlite/schema.js +277 -1
- package/dist/runstore/sqlite/storeOpenError.d.ts +6 -0
- package/dist/runstore/sqlite/storeOpenError.js +11 -0
- package/dist/runstore/sqlite/storeSchemaError.d.ts +5 -0
- package/dist/runstore/sqlite/storeSchemaError.js +8 -0
- package/dist/runstore/stageExecution.js +3 -0
- package/dist/runstore/storeFilesystem.d.ts +32 -0
- package/dist/runstore/storeFilesystem.js +148 -0
- package/dist/runstore/trackProjection.js +2 -0
- package/dist/runstore/workspaceLayout.d.ts +13 -0
- package/dist/runstore/workspaceLayout.js +76 -0
- package/dist/runtime/answerResume.d.ts +1 -0
- package/dist/runtime/answerResume.js +3 -1
- package/dist/runtime/completionCheckRunner.d.ts +15 -1
- package/dist/runtime/completionCheckRunner.js +67 -7
- package/dist/runtime/containerLimits.d.ts +16 -0
- package/dist/runtime/containerLimits.js +84 -0
- package/dist/runtime/credentialBinding.d.ts +2 -0
- package/dist/runtime/credentialBinding.js +3 -0
- package/dist/runtime/credentialMaterialisation.d.ts +28 -0
- package/dist/runtime/credentialMaterialisation.js +128 -0
- package/dist/runtime/emailSource.d.ts +72 -0
- package/dist/runtime/emailSource.js +304 -0
- package/dist/runtime/gitCheckoutCapability.js +7 -13
- package/dist/runtime/githubPollSource.d.ts +86 -0
- package/dist/runtime/githubPollSource.js +284 -0
- package/dist/runtime/pipelineRunner.d.ts +40 -1
- package/dist/runtime/pipelineRunner.js +181 -17
- package/dist/runtime/pipelineScheduler.d.ts +17 -0
- package/dist/runtime/pipelineScheduler.js +220 -28
- package/dist/runtime/reloadRunCatalog.js +6 -1
- package/dist/runtime/repositoryMaterialize.d.ts +47 -0
- package/dist/runtime/repositoryMaterialize.js +235 -0
- package/dist/runtime/resumeTimedOut.d.ts +1 -1
- package/dist/runtime/resumeTimedOut.js +5 -2
- package/dist/runtime/runChangeBus.js +9 -0
- package/dist/runtime/runDeletion.d.ts +7 -0
- package/dist/runtime/runDeletion.js +37 -0
- package/dist/runtime/runManager.d.ts +169 -4
- package/dist/runtime/runManager.js +1748 -83
- package/dist/runtime/runRetentionSweep.d.ts +31 -0
- package/dist/runtime/runRetentionSweep.js +328 -0
- package/dist/runtime/runRetryCoordinator.d.ts +4 -0
- package/dist/runtime/runRetryCoordinator.js +20 -2
- package/dist/runtime/runSkills.d.ts +40 -0
- package/dist/runtime/runSkills.js +173 -0
- package/dist/runtime/scheduleSource.d.ts +38 -0
- package/dist/runtime/scheduleSource.js +119 -0
- package/dist/runtime/settingsFile.d.ts +5 -0
- package/dist/runtime/settingsFile.js +61 -54
- package/dist/runtime/stageAttemptBootstrap.d.ts +5 -1
- package/dist/runtime/stageAttemptBootstrap.js +250 -27
- package/dist/runtime/stageCacheEnv.d.ts +6 -0
- package/dist/runtime/stageCacheEnv.js +34 -0
- package/dist/runtime/stageConcurrency.d.ts +2 -1
- package/dist/runtime/stageConcurrency.js +4 -2
- package/dist/runtime/stageEnvironment.d.ts +38 -0
- package/dist/runtime/stageEnvironment.js +194 -0
- package/dist/runtime/stageHitl.d.ts +1 -0
- package/dist/runtime/stageHitl.js +5 -1
- package/dist/runtime/stageProcessLauncher.d.ts +20 -0
- package/dist/runtime/stageProcessLauncher.js +236 -41
- package/dist/runtime/stageRecovery.d.ts +3 -1
- package/dist/runtime/stageRecovery.js +19 -4
- package/dist/runtime/stageRoots.d.ts +38 -1
- package/dist/runtime/stageRoots.js +118 -0
- package/dist/runtime/stageRunner.d.ts +6 -0
- package/dist/runtime/stageRunner.js +32 -6
- package/dist/runtime/stageSecretDecl.d.ts +9 -0
- package/dist/runtime/stageSecretDecl.js +102 -0
- package/dist/runtime/stageSecrets.d.ts +40 -0
- package/dist/runtime/stageSecrets.js +181 -0
- package/dist/runtime/stageStreamLog.d.ts +1 -0
- package/dist/runtime/stageStreamLog.js +2 -1
- package/dist/runtime/stageWorker.d.ts +3 -2
- package/dist/runtime/stageWorker.js +58 -10
- package/dist/runtime/stageWorkerProtocol.d.ts +5 -0
- package/dist/runtime/stageWorkerProtocol.js +8 -0
- package/dist/runtime/startPayload.d.ts +32 -0
- package/dist/runtime/startPayload.js +59 -0
- package/dist/runtime/taskInput.js +14 -5
- package/dist/runtime/triggerCatalog.d.ts +6 -0
- package/dist/runtime/triggerCatalog.js +24 -0
- package/dist/runtime/triggerEventMatch.d.ts +2 -0
- package/dist/runtime/triggerEventMatch.js +6 -0
- package/dist/runtime/triggerPort.d.ts +14 -0
- package/dist/runtime/triggerPort.js +1 -0
- package/dist/runtime/triggerRunner.d.ts +17 -0
- package/dist/runtime/triggerRunner.js +89 -0
- package/dist/runtime/verifiedStageExecution.d.ts +8 -0
- package/dist/runtime/verifiedStageExecution.js +16 -1
- package/dist/runtime/webhookSignature.d.ts +1 -0
- package/dist/runtime/webhookSignature.js +12 -0
- package/dist/runtime/workspaceBinding.d.ts +20 -0
- package/dist/runtime/workspaceBinding.js +60 -0
- package/dist/server/allowedHosts.d.ts +19 -0
- package/dist/server/allowedHosts.js +179 -0
- package/dist/server/bootstrap.d.ts +42 -2
- package/dist/server/bootstrap.js +273 -5
- package/dist/server/controlToken.d.ts +47 -0
- package/dist/server/controlToken.js +254 -0
- package/dist/server/createHttpHost.d.ts +11 -2
- package/dist/server/createHttpHost.js +106 -10
- package/dist/server/ensureGlobalService.d.ts +5 -1
- package/dist/server/ensureGlobalService.js +35 -1
- package/dist/server/healthSurfaces.d.ts +6 -0
- package/dist/server/healthSurfaces.js +123 -0
- package/dist/server/http.d.ts +27 -1
- package/dist/server/http.js +2254 -477
- package/dist/server/listenHost.d.ts +5 -0
- package/dist/server/listenHost.js +56 -0
- package/dist/server/mcpHost.d.ts +10 -1
- package/dist/server/mcpHost.js +36 -6
- package/dist/server/operatorResults.d.ts +2 -2
- package/dist/server/operatorResults.js +40 -29
- package/dist/server/requestAuthContext.d.ts +12 -0
- package/dist/server/requestAuthContext.js +21 -0
- package/dist/server/shutdown.d.ts +69 -0
- package/dist/server/shutdown.js +219 -0
- package/dist/server/triggerRoutes.d.ts +15 -0
- package/dist/server/triggerRoutes.js +268 -0
- package/dist/tools/askOperator.d.ts +3 -2
- package/dist/types/completion.d.ts +3 -0
- package/dist/types/pipeline.d.ts +10 -1
- package/dist/types/preEmitCheck.d.ts +7 -0
- package/dist/types/stage.d.ts +22 -0
- package/dist/types/stageflowManifest.d.ts +3 -0
- package/dist/types/task.d.ts +8 -0
- package/dist/types/trigger.d.ts +18 -0
- package/dist/types/trigger.js +1 -0
- package/dist/ui/assets/{index-CSsDued1.css → index-Bv4p1aPK.css} +1 -1
- package/dist/ui/assets/index-CntB4bZR.js +133 -0
- package/dist/ui/index.html +2 -2
- package/dist/workshop/autosave.d.ts +58 -0
- package/dist/workshop/autosave.js +222 -0
- package/dist/workshop/buildStore.d.ts +79 -0
- package/dist/workshop/buildStore.js +326 -0
- package/dist/workshop/chatTurn.d.ts +232 -0
- package/dist/workshop/chatTurn.js +475 -0
- package/dist/workshop/modelSettings.d.ts +18 -0
- package/dist/workshop/modelSettings.js +44 -0
- package/dist/workshop/sessionStore.d.ts +61 -0
- package/dist/workshop/sessionStore.js +254 -0
- package/dist/workshop/workshopPin.d.ts +72 -0
- package/dist/workshop/workshopPin.js +193 -0
- package/examples/README.md +99 -0
- package/examples/archify-on-pr/README.md +213 -0
- package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +19 -0
- package/examples/archify-on-pr/archify-on-pr.task.yaml +19 -0
- package/examples/archify-on-pr/author-diagrams.yaml +106 -0
- package/examples/archify-on-pr/detect-changes.yaml +89 -0
- package/examples/browser-session/README.md +77 -0
- package/examples/browser-session/browser-session.pipeline.yaml +50 -0
- package/examples/browser-session/browser-session.task.yaml +6 -0
- package/examples/browser-session/check-login.yaml +22 -0
- package/examples/browser-session/fixture-server.mjs +64 -0
- package/examples/browser-session/human-login.yaml +31 -0
- package/examples/browser-session/merge.yaml +36 -0
- package/examples/browser-session/work-a.yaml +41 -0
- package/examples/browser-session/work-b.yaml +41 -0
- package/examples/ci-validate/README.md +35 -0
- package/examples/ci-validate/check.yaml +13 -0
- package/examples/ci-validate/ci-demo.pipeline.yaml +4 -0
- package/examples/ci-validate/validate.sh +5 -0
- package/examples/compliance-review-demo/README.md +125 -0
- package/examples/compliance-review-demo/compliance-review-demo.pipeline.yaml +29 -0
- package/examples/compliance-review-demo/connect-account.yaml +47 -0
- package/examples/compliance-review-demo/map-to-control.yaml +123 -0
- package/examples/compliance-review-demo/poll-bucket-config.yaml +103 -0
- package/examples/compliance-review-demo/publish-report.yaml +79 -0
- package/examples/compliance-review-demo/review-verdict.yaml +122 -0
- package/examples/compliance-review-demo/run-compliance-check.yaml +96 -0
- package/examples/compliance-review-demo/store-evidence.yaml +143 -0
- package/examples/conditional-fork/README.md +89 -0
- package/examples/conditional-fork/branch-a.yaml +13 -0
- package/examples/conditional-fork/branch-b.yaml +13 -0
- package/examples/conditional-fork/decide.yaml +35 -0
- package/examples/conditional-fork/fork-demo.pipeline.yaml +20 -0
- package/examples/conditional-fork/fork-demo.task.yaml +8 -0
- package/examples/context7-mcp/README.md +42 -0
- package/examples/context7-mcp/context7-mcp.pipeline.yaml +15 -0
- package/examples/context7-mcp/context7-mcp.task.yaml +4 -0
- package/examples/context7-mcp/fetch-docs.yaml +52 -0
- package/examples/context7-mcp/resolve-library.yaml +31 -0
- package/examples/context7-mcp/write-brief.yaml +52 -0
- package/examples/feature-loop/README.md +104 -0
- package/examples/feature-loop/address-feedback.yaml +148 -0
- package/examples/feature-loop/align.yaml +116 -0
- package/examples/feature-loop/decompose.yaml +86 -0
- package/examples/feature-loop/default-model-hierarchy.task.yaml +58 -0
- package/examples/feature-loop/feature-loop.pipeline.yaml +140 -0
- package/examples/feature-loop/feature-loop.task.yaml +25 -0
- package/examples/feature-loop/implement.yaml +128 -0
- package/examples/feature-loop/plan.yaml +111 -0
- package/examples/feature-loop/publish.yaml +167 -0
- package/examples/feature-loop/review.yaml +146 -0
- package/examples/feature-loop/verify.yaml +106 -0
- package/examples/feedback-loop/README.md +72 -0
- package/examples/feedback-loop/VERIFY.md +208 -0
- package/examples/feedback-loop/continue-only.task.yaml +4 -0
- package/examples/feedback-loop/exhaust-require-continue.task.yaml +6 -0
- package/examples/feedback-loop/exhaust-wait-human.task.yaml +11 -0
- package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +23 -0
- package/examples/feedback-loop/feedback-loop.pipeline.yaml +23 -0
- package/examples/feedback-loop/feedback-loop.task.yaml +4 -0
- package/examples/feedback-loop/implement.yaml +14 -0
- package/examples/feedback-loop/plan.yaml +14 -0
- package/examples/feedback-loop/review.yaml +15 -0
- package/examples/feedback-loop/send-back-once.task.yaml +6 -0
- package/examples/feedback-loop/submit.yaml +12 -0
- package/examples/generic-fan-in/README.md +151 -0
- package/examples/generic-fan-in/clarify.yaml +18 -0
- package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +19 -0
- package/examples/generic-fan-in/generic-fan-in-accepted.task.yaml +8 -0
- package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +18 -0
- package/examples/generic-fan-in/generic-fan-in.task.yaml +9 -0
- package/examples/generic-fan-in/research.yaml +45 -0
- package/examples/generic-fan-in/synthesize.yaml +66 -0
- package/examples/generic-fan-in/validation.yaml +45 -0
- package/examples/github-release/README.md +78 -0
- package/examples/github-release/draft-release-notes.yaml +64 -0
- package/examples/github-release/github-release.pipeline.yaml +11 -0
- package/examples/github-release/github-release.task.yaml +13 -0
- package/examples/github-release/publish-github-release.yaml +64 -0
- package/examples/hello-world/README.md +28 -0
- package/examples/hello-world/hello.pipeline.yaml +4 -0
- package/examples/hello-world/my-task.task.yaml +6 -0
- package/examples/hello-world/research.yaml +18 -0
- package/examples/mcp-hitl-tour/README.md +113 -0
- package/examples/mcp-hitl-tour/draft-angle.yaml +33 -0
- package/examples/mcp-hitl-tour/draft-short.yaml +32 -0
- package/examples/mcp-hitl-tour/emit-angles.yaml +45 -0
- package/examples/mcp-hitl-tour/gather.yaml +31 -0
- package/examples/mcp-hitl-tour/intake.yaml +62 -0
- package/examples/mcp-hitl-tour/mcp-hitl-tour-exhaust.task.yaml +13 -0
- package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +54 -0
- package/examples/mcp-hitl-tour/mcp-hitl-tour.task.yaml +16 -0
- package/examples/mcp-hitl-tour/polish.yaml +33 -0
- package/examples/mcp-hitl-tour/review.yaml +17 -0
- package/examples/mcp-hitl-tour/ship.yaml +25 -0
- package/examples/oss-issue-contribution/README.md +218 -0
- package/examples/oss-issue-contribution/oss-address-review-feedback.yaml +117 -0
- package/examples/oss-issue-contribution/oss-approve-contribution.yaml +181 -0
- package/examples/oss-issue-contribution/oss-approve-plan.yaml +111 -0
- package/examples/oss-issue-contribution/oss-explain-fix.yaml +108 -0
- package/examples/oss-issue-contribution/oss-explain-issue.yaml +114 -0
- package/examples/oss-issue-contribution/oss-implement-source-fix.yaml +171 -0
- package/examples/oss-issue-contribution/oss-investigate-area.yaml +116 -0
- package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +125 -0
- package/examples/oss-issue-contribution/oss-issue-intake.yaml +106 -0
- package/examples/oss-issue-contribution/oss-plan-investigation.yaml +112 -0
- package/examples/oss-issue-contribution/oss-plan-review.yaml +109 -0
- package/examples/oss-issue-contribution/oss-reproduce-issue.yaml +140 -0
- package/examples/oss-issue-contribution/oss-review-change.yaml +107 -0
- package/examples/oss-issue-contribution/oss-verify-fix.yaml +130 -0
- package/examples/oss-issue-contribution/oss-write-regression-test.yaml +135 -0
- package/examples/oss-issue-contribution/pi-7629.task.yaml +79 -0
- package/examples/oss-issue-contribution/pi-8919.task.yaml +75 -0
- package/examples/oss-issue-contribution/pydantic-ai-7971.task.yaml +53 -0
- package/examples/oss-quick-fix/README.md +65 -0
- package/examples/oss-quick-fix/implement-fix.yaml +84 -0
- package/examples/oss-quick-fix/oss-quick-fix.pipeline.yaml +35 -0
- package/examples/oss-quick-fix/oss-quick-fix.task.yaml +19 -0
- package/examples/oss-quick-fix/ship-fix.yaml +81 -0
- package/examples/oss-quick-fix/understand-issue.yaml +67 -0
- package/examples/oss-quick-fix/verify-fix.yaml +73 -0
- package/examples/plan-review/README.md +37 -0
- package/examples/plan-review/plan-review-followup.yaml +13 -0
- package/examples/plan-review/plan-review.pipeline.yaml +9 -0
- package/examples/plan-review/plan-review.task.yaml +9 -0
- package/examples/plan-review/plan-review.yaml +25 -0
- package/examples/playwright-mcp/README.md +39 -0
- package/examples/playwright-mcp/playwright-mcp.pipeline.yaml +5 -0
- package/examples/playwright-mcp/playwright-mcp.task.yaml +4 -0
- package/examples/playwright-mcp/screenshot.yaml +40 -0
- package/examples/retry-tour/README.md +75 -0
- package/examples/retry-tour/retry-tour.pipeline.yaml +122 -0
- package/examples/retry-tour/retry-tour.task.yaml +9 -0
- package/examples/route-if-tour/README.md +202 -0
- package/examples/route-if-tour/route-if-tour.pipeline.yaml +950 -0
- package/examples/route-if-tour/route-if-tour.task.yaml +4 -0
- package/examples/route-wiring-smoke-test/01-core-routing.pipeline.yaml +168 -0
- package/examples/route-wiring-smoke-test/02-on-gating.pipeline.yaml +44 -0
- package/examples/route-wiring-smoke-test/03-two-entry-points.pipeline.yaml +28 -0
- package/examples/route-wiring-smoke-test/04-entry-false-equivalent-to-omitted.pipeline.yaml +27 -0
- package/examples/route-wiring-smoke-test/05-fork-choice.pipeline.yaml +110 -0
- package/examples/route-wiring-smoke-test/06-fan-out-fan-in.pipeline.yaml +82 -0
- package/examples/route-wiring-smoke-test/07-loop-human-decision.pipeline.yaml +82 -0
- package/examples/route-wiring-smoke-test/08-skip-fallback-and-multi-on-gate.pipeline.yaml +77 -0
- package/examples/route-wiring-smoke-test/09-multi-loop-targets.pipeline.yaml +102 -0
- package/examples/route-wiring-smoke-test/10-uses-dialect-fork.pipeline.yaml +23 -0
- package/examples/route-wiring-smoke-test/11-sequential-io-handoff.pipeline.yaml +37 -0
- package/examples/route-wiring-smoke-test/12-complex-io-schemas.pipeline.yaml +138 -0
- package/examples/route-wiring-smoke-test/13-ref-io-handoff.pipeline.yaml +84 -0
- package/examples/route-wiring-smoke-test/14-if-eq-gating.pipeline.yaml +50 -0
- package/examples/route-wiring-smoke-test/15-if-all-gated.pipeline.yaml +37 -0
- package/examples/route-wiring-smoke-test/16-if-composition.pipeline.yaml +65 -0
- package/examples/route-wiring-smoke-test/17-if-join.pipeline.yaml +80 -0
- package/examples/route-wiring-smoke-test/README.md +377 -0
- package/examples/route-wiring-smoke-test/rejected/01-reject-legacy-needs.pipeline.yaml +26 -0
- package/examples/route-wiring-smoke-test/rejected/02-reject-legacy-fork.pipeline.yaml +25 -0
- package/examples/route-wiring-smoke-test/rejected/03-reject-legacy-feedback-loop.pipeline.yaml +30 -0
- package/examples/route-wiring-smoke-test/rejected/04-reject-missing-entry.pipeline.yaml +24 -0
- package/examples/route-wiring-smoke-test/rejected/05-reject-unreachable.pipeline.yaml +35 -0
- package/examples/route-wiring-smoke-test/rejected/06-reject-allow-none-without-route-select.pipeline.yaml +37 -0
- package/examples/route-wiring-smoke-test/rejected/07-reject-cycle.pipeline.yaml +27 -0
- package/examples/route-wiring-smoke-test/rejected/08-reject-loop-non-ancestor.pipeline.yaml +42 -0
- package/examples/route-wiring-smoke-test/rejected/09-reject-route-select-leaf.pipeline.yaml +26 -0
- package/examples/route-wiring-smoke-test/rejected/12-reject-loop-replay-unsafe.pipeline.yaml +55 -0
- package/examples/route-wiring-smoke-test/rejected/13-reject-multiple-loop-entries.pipeline.yaml +51 -0
- package/examples/route-wiring-smoke-test/rejected/14-reject-duplicate-stage-id.pipeline.yaml +35 -0
- package/examples/route-wiring-smoke-test/rejected/15-reject-unknown-route-target.pipeline.yaml +15 -0
- package/examples/route-wiring-smoke-test/rejected/16-reject-malformed-route-item.pipeline.yaml +15 -0
- package/examples/route-wiring-smoke-test/rejected/17-reject-io-incompatible.pipeline.yaml +33 -0
- package/examples/route-wiring-smoke-test/rejected/18-reject-nested-io.pipeline.yaml +51 -0
- package/examples/route-wiring-smoke-test/rejected/19-reject-array-item-io.pipeline.yaml +55 -0
- package/examples/route-wiring-smoke-test/rejected/20-reject-closed-io.pipeline.yaml +36 -0
- package/examples/route-wiring-smoke-test/rejected/21-reject-ref-io.pipeline.yaml +42 -0
- package/examples/route-wiring-smoke-test/rejected/22-reject-if-unknown-field.pipeline.yaml +33 -0
- package/examples/route-wiring-smoke-test/rejected/23-reject-if-optional-field.pipeline.yaml +32 -0
- package/examples/route-wiring-smoke-test/rejected/24-reject-if-empty-all.pipeline.yaml +31 -0
- package/examples/route-wiring-smoke-test/rejected/25-reject-if-gt-on-string.pipeline.yaml +33 -0
- package/examples/route-wiring-smoke-test/rejected/26-reject-if-optional-nested.pipeline.yaml +36 -0
- package/examples/route-wiring-smoke-test/rejected/27-reject-if-on-loop.pipeline.yaml +63 -0
- package/examples/route-wiring-smoke-test/rejected/29-reject-if-on-failed.pipeline.yaml +46 -0
- package/examples/route-wiring-smoke-test/smoke-test.task.yaml +4 -0
- package/examples/route-wiring-smoke-test/stages/branch-a.yaml +12 -0
- package/examples/route-wiring-smoke-test/stages/branch-b.yaml +12 -0
- package/examples/route-wiring-smoke-test/stages/decide.yaml +12 -0
- package/examples/route-wiring-smoke-test/stages/finish.yaml +12 -0
- package/examples/ship-feature/README.md +190 -0
- package/examples/ship-feature/feat-address-feedback.yaml +119 -0
- package/examples/ship-feature/feat-implement.yaml +180 -0
- package/examples/ship-feature/feat-plan.yaml +120 -0
- package/examples/ship-feature/feat-review.yaml +109 -0
- package/examples/ship-feature/feat-ship.yaml +203 -0
- package/examples/ship-feature/ship-feature.pipeline.yaml +44 -0
- package/examples/ship-feature/ship-feature.task.yaml +78 -0
- package/examples/stage-mcp/README.md +145 -0
- package/examples/stage-mcp/echo-mcp.mjs +20 -0
- package/examples/stage-mcp/stage-mcp-dead.pipeline.yaml +5 -0
- package/examples/stage-mcp/stage-mcp-dead.task.yaml +3 -0
- package/examples/stage-mcp/stage-mcp-missing-var.pipeline.yaml +5 -0
- package/examples/stage-mcp/stage-mcp-missing-var.task.yaml +3 -0
- package/examples/stage-mcp/stage-mcp.pipeline.yaml +5 -0
- package/examples/stage-mcp/stage-mcp.task.yaml +4 -0
- package/examples/stage-mcp/use-echo.yaml +20 -0
- package/examples/stageflow.yaml +51 -0
- package/examples/verify-tour/README.md +80 -0
- package/examples/verify-tour/verify-tour.pipeline.yaml +162 -0
- package/examples/verify-tour/verify-tour.task.yaml +11 -0
- package/package.json +9 -1
- package/skills/stageflow/SKILL.md +6 -6
- package/skills/stageflow/references/control-surface.md +13 -5
- package/skills/stageflow/scripts/detect-host.mjs +14 -1
- package/skills/stageflow/scripts/detect-host.test.mjs +29 -1
- package/skills/stageflow-author/SKILL.md +4 -3
- package/skills/stageflow-author/references/catalog-mapping.md +16 -0
- package/skills/stageflow-author/references/catalog-write-conventions.md +1 -1
- package/skills/stageflow-author/references/stage-prompt-template.md +4 -4
- package/skills/stageflow-run/SKILL.md +22 -12
- package/skills/stageflow-run/references/mcp-call.md +9 -1
- package/skills/stageflow-run/references/task-and-pipeline-selection.md +19 -6
- package/skills/stageflow-run/scripts/mcp-call.mjs +46 -6
- package/skills/stageflow-run/scripts/mcp-call.test.mjs +64 -0
- package/skills/stageflow-session-capture/references/catalog-authoring.md +2 -0
- package/skills/stageflow-setup/SKILL.md +51 -8
- package/dist/ui/assets/index-Da5EHU6M.js +0 -119
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { access, chmod, mkdir, readdir, rm } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { globalStageflowHome } from "../project/globalHome.js";
|
|
4
|
+
import { createLocalAuditSink, safeAudit } from "./auditSink.js";
|
|
5
|
+
import { validateProfileKey, validateProfileScope, } from "./profileStore.js";
|
|
6
|
+
function browserRoot() {
|
|
7
|
+
return path.join(globalStageflowHome(), "browser");
|
|
8
|
+
}
|
|
9
|
+
function scopeDir(scope) {
|
|
10
|
+
return path.join(browserRoot(), scope);
|
|
11
|
+
}
|
|
12
|
+
async function ensurePrivateDir(dir) {
|
|
13
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
14
|
+
await chmod(dir, 0o700);
|
|
15
|
+
}
|
|
16
|
+
async function exists(p) {
|
|
17
|
+
try {
|
|
18
|
+
await access(p);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function createLocalProfileStore(options = {}) {
|
|
26
|
+
const audit = options.audit ?? createLocalAuditSink();
|
|
27
|
+
return {
|
|
28
|
+
async open(rawKey) {
|
|
29
|
+
const key = validateProfileKey(rawKey);
|
|
30
|
+
const base = path.join(scopeDir(key.scope), key.name);
|
|
31
|
+
const profileDir = path.join(base, "profile");
|
|
32
|
+
const stateDir = path.join(base, "state");
|
|
33
|
+
const created = !(await exists(base));
|
|
34
|
+
await ensurePrivateDir(browserRoot());
|
|
35
|
+
await ensurePrivateDir(scopeDir(key.scope));
|
|
36
|
+
await ensurePrivateDir(base);
|
|
37
|
+
await ensurePrivateDir(profileDir);
|
|
38
|
+
await ensurePrivateDir(stateDir);
|
|
39
|
+
if (created) {
|
|
40
|
+
await safeAudit(audit, {
|
|
41
|
+
event: "profile_created",
|
|
42
|
+
scope: key.scope,
|
|
43
|
+
profile: key.name,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return { key, profileDir, stateDir };
|
|
47
|
+
},
|
|
48
|
+
async list(scope) {
|
|
49
|
+
const dir = scopeDir(validateProfileScope(scope));
|
|
50
|
+
try {
|
|
51
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
52
|
+
return entries
|
|
53
|
+
.filter((e) => e.isDirectory())
|
|
54
|
+
.map((e) => e.name)
|
|
55
|
+
.sort();
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
if (err.code === "ENOENT")
|
|
59
|
+
return [];
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
async delete(rawKey) {
|
|
64
|
+
const key = validateProfileKey(rawKey);
|
|
65
|
+
const target = path.join(scopeDir(key.scope), key.name);
|
|
66
|
+
const existed = await exists(target);
|
|
67
|
+
await rm(target, { recursive: true, force: true });
|
|
68
|
+
if (existed) {
|
|
69
|
+
await safeAudit(audit, {
|
|
70
|
+
event: "profile_deleted",
|
|
71
|
+
scope: key.scope,
|
|
72
|
+
profile: key.name,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
async deleteScope(scope) {
|
|
77
|
+
const valid = validateProfileScope(scope);
|
|
78
|
+
for (const name of await this.list(valid)) {
|
|
79
|
+
await safeAudit(audit, {
|
|
80
|
+
event: "profile_deleted",
|
|
81
|
+
scope: valid,
|
|
82
|
+
profile: name,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
await rm(scopeDir(valid), {
|
|
86
|
+
recursive: true,
|
|
87
|
+
force: true,
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { StageBrowserConfig } from "../types/stage.js";
|
|
2
|
+
import type { BrowserEnv, BrowserRunner } from "./browserHost.js";
|
|
3
|
+
export declare const BROWSER_LOGIN_CHECK_FILENAME = "browser-login-check.json";
|
|
4
|
+
export type LoginCheckConfig = NonNullable<StageBrowserConfig["check"]>;
|
|
5
|
+
export type LoginState = "logged_in" | "logged_out" | "unknown";
|
|
6
|
+
export type LoginCheckMatch = {
|
|
7
|
+
state: LoginState;
|
|
8
|
+
/** `null` when the patterns cannot decide. */
|
|
9
|
+
logged_in: boolean | null;
|
|
10
|
+
};
|
|
11
|
+
export type LoginCheckResult = LoginCheckMatch & {
|
|
12
|
+
url: string;
|
|
13
|
+
attempt: number;
|
|
14
|
+
check_url: string;
|
|
15
|
+
};
|
|
16
|
+
export declare const OPEN_COMMAND_TIMEOUT_MS = 60000;
|
|
17
|
+
/** Pure: `logged_out_url` wins over `logged_in_url`; no match is `unknown`. */
|
|
18
|
+
export declare function matchLoginState(input: {
|
|
19
|
+
finalUrl: string;
|
|
20
|
+
check: Pick<LoginCheckConfig, "logged_in_url" | "logged_out_url">;
|
|
21
|
+
}): LoginCheckMatch;
|
|
22
|
+
export type LoginCheckOptions = {
|
|
23
|
+
waitMs?: number;
|
|
24
|
+
settleMs?: number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Opens `check.url` in the stage's own browser session (same persisted env, so
|
|
28
|
+
* no second browser is launched) and matches the settled final address. The
|
|
29
|
+
* session stays open for the stage; its teardown closes it later.
|
|
30
|
+
*/
|
|
31
|
+
export declare function runLoginCheck(env: BrowserEnv, check: LoginCheckConfig, runner: BrowserRunner, options?: LoginCheckOptions): Promise<LoginCheckMatch & {
|
|
32
|
+
url: string;
|
|
33
|
+
}>;
|
|
34
|
+
export declare function readStageLoginCheck(runDir: string, stageId: string): Promise<LoginCheckResult | undefined>;
|
|
35
|
+
/**
|
|
36
|
+
* Computes the Host result once per attempt and persists it in the stage run
|
|
37
|
+
* dir. A resume of the same attempt reuses the stored value, so the result the
|
|
38
|
+
* agent saw is the result the emit is validated against.
|
|
39
|
+
*/
|
|
40
|
+
export declare function ensureStageLoginCheck(input: {
|
|
41
|
+
runDir: string;
|
|
42
|
+
stageId: string;
|
|
43
|
+
attempt: number;
|
|
44
|
+
env: BrowserEnv;
|
|
45
|
+
check: LoginCheckConfig;
|
|
46
|
+
runner?: BrowserRunner;
|
|
47
|
+
options?: LoginCheckOptions;
|
|
48
|
+
}): Promise<LoginCheckResult>;
|
|
49
|
+
export type ExpectedLoginResult = Pick<LoginCheckResult, "state" | "logged_in" | "url">;
|
|
50
|
+
/** Prompt block that hands the Host result to the agent. */
|
|
51
|
+
export declare function loginCheckPromptBlock(result: ExpectedLoginResult): string;
|
|
52
|
+
/** Emit-time validation of the payload against the Host result; `undefined` when valid. */
|
|
53
|
+
export declare function loginCheckIssue(payload: unknown, expected: ExpectedLoginResult): string | undefined;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { stageDir } from "../runstore/paths.js";
|
|
4
|
+
import { defaultBrowserRunner } from "./browserTeardown.js";
|
|
5
|
+
export const BROWSER_LOGIN_CHECK_FILENAME = "browser-login-check.json";
|
|
6
|
+
const DEFAULT_WAIT_MS = 15_000;
|
|
7
|
+
const WAIT_TIMEOUT_MARGIN_MS = 15_000;
|
|
8
|
+
export const OPEN_COMMAND_TIMEOUT_MS = 60_000;
|
|
9
|
+
const DEFAULT_SETTLE_MS = 300;
|
|
10
|
+
const MAX_URL_READS = 4;
|
|
11
|
+
/** `*` matches any run of characters (including `/`), `?` one character. Whole-URL match. */
|
|
12
|
+
function globToRegExp(glob) {
|
|
13
|
+
let source = "";
|
|
14
|
+
for (const ch of glob.replace(/\*+/g, "*")) {
|
|
15
|
+
if (ch === "*")
|
|
16
|
+
source += ".*";
|
|
17
|
+
else if (ch === "?")
|
|
18
|
+
source += ".";
|
|
19
|
+
else
|
|
20
|
+
source += ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
21
|
+
}
|
|
22
|
+
return new RegExp(`^${source}$`, "s");
|
|
23
|
+
}
|
|
24
|
+
function asList(value) {
|
|
25
|
+
if (value === undefined)
|
|
26
|
+
return [];
|
|
27
|
+
return Array.isArray(value) ? value : [value];
|
|
28
|
+
}
|
|
29
|
+
function matchesAny(url, globs) {
|
|
30
|
+
return globs.some((glob) => globToRegExp(glob).test(url));
|
|
31
|
+
}
|
|
32
|
+
/** Pure: `logged_out_url` wins over `logged_in_url`; no match is `unknown`. */
|
|
33
|
+
export function matchLoginState(input) {
|
|
34
|
+
const { finalUrl, check } = input;
|
|
35
|
+
if (matchesAny(finalUrl, asList(check.logged_out_url))) {
|
|
36
|
+
return { state: "logged_out", logged_in: false };
|
|
37
|
+
}
|
|
38
|
+
if (matchesAny(finalUrl, asList(check.logged_in_url))) {
|
|
39
|
+
return { state: "logged_in", logged_in: true };
|
|
40
|
+
}
|
|
41
|
+
return { state: "unknown", logged_in: null };
|
|
42
|
+
}
|
|
43
|
+
async function readUrl(env, runner) {
|
|
44
|
+
const got = await runner(["get", "url"], env);
|
|
45
|
+
if (got.code !== 0) {
|
|
46
|
+
throw new Error("login check: could not read the page address");
|
|
47
|
+
}
|
|
48
|
+
return (got.stdout ?? "").trim();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Opens `check.url` in the stage's own browser session (same persisted env, so
|
|
52
|
+
* no second browser is launched) and matches the settled final address. The
|
|
53
|
+
* session stays open for the stage; its teardown closes it later.
|
|
54
|
+
*/
|
|
55
|
+
export async function runLoginCheck(env, check, runner, options = {}) {
|
|
56
|
+
const opened = await runner(["open", check.url], env, {
|
|
57
|
+
timeoutMs: OPEN_COMMAND_TIMEOUT_MS,
|
|
58
|
+
});
|
|
59
|
+
if (opened.code !== 0) {
|
|
60
|
+
throw new Error(`login check: could not open ${check.url}`);
|
|
61
|
+
}
|
|
62
|
+
const waitMs = options.waitMs ?? DEFAULT_WAIT_MS;
|
|
63
|
+
await runner(["wait", "--load", "networkidle", "--timeout", String(waitMs)], env, {
|
|
64
|
+
timeoutMs: waitMs + WAIT_TIMEOUT_MARGIN_MS,
|
|
65
|
+
}).catch(() => undefined);
|
|
66
|
+
const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
|
|
67
|
+
let url = await readUrl(env, runner);
|
|
68
|
+
for (let i = 1; i < MAX_URL_READS; i++) {
|
|
69
|
+
if (settleMs > 0)
|
|
70
|
+
await new Promise((r) => setTimeout(r, settleMs));
|
|
71
|
+
const next = await readUrl(env, runner);
|
|
72
|
+
if (next === url)
|
|
73
|
+
break;
|
|
74
|
+
url = next;
|
|
75
|
+
}
|
|
76
|
+
if (url === "") {
|
|
77
|
+
throw new Error("login check: the browser reported an empty address");
|
|
78
|
+
}
|
|
79
|
+
return { ...matchLoginState({ finalUrl: url, check }), url };
|
|
80
|
+
}
|
|
81
|
+
function resultFile(runDir, stageId) {
|
|
82
|
+
return path.join(stageDir(runDir, stageId), BROWSER_LOGIN_CHECK_FILENAME);
|
|
83
|
+
}
|
|
84
|
+
export async function readStageLoginCheck(runDir, stageId) {
|
|
85
|
+
try {
|
|
86
|
+
const parsed = JSON.parse(await readFile(resultFile(runDir, stageId), "utf8"));
|
|
87
|
+
if (parsed && typeof parsed === "object" && typeof parsed.url === "string") {
|
|
88
|
+
return parsed;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// not computed yet
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Computes the Host result once per attempt and persists it in the stage run
|
|
98
|
+
* dir. A resume of the same attempt reuses the stored value, so the result the
|
|
99
|
+
* agent saw is the result the emit is validated against.
|
|
100
|
+
*/
|
|
101
|
+
export async function ensureStageLoginCheck(input) {
|
|
102
|
+
const existing = await readStageLoginCheck(input.runDir, input.stageId);
|
|
103
|
+
if (existing !== undefined &&
|
|
104
|
+
existing.attempt === input.attempt &&
|
|
105
|
+
existing.check_url === input.check.url) {
|
|
106
|
+
return existing;
|
|
107
|
+
}
|
|
108
|
+
const probed = await runLoginCheck(input.env, input.check, input.runner ?? defaultBrowserRunner, input.options);
|
|
109
|
+
const result = {
|
|
110
|
+
...probed,
|
|
111
|
+
attempt: input.attempt,
|
|
112
|
+
check_url: input.check.url,
|
|
113
|
+
};
|
|
114
|
+
const file = resultFile(input.runDir, input.stageId);
|
|
115
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
116
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
117
|
+
await writeFile(tmp, `${JSON.stringify(result, null, 2)}\n`, { mode: 0o600 });
|
|
118
|
+
await rename(tmp, file);
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
/** Prompt block that hands the Host result to the agent. */
|
|
122
|
+
export function loginCheckPromptBlock(result) {
|
|
123
|
+
const lines = ["## Host login check", ""];
|
|
124
|
+
if (result.state === "unknown") {
|
|
125
|
+
lines.push(`The Host opened the check page and its final address is ${JSON.stringify(result.url)}, but the configured patterns cannot decide whether the session is logged in (result: unknown).`, "The browser session is already open on that page. Use the browser skill to inspect the page (read-only), decide whether the profile is logged in, and emit payload `{ \"logged_in\": <true|false>, \"url\": <that exact address> }`.", "The Host only checks that `logged_in` is a boolean and that `url` equals the address above.");
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
lines.push(`Host login check result: ${JSON.stringify({ logged_in: result.logged_in, url: result.url })}`, "Report exactly this in your envelope payload. Do not browse. The Host rejects any other value.");
|
|
129
|
+
}
|
|
130
|
+
return lines.join("\n");
|
|
131
|
+
}
|
|
132
|
+
/** Emit-time validation of the payload against the Host result; `undefined` when valid. */
|
|
133
|
+
export function loginCheckIssue(payload, expected) {
|
|
134
|
+
const value = payload && typeof payload === "object"
|
|
135
|
+
? payload
|
|
136
|
+
: {};
|
|
137
|
+
if (typeof value.logged_in !== "boolean") {
|
|
138
|
+
return "login check payload.logged_in must be a boolean";
|
|
139
|
+
}
|
|
140
|
+
if (value.url !== expected.url) {
|
|
141
|
+
return `login check payload.url must equal the Host-observed address ${JSON.stringify(expected.url)}`;
|
|
142
|
+
}
|
|
143
|
+
if (expected.logged_in !== null && value.logged_in !== expected.logged_in) {
|
|
144
|
+
return `login check payload.logged_in must be ${expected.logged_in} (Host result)`;
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { lockKey, ownerMatches, sameOwner, } from "./profileLock.js";
|
|
2
|
+
export function createInMemoryProfileLock(options = {}) {
|
|
3
|
+
const held = new Map();
|
|
4
|
+
const id = (key) => `${key.scope}\u0000${key.name}`;
|
|
5
|
+
const releaseIfHeldBy = (k, owner) => {
|
|
6
|
+
const current = held.get(k);
|
|
7
|
+
if (current !== undefined && sameOwner(current, owner))
|
|
8
|
+
held.delete(k);
|
|
9
|
+
};
|
|
10
|
+
return {
|
|
11
|
+
async acquire(rawKey, owner) {
|
|
12
|
+
const key = lockKey(rawKey);
|
|
13
|
+
const k = id(key);
|
|
14
|
+
let current = held.get(k);
|
|
15
|
+
if (current !== undefined &&
|
|
16
|
+
!sameOwner(current, owner) &&
|
|
17
|
+
options.isRunLive !== undefined &&
|
|
18
|
+
!(await options.isRunLive(current.runId))) {
|
|
19
|
+
held.delete(k);
|
|
20
|
+
current = undefined;
|
|
21
|
+
}
|
|
22
|
+
if (current !== undefined && !sameOwner(current, owner)) {
|
|
23
|
+
return { status: "queued", holder: { ...current } };
|
|
24
|
+
}
|
|
25
|
+
if (current === undefined)
|
|
26
|
+
held.set(k, { ...owner });
|
|
27
|
+
return {
|
|
28
|
+
status: "acquired",
|
|
29
|
+
release: async () => releaseIfHeldBy(k, owner),
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
async holder(rawKey) {
|
|
33
|
+
const current = held.get(id(lockKey(rawKey)));
|
|
34
|
+
return current === undefined ? undefined : { ...current };
|
|
35
|
+
},
|
|
36
|
+
async releaseOwner(target) {
|
|
37
|
+
for (const [k, owner] of [...held]) {
|
|
38
|
+
if (ownerMatches(owner, target))
|
|
39
|
+
held.delete(k);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
async reclaimStale(isRunLive) {
|
|
43
|
+
let dropped = 0;
|
|
44
|
+
for (const [k, owner] of [...held]) {
|
|
45
|
+
if (!(await isRunLive(owner.runId))) {
|
|
46
|
+
held.delete(k);
|
|
47
|
+
dropped += 1;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return dropped;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { validateProfileKey, validateProfileScope, } from "./profileStore.js";
|
|
2
|
+
export function createInMemoryProfileStore() {
|
|
3
|
+
const scopes = new Map();
|
|
4
|
+
return {
|
|
5
|
+
async open(rawKey) {
|
|
6
|
+
const key = validateProfileKey(rawKey);
|
|
7
|
+
let profiles = scopes.get(key.scope);
|
|
8
|
+
if (!profiles) {
|
|
9
|
+
profiles = new Map();
|
|
10
|
+
scopes.set(key.scope, profiles);
|
|
11
|
+
}
|
|
12
|
+
let handle = profiles.get(key.name);
|
|
13
|
+
if (!handle) {
|
|
14
|
+
const base = `memory://${key.scope}/${key.name}`;
|
|
15
|
+
handle = { key, profileDir: `${base}/profile`, stateDir: `${base}/state` };
|
|
16
|
+
profiles.set(key.name, handle);
|
|
17
|
+
}
|
|
18
|
+
return handle;
|
|
19
|
+
},
|
|
20
|
+
async list(scope) {
|
|
21
|
+
return [...(scopes.get(validateProfileScope(scope))?.keys() ?? [])].sort();
|
|
22
|
+
},
|
|
23
|
+
async delete(rawKey) {
|
|
24
|
+
const key = validateProfileKey(rawKey);
|
|
25
|
+
scopes.get(key.scope)?.delete(key.name);
|
|
26
|
+
},
|
|
27
|
+
async deleteScope(scope) {
|
|
28
|
+
scopes.delete(validateProfileScope(scope));
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { StageLogEvent } from "../runstore/port.js";
|
|
2
|
+
import { type AuditSink } from "./auditSink.js";
|
|
3
|
+
export type NavigationPolicy = {
|
|
4
|
+
runId: string;
|
|
5
|
+
stageId: string;
|
|
6
|
+
profile?: string;
|
|
7
|
+
allow_domains: string[];
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Soft allowlist check for profile stages (agent-browser refuses
|
|
11
|
+
* --allowed-domains with --profile). The only cheap signal is the persisted
|
|
12
|
+
* bash tool activity: it sees explicit `agent-browser open|goto|navigate <url>`
|
|
13
|
+
* commands, not redirects, link clicks or in-page navigation. Hosts only are
|
|
14
|
+
* recorded, never full URLs.
|
|
15
|
+
*/
|
|
16
|
+
export declare function auditStageNavigations(policy: NavigationPolicy, events: (() => Promise<StageLogEvent[]>) | undefined, sink: AuditSink | undefined): Promise<void>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { safeAudit } from "./auditSink.js";
|
|
2
|
+
import { hostOf, normalizeDomain } from "./sitePolicy.js";
|
|
3
|
+
const NAV_COMMAND = /\bagent-browser\b[^\n|;&]*?\s(?:open|goto|navigate|tab\s+new)\s+(?:--?\S+\s+)*["']?([^\s"'|;&]+)/g;
|
|
4
|
+
function allowed(host, allow) {
|
|
5
|
+
return allow.some((entry) => {
|
|
6
|
+
const d = normalizeDomain(entry);
|
|
7
|
+
return host === d || host.endsWith(`.${d}`);
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Soft allowlist check for profile stages (agent-browser refuses
|
|
12
|
+
* --allowed-domains with --profile). The only cheap signal is the persisted
|
|
13
|
+
* bash tool activity: it sees explicit `agent-browser open|goto|navigate <url>`
|
|
14
|
+
* commands, not redirects, link clicks or in-page navigation. Hosts only are
|
|
15
|
+
* recorded, never full URLs.
|
|
16
|
+
*/
|
|
17
|
+
export async function auditStageNavigations(policy, events, sink) {
|
|
18
|
+
if (policy.allow_domains.length === 0)
|
|
19
|
+
return;
|
|
20
|
+
const base = {
|
|
21
|
+
runId: policy.runId,
|
|
22
|
+
stageId: policy.stageId,
|
|
23
|
+
...(policy.profile !== undefined ? { profile: policy.profile } : {}),
|
|
24
|
+
};
|
|
25
|
+
let log;
|
|
26
|
+
try {
|
|
27
|
+
if (events === undefined)
|
|
28
|
+
throw new Error("no activity log available");
|
|
29
|
+
log = await events();
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
await safeAudit(sink, {
|
|
33
|
+
event: "allowlist_unverified",
|
|
34
|
+
...base,
|
|
35
|
+
reason: "stage activity log unavailable; navigations were not checked",
|
|
36
|
+
});
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const seen = new Set();
|
|
40
|
+
for (const event of log) {
|
|
41
|
+
if (event.event !== "tool_start" || !event.argsPreview)
|
|
42
|
+
continue;
|
|
43
|
+
for (const match of event.argsPreview.matchAll(NAV_COMMAND)) {
|
|
44
|
+
const host = hostOf(match[1] ?? "");
|
|
45
|
+
if (host === undefined || allowed(host, policy.allow_domains))
|
|
46
|
+
continue;
|
|
47
|
+
if (seen.has(host))
|
|
48
|
+
continue;
|
|
49
|
+
seen.add(host);
|
|
50
|
+
await safeAudit(sink, { event: "navigation_outside_allowlist", ...base, host });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
/** Reads a persisted browser env; undefined when absent, throws on other I/O errors. */
|
|
3
|
+
export async function readPersistedBrowserEnv(file) {
|
|
4
|
+
try {
|
|
5
|
+
const parsed = JSON.parse(await readFile(file, "utf8"));
|
|
6
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
7
|
+
return parsed;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
if (err.code !== "ENOENT")
|
|
12
|
+
throw err;
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type ProfileKey } from "./profileStore.js";
|
|
2
|
+
/**
|
|
3
|
+
* A profile is leased to a run. `stageId` is informational (the stage that
|
|
4
|
+
* first took the lease) and never part of identity.
|
|
5
|
+
*/
|
|
6
|
+
export type ProfileLockOwner = {
|
|
7
|
+
runId: string;
|
|
8
|
+
stageId?: string;
|
|
9
|
+
};
|
|
10
|
+
export type ProfileLockAcquireResult = {
|
|
11
|
+
status: "acquired";
|
|
12
|
+
release(): Promise<void>;
|
|
13
|
+
} | {
|
|
14
|
+
status: "queued";
|
|
15
|
+
holder: ProfileLockOwner;
|
|
16
|
+
};
|
|
17
|
+
export type RunLiveness = (runId: string) => Promise<boolean>;
|
|
18
|
+
export interface ProfileLock {
|
|
19
|
+
/**
|
|
20
|
+
* Any stage of the holder run joins the lease and succeeds at once. A
|
|
21
|
+
* profile leased to another run returns `queued` with the holder; callers retry.
|
|
22
|
+
*/
|
|
23
|
+
acquire(key: ProfileKey, owner: ProfileLockOwner): Promise<ProfileLockAcquireResult>;
|
|
24
|
+
holder(key: ProfileKey): Promise<ProfileLockOwner | undefined>;
|
|
25
|
+
/** Releases every lease held by this run (run end, cancel, abandon, failure). */
|
|
26
|
+
releaseOwner(owner: {
|
|
27
|
+
runId: string;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
/** Drops locks whose holder run is no longer live; returns the count dropped. */
|
|
30
|
+
reclaimStale(isRunLive: RunLiveness): Promise<number>;
|
|
31
|
+
}
|
|
32
|
+
export type ProfileLockOptions = {
|
|
33
|
+
/** When set, `acquire` reclaims a lock whose holder run is not live. */
|
|
34
|
+
isRunLive?: RunLiveness;
|
|
35
|
+
};
|
|
36
|
+
export declare function sameOwner(a: ProfileLockOwner, b: ProfileLockOwner): boolean;
|
|
37
|
+
export declare function lockKey(key: ProfileKey): ProfileKey;
|
|
38
|
+
export declare function ownerMatches(held: ProfileLockOwner, target: {
|
|
39
|
+
runId: string;
|
|
40
|
+
}): boolean;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { validateProfileKey } from "./profileStore.js";
|
|
2
|
+
export function sameOwner(a, b) {
|
|
3
|
+
return a.runId === b.runId;
|
|
4
|
+
}
|
|
5
|
+
export function lockKey(key) {
|
|
6
|
+
return validateProfileKey(key);
|
|
7
|
+
}
|
|
8
|
+
export function ownerMatches(held, target) {
|
|
9
|
+
return held.runId === target.runId;
|
|
10
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const LOCAL_BROWSER_SCOPE = "local";
|
|
2
|
+
export interface ProfileKey {
|
|
3
|
+
scope: string;
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ProfileHandle {
|
|
7
|
+
key: ProfileKey;
|
|
8
|
+
profileDir: string;
|
|
9
|
+
stateDir: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ProfileStore {
|
|
12
|
+
open(key: ProfileKey): Promise<ProfileHandle>;
|
|
13
|
+
list(scope: string): Promise<string[]>;
|
|
14
|
+
delete(key: ProfileKey): Promise<void>;
|
|
15
|
+
deleteScope(scope: string): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare class InvalidProfileKeyError extends Error {
|
|
18
|
+
readonly code = "invalid_profile_key";
|
|
19
|
+
constructor(message: string);
|
|
20
|
+
}
|
|
21
|
+
export declare function validateProfileName(name: unknown): string;
|
|
22
|
+
export declare function validateProfileScope(scope: unknown): string;
|
|
23
|
+
export declare function validateProfileKey(key: ProfileKey): ProfileKey;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// TODO(multi-tenant): this fixed scope is the single local owner. Callers import it instead of
|
|
2
|
+
// receiving a scope from the run, so profiles, locks and CLI commands always use "local".
|
|
3
|
+
// A hosted service must give every run an owner scope (set by the Host from the authenticated
|
|
4
|
+
// user, never from YAML) and pass it down. Find every call site with: grep -rn "LOCAL_BROWSER_SCOPE" src
|
|
5
|
+
// See docs/browser-internals.md, "Known limitations" item 3.
|
|
6
|
+
export const LOCAL_BROWSER_SCOPE = "local";
|
|
7
|
+
export class InvalidProfileKeyError extends Error {
|
|
8
|
+
code = "invalid_profile_key";
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "InvalidProfileKeyError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const MAX_SEGMENT_LENGTH = 64;
|
|
15
|
+
const SEGMENT_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
16
|
+
function validateSegment(label, value) {
|
|
17
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
18
|
+
throw new InvalidProfileKeyError(`Profile ${label} must be a non-empty string`);
|
|
19
|
+
}
|
|
20
|
+
if (value.length > MAX_SEGMENT_LENGTH) {
|
|
21
|
+
throw new InvalidProfileKeyError(`Profile ${label} must be at most ${MAX_SEGMENT_LENGTH} characters`);
|
|
22
|
+
}
|
|
23
|
+
if (!SEGMENT_PATTERN.test(value) || value.includes("..")) {
|
|
24
|
+
throw new InvalidProfileKeyError(`Profile ${label} "${value}" may only use letters, digits, ".", "_" and "-", must start with a letter or digit, and must not contain ".."`);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
export function validateProfileName(name) {
|
|
29
|
+
return validateSegment("name", name);
|
|
30
|
+
}
|
|
31
|
+
export function validateProfileScope(scope) {
|
|
32
|
+
return validateSegment("scope", scope);
|
|
33
|
+
}
|
|
34
|
+
export function validateProfileKey(key) {
|
|
35
|
+
return {
|
|
36
|
+
scope: validateProfileScope(key?.scope),
|
|
37
|
+
name: validateProfileName(key?.name),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RunStatus } from "../runstore/port.js";
|
|
2
|
+
import type { RunLiveness } from "./profileLock.js";
|
|
3
|
+
/**
|
|
4
|
+
* A run is live while it can still use its browser: not yet terminal. Runs
|
|
5
|
+
* waiting at a gate keep status "running", so they stay live.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createRunLiveness(source: {
|
|
8
|
+
readRunMeta(runId: string): Promise<{
|
|
9
|
+
status?: RunStatus;
|
|
10
|
+
}>;
|
|
11
|
+
}): RunLiveness;
|
|
12
|
+
/** `cli-<pid>-...` runs are live while that process is; undefined for other run ids. */
|
|
13
|
+
export declare function cliRunLive(runId: string): boolean | undefined;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A run is live while it can still use its browser: not yet terminal. Runs
|
|
3
|
+
* waiting at a gate keep status "running", so they stay live.
|
|
4
|
+
*/
|
|
5
|
+
export function createRunLiveness(source) {
|
|
6
|
+
return async (runId) => {
|
|
7
|
+
const cli = cliRunLive(runId);
|
|
8
|
+
if (cli !== undefined)
|
|
9
|
+
return cli;
|
|
10
|
+
try {
|
|
11
|
+
const status = (await source.readRunMeta(runId)).status;
|
|
12
|
+
return (status === undefined ||
|
|
13
|
+
status === "created" ||
|
|
14
|
+
status === "queued" ||
|
|
15
|
+
status === "running");
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
if (err.code === "ENOENT")
|
|
19
|
+
return false;
|
|
20
|
+
if (/not found/i.test(String(err?.message)))
|
|
21
|
+
return false;
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** `cli-<pid>-...` runs are live while that process is; undefined for other run ids. */
|
|
27
|
+
export function cliRunLive(runId) {
|
|
28
|
+
const match = /^cli-(\d+)-/.exec(runId);
|
|
29
|
+
return match === null ? undefined : pidAlive(Number(match[1]));
|
|
30
|
+
}
|
|
31
|
+
function pidAlive(pid) {
|
|
32
|
+
try {
|
|
33
|
+
process.kill(pid, 0);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
return err.code === "EPERM";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { StageBrowserConfig } from "../types/stage.js";
|
|
2
|
+
export declare class BlockedSiteError extends Error {
|
|
3
|
+
readonly code = "browser_site_blocked";
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
export declare function normalizeDomain(raw: string): string;
|
|
7
|
+
export declare function hostOf(urlOrHost: string): string | undefined;
|
|
8
|
+
export declare function isBlockedHost(host: string, blocked: readonly string[]): string | undefined;
|
|
9
|
+
/** Throws when the stage's allow_domains or check URLs touch a Host-blocked site. */
|
|
10
|
+
export declare function assertBrowserSitesAllowed(stageId: string, browser: StageBrowserConfig, blocked: readonly string[]): void;
|