stageflow 0.32.0 → 0.35.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 +4 -3
- package/dist/agent/adapterSpill.d.ts +14 -0
- package/dist/agent/adapterSpill.js +83 -0
- package/dist/agent/agentBackend.d.ts +2 -2
- package/dist/agent/agentBackend.js +2 -2
- package/dist/agent/claudeAdapter.d.ts +13 -2
- package/dist/agent/claudeAdapter.js +50 -6
- package/dist/agent/cursorProvider.d.ts +3 -0
- package/dist/agent/cursorProvider.js +23 -6
- package/dist/agent/fakeAgent.d.ts +3 -3
- package/dist/agent/fakeAgent.js +0 -6
- package/dist/agent/piAdapter.d.ts +2 -2
- package/dist/agent/piAdapter.js +116 -15
- package/dist/agent/piIsolatedMcp.d.ts +1 -0
- package/dist/agent/piIsolatedMcp.js +19 -1
- package/dist/agent/piIsolatedMcpProbe.d.ts +11 -0
- package/dist/agent/piIsolatedMcpProbe.js +20 -0
- package/dist/agent/port.d.ts +2 -2
- package/dist/agent/providerAuth.d.ts +6 -0
- package/dist/agent/providerAuth.js +86 -17
- package/dist/agent/{resolveAgentPort.d.ts → resolveStagePort.d.ts} +8 -6
- package/dist/agent/{resolveAgentPort.js → resolveStagePort.js} +13 -9
- package/dist/agent/stageTurnDriver.d.ts +33 -0
- package/dist/agent/stageTurnDriver.js +64 -0
- package/dist/agent/toolOutputBudget.d.ts +46 -0
- package/dist/agent/toolOutputBudget.js +97 -0
- package/dist/agent/toolOutputBudgetExtension.d.ts +22 -0
- package/dist/agent/toolOutputBudgetExtension.js +141 -0
- package/dist/agent/toolOutputSpill.d.ts +34 -0
- package/dist/agent/toolOutputSpill.js +85 -0
- package/dist/browser/agentBrowserLiveViewRelay.d.ts +41 -0
- package/dist/browser/agentBrowserLiveViewRelay.js +801 -0
- package/dist/browser/anchor.d.ts +2 -0
- package/dist/browser/anchor.js +2 -1
- package/dist/browser/auditSink.d.ts +8 -0
- package/dist/browser/browserHost.d.ts +29 -0
- package/dist/browser/browserSweep.d.ts +4 -1
- package/dist/browser/browserSweep.js +7 -0
- package/dist/browser/browserTeardown.d.ts +1 -0
- package/dist/browser/browserTeardown.js +32 -2
- package/dist/browser/containerBrowserEndpoint.d.ts +11 -0
- package/dist/browser/containerBrowserEndpoint.js +57 -0
- package/dist/browser/containerBrowserHost.d.ts +24 -0
- package/dist/browser/containerBrowserHost.js +147 -0
- package/dist/browser/dockerSandboxOrchestrator.d.ts +26 -0
- package/dist/browser/dockerSandboxOrchestrator.js +142 -0
- package/dist/browser/fakeLiveViewRelay.d.ts +15 -0
- package/dist/browser/fakeLiveViewRelay.js +94 -0
- package/dist/browser/fakeLiveViewSource.d.ts +6 -0
- package/dist/browser/fakeLiveViewSource.js +16 -0
- package/dist/browser/fakeRemoteBrowserHost.d.ts +2 -1
- package/dist/browser/fakeRemoteBrowserHost.js +2 -1
- package/dist/browser/fakeSandboxOrchestrator.d.ts +9 -0
- package/dist/browser/fakeSandboxOrchestrator.js +44 -0
- package/dist/browser/gateHandoff.d.ts +0 -2
- package/dist/browser/gateHandoff.js +5 -11
- package/dist/browser/hostCapabilities.d.ts +23 -0
- package/dist/browser/hostCapabilities.js +23 -0
- package/dist/browser/humanLogin.d.ts +2 -2
- package/dist/browser/humanLogin.js +11 -2
- package/dist/browser/liveViewPersisted.d.ts +11 -0
- package/dist/browser/liveViewPersisted.js +22 -0
- package/dist/browser/liveViewRelay.d.ts +76 -0
- package/dist/browser/liveViewRelay.js +1 -0
- package/dist/browser/liveViewSessions.d.ts +25 -0
- package/dist/browser/liveViewSessions.js +139 -0
- package/dist/browser/liveViewSource.d.ts +25 -0
- package/dist/browser/liveViewSource.js +7 -0
- package/dist/browser/liveViewTickets.d.ts +46 -0
- package/dist/browser/liveViewTickets.js +122 -0
- package/dist/browser/localBrowserHost.d.ts +10 -0
- package/dist/browser/localBrowserHost.js +53 -7
- package/dist/browser/ownerScope.d.ts +10 -0
- package/dist/browser/ownerScope.js +6 -0
- package/dist/browser/profileStore.d.ts +5 -2
- package/dist/browser/profileStore.js +3 -5
- package/dist/browser/sandboxOrchestrator.d.ts +60 -0
- package/dist/browser/sandboxOrchestrator.js +8 -0
- package/dist/browser/sessionApiSandboxOrchestrator.d.ts +34 -0
- package/dist/browser/sessionApiSandboxOrchestrator.js +108 -0
- package/dist/browser/stageBrowserEnv.d.ts +7 -0
- package/dist/browser/stageBrowserEnv.js +53 -15
- package/dist/browser/stageHandoff.d.ts +47 -0
- package/dist/browser/stageHandoff.js +101 -0
- package/dist/browser/stageProfileLock.d.ts +1 -0
- package/dist/browser/stageProfileLock.js +1 -3
- package/dist/browser/volumeProfileStore.d.ts +7 -0
- package/dist/browser/volumeProfileStore.js +35 -0
- package/dist/cli/browserCommand.d.ts +3 -1
- package/dist/cli/browserCommand.js +25 -20
- package/dist/cli/resolveCliProjectRoot.d.ts +7 -0
- package/dist/cli/resolveCliProjectRoot.js +27 -0
- package/dist/cli/runCommand.d.ts +1 -1
- package/dist/cli/runCommand.js +59 -17
- package/dist/cli/skillsCommand.d.ts +20 -0
- package/dist/cli/skillsCommand.js +74 -31
- package/dist/cli/validateCommand.d.ts +1 -1
- package/dist/cli/validateCommand.js +22 -4
- package/dist/cli.js +4 -4
- package/dist/config/browseCatalog.d.ts +2 -0
- package/dist/config/browseCatalog.js +6 -0
- package/dist/config/catalogRelativePath.d.ts +4 -0
- package/dist/config/catalogRelativePath.js +37 -9
- package/dist/config/createStage.d.ts +1 -0
- package/dist/config/createStage.js +11 -2
- package/dist/config/createTask.d.ts +30 -0
- package/dist/config/createTask.js +161 -0
- package/dist/config/createTrigger.d.ts +15 -0
- package/dist/config/createTrigger.js +32 -25
- package/dist/config/draftPackage.d.ts +41 -0
- package/dist/config/draftPackage.js +216 -23
- package/dist/config/ensureCatalogScanRoot.d.ts +2 -1
- package/dist/config/ensureCatalogScanRoot.js +6 -2
- package/dist/config/findTaskInCatalog.d.ts +33 -0
- package/dist/config/findTaskInCatalog.js +69 -0
- package/dist/config/hostConfig.d.ts +21 -0
- package/dist/config/hostConfig.js +113 -1
- package/dist/config/importTask.d.ts +19 -0
- package/dist/config/importTask.js +133 -0
- package/dist/config/installSkillRequest.d.ts +40 -0
- package/dist/config/installSkillRequest.js +286 -0
- package/dist/config/listConfig.d.ts +0 -16
- package/dist/config/listConfig.js +0 -3
- package/dist/config/loadOutcome.d.ts +11 -6
- package/dist/config/loadPipeline.js +0 -1
- package/dist/config/loadStage.js +2 -0
- package/dist/config/loadStageflowManifest.js +4 -1
- package/dist/config/loadTask.js +2 -0
- package/dist/config/loadTrigger.js +2 -0
- package/dist/config/mergePipelineIncludes.js +3 -0
- package/dist/config/normalizePipelineStageEntry.js +10 -1
- package/dist/config/printTargetYaml.js +0 -1
- package/dist/config/publishDraftPackage.d.ts +5 -1
- package/dist/config/publishDraftPackage.js +4 -1
- package/dist/config/resolveCatalogRoots.d.ts +4 -1
- package/dist/config/resolveCatalogRoots.js +18 -1
- package/dist/config/skillUsageIndex.d.ts +6 -0
- package/dist/config/skillUsageIndex.js +39 -0
- package/dist/config/stageBrowser.js +13 -0
- package/dist/config/triggerAdapterStatus.d.ts +11 -0
- package/dist/config/triggerAdapterStatus.js +90 -0
- package/dist/config/triggerCatalog.d.ts +41 -2
- package/dist/config/triggerCatalog.js +107 -15
- package/dist/config/updateTask.d.ts +30 -0
- package/dist/config/updateTask.js +119 -0
- package/dist/config/updateTrigger.d.ts +38 -0
- package/dist/config/updateTrigger.js +220 -0
- package/dist/config/validateCatalog.d.ts +6 -2
- package/dist/config/validateCatalog.js +48 -7
- package/dist/config/workshopCatalogFilenames.d.ts +37 -0
- package/dist/config/workshopCatalogFilenames.js +125 -0
- package/dist/config/workshopCatalogRepair.d.ts +7 -0
- package/dist/config/workshopCatalogRepair.js +144 -0
- package/dist/config/yamlParsePosition.d.ts +4 -0
- package/dist/config/yamlParsePosition.js +9 -0
- package/dist/hosted/containment.d.ts +3 -0
- package/dist/hosted/containment.js +36 -0
- package/dist/hosted/errors.d.ts +21 -0
- package/dist/hosted/errors.js +30 -0
- package/dist/hosted/packageSkills.d.ts +3 -0
- package/dist/hosted/packageSkills.js +80 -0
- package/dist/hosted/runtime.d.ts +12 -0
- package/dist/hosted/runtime.js +596 -0
- package/dist/hosted/sequencer.d.ts +28 -0
- package/dist/hosted/sequencer.js +129 -0
- package/dist/hosted/translate.d.ts +3 -0
- package/dist/hosted/translate.js +43 -0
- package/dist/hosted/types.d.ts +108 -0
- package/dist/hosted/types.js +10 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +6 -0
- package/dist/mcp/getStartedTool.js +15 -6
- package/dist/mcp/triggerTools.js +2 -8
- package/dist/operatorAgent/agentPort.d.ts +11 -0
- package/dist/operatorAgent/agentPort.js +12 -0
- package/dist/operatorAgent/index.d.ts +1 -0
- package/dist/operatorAgent/index.js +1 -0
- package/dist/operatorAgent/piModel.js +26 -6
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.d.ts +1 -1
- package/dist/operatorAgent/profiles/workshopAuthorPlaybook.js +4 -2
- package/dist/operatorAgent/profiles/workshopAuthorTools.js +103 -16
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/project/ensureProjectRootsFromEnv.d.ts +3 -0
- package/dist/project/ensureProjectRootsFromEnv.js +22 -0
- package/dist/project/globalHome.d.ts +1 -0
- package/dist/project/globalHome.js +18 -0
- package/dist/project/workspaceCatalog.d.ts +10 -0
- package/dist/project/workspaceCatalog.js +91 -0
- package/dist/projection/projectRun.d.ts +0 -1
- package/dist/projection/projectRun.js +0 -1
- package/dist/runstore/backup.js +14 -4
- package/dist/runstore/port.d.ts +11 -1
- package/dist/runstore/restore.js +5 -2
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +8 -0
- package/dist/runstore/sqlite/SqliteRunStore.js +29 -9
- package/dist/runstore/sqlite/migrations/012-trigger-fires.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/012-trigger-fires.js +18 -0
- package/dist/runstore/sqlite/migrations/index.js +2 -0
- package/dist/runstore/stageSnapshot.d.ts +3 -1
- package/dist/runstore/stageSnapshot.js +19 -0
- package/dist/runstore/workspaceLayout.js +27 -0
- package/dist/runstore/writeContainedArtifact.d.ts +1 -0
- package/dist/runstore/writeContainedArtifact.js +52 -0
- package/dist/runtime/containerLimits.d.ts +0 -3
- package/dist/runtime/containerLimits.js +0 -5
- package/dist/runtime/credentialBinding.d.ts +3 -0
- package/dist/runtime/credentialBinding.js +23 -6
- package/dist/runtime/emailSource.js +20 -28
- package/dist/runtime/githubPollSource.js +20 -28
- package/dist/runtime/pipelineRunner.d.ts +4 -4
- package/dist/runtime/pipelineScheduler.d.ts +2 -2
- package/dist/runtime/pipelineScheduler.js +3 -0
- package/dist/runtime/resumeReconstruct.d.ts +2 -2
- package/dist/runtime/resumeTimedOut.d.ts +2 -2
- package/dist/runtime/runManager.d.ts +18 -2
- package/dist/runtime/runManager.js +95 -30
- package/dist/runtime/runRetentionSweep.d.ts +0 -2
- package/dist/runtime/runRetentionSweep.js +0 -8
- package/dist/runtime/runRetryCoordinator.d.ts +2 -2
- package/dist/runtime/scheduleSource.js +15 -24
- package/dist/runtime/stageAttemptBootstrap.d.ts +2 -2
- package/dist/runtime/stageAttemptBootstrap.js +10 -1
- package/dist/runtime/stageProcessLauncher.js +2 -2
- package/dist/runtime/stageRoots.js +12 -1
- package/dist/runtime/stageRunner.d.ts +2 -2
- package/dist/runtime/stageRunner.js +8 -2
- package/dist/runtime/stageWorker.js +2 -2
- package/dist/runtime/triggerPort.d.ts +5 -3
- package/dist/runtime/triggerRunner.d.ts +1 -0
- package/dist/runtime/triggerRunner.js +27 -20
- package/dist/runtime/webhookAccept.d.ts +25 -0
- package/dist/runtime/webhookAccept.js +56 -0
- package/dist/server/bootstrap.d.ts +7 -2
- package/dist/server/bootstrap.js +15 -1
- package/dist/server/controlToken.d.ts +1 -1
- package/dist/server/controlToken.js +18 -2
- package/dist/server/healthSurfaces.d.ts +12 -0
- package/dist/server/healthSurfaces.js +27 -6
- package/dist/server/http.d.ts +19 -10
- package/dist/server/http.js +605 -85
- package/dist/server/liveViewPath.d.ts +9 -0
- package/dist/server/liveViewPath.js +8 -0
- package/dist/server/liveViewRoutes.d.ts +58 -0
- package/dist/server/liveViewRoutes.js +565 -0
- package/dist/server/mcpHost.d.ts +4 -2
- package/dist/server/mcpHost.js +2 -0
- package/dist/server/readCatalogFile.d.ts +29 -0
- package/dist/server/readCatalogFile.js +215 -0
- package/dist/server/triggerRoutes.d.ts +1 -0
- package/dist/server/triggerRoutes.js +200 -73
- package/dist/tools/readEmail.d.ts +3 -3
- package/dist/tools/writeStageArtifact.d.ts +9 -0
- package/dist/tools/writeStageArtifact.js +2 -47
- package/dist/types/pipeline.d.ts +1 -1
- package/dist/types/stage.d.ts +1 -1
- package/dist/types/stageflowManifest.d.ts +1 -1
- package/dist/ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/ui/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/ui/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/ui/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/ui/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/ui/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/ui/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/ui/assets/index-BoFPkKFg.js +256 -0
- package/dist/ui/assets/{index-Bv4p1aPK.css → index-CIPbbBUi.css} +1 -1
- package/dist/ui/index.html +4 -4
- package/dist/workshop/autosave.d.ts +2 -2
- package/dist/workshop/autosave.js +4 -6
- package/dist/workshop/buildStore.d.ts +2 -0
- package/dist/workshop/chatAttachments.d.ts +27 -0
- package/dist/workshop/chatAttachments.js +98 -0
- package/dist/workshop/chatTurn.d.ts +5 -2
- package/dist/workshop/chatTurn.js +22 -18
- package/dist/workshop/sessionMutations.d.ts +17 -0
- package/dist/workshop/sessionMutations.js +107 -0
- package/dist/workshop/sessionStore.d.ts +10 -0
- package/dist/workshop/sessionStore.js +52 -0
- package/dist/workshop/workshopPin.d.ts +4 -3
- package/dist/workshop/workshopPin.js +26 -6
- package/dist/workshop/workshopTranscriptTurn.d.ts +8 -0
- package/dist/workshop/workshopTranscriptTurn.js +46 -0
- package/examples/README.md +1 -1
- package/examples/archify-on-pr/README.md +1 -1
- package/examples/archify-on-pr/archify-on-pr.pipeline.yaml +0 -1
- package/examples/archify-on-pr/author-diagrams.yaml +1 -0
- package/examples/conditional-fork/branch-a.yaml +1 -1
- package/examples/conditional-fork/decide.yaml +2 -1
- package/examples/feature-loop/{implement.yaml → feature-implement.yaml} +1 -1
- package/examples/feature-loop/feature-loop.pipeline.yaml +10 -10
- package/examples/feature-loop/{plan.yaml → feature-plan.yaml} +1 -1
- package/examples/feature-loop/{review.yaml → feature-review.yaml} +1 -1
- package/examples/feedback-loop/{implement.yaml → feedback-implement.yaml} +1 -1
- package/examples/feedback-loop/feedback-loop-wait-human.pipeline.yaml +9 -9
- package/examples/feedback-loop/feedback-loop.pipeline.yaml +9 -9
- package/examples/feedback-loop/{plan.yaml → feedback-plan.yaml} +1 -1
- package/examples/feedback-loop/{review.yaml → feedback-review.yaml} +1 -1
- package/examples/generic-fan-in/{research.yaml → fan-in-research.yaml} +1 -1
- package/examples/generic-fan-in/generic-fan-in-accepted.pipeline.yaml +3 -3
- package/examples/generic-fan-in/generic-fan-in.pipeline.yaml +3 -3
- package/examples/hello-world/{research.yaml → hello-research.yaml} +1 -1
- package/examples/hello-world/hello.pipeline.yaml +2 -2
- package/examples/mcp-hitl-tour/{review.yaml → mcp-hitl-review.yaml} +1 -1
- package/examples/mcp-hitl-tour/mcp-hitl-tour.pipeline.yaml +3 -3
- package/examples/oss-issue-contribution/oss-explain-fix.yaml +1 -0
- package/examples/oss-issue-contribution/oss-explain-issue.yaml +1 -0
- package/examples/oss-issue-contribution/oss-issue-contribution.pipeline.yaml +0 -2
- package/examples/stageflow.yaml +6 -0
- package/examples/triggers/sample-github-pr.trigger.yaml +12 -0
- package/examples/triggers/sample-inbox-email.trigger.yaml +14 -0
- package/examples/triggers/sample-manual-hello.trigger.yaml +5 -0
- package/examples/triggers/sample-nightly-hello.trigger.yaml +8 -0
- package/examples/triggers/sample-signed-webhook.trigger.yaml +11 -0
- package/examples/triggers-readiness/README.md +48 -0
- package/examples/triggers-readiness/readiness-emit.yaml +18 -0
- package/examples/triggers-readiness/readiness.pipeline.yaml +4 -0
- package/examples/triggers-readiness/readiness.task.yaml +6 -0
- package/examples/triggers-readiness/stageflow.yaml +13 -0
- package/examples/triggers-readiness/triggers/readiness-dynamic-manual.trigger.yaml +4 -0
- package/examples/triggers-readiness/triggers/readiness-email.trigger.yaml +12 -0
- package/examples/triggers-readiness/triggers/readiness-github-pr.trigger.yaml +12 -0
- package/examples/triggers-readiness/triggers/readiness-manual.trigger.yaml +5 -0
- package/examples/triggers-readiness/triggers/readiness-schedule.trigger.yaml +8 -0
- package/examples/triggers-readiness/triggers/readiness-webhook.trigger.yaml +11 -0
- package/package.json +9 -1
- package/skills/stageflow/references/control-surface.md +1 -1
- package/skills/stageflow-author/references/catalog-write-conventions.md +2 -2
- package/skills/stageflow-run/SKILL.md +1 -1
- package/skills/stageflow-run/references/mcp-call.md +1 -1
- package/skills/stageflow-setup/SKILL.md +1 -1
- package/dist/ui/assets/index-59cjF0OA.js +0 -133
|
@@ -1,30 +1,70 @@
|
|
|
1
1
|
import { mkdir, rename, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { loadHostConfig } from "../config/hostConfig.js";
|
|
3
|
+
import { browserHostForOwner, loadHostConfig } from "../config/hostConfig.js";
|
|
4
4
|
import { stageDir } from "../runstore/paths.js";
|
|
5
5
|
import { safeAudit } from "./auditSink.js";
|
|
6
6
|
import { assertBrowserSitesAllowed } from "./sitePolicy.js";
|
|
7
7
|
import { BROWSER_ENV_FILENAME, BROWSER_POLICY_FILENAME, } from "./browserHost.js";
|
|
8
8
|
import { ensureRunProfileBrowser } from "./anchor.js";
|
|
9
9
|
import { BROWSER_CLOSED_FILENAME, closeBrowserSession, defaultBrowserRunner, writeSessionOwner, } from "./browserTeardown.js";
|
|
10
|
-
import {
|
|
10
|
+
import { loginPageUrl } from "./humanLogin.js";
|
|
11
|
+
import { persistStageHandoff, resolveStageHandoffCapabilities } from "./stageHandoff.js";
|
|
11
12
|
import { ensureStageLoginCheck, OPEN_COMMAND_TIMEOUT_MS } from "./loginCheck.js";
|
|
12
13
|
import { readPersistedBrowserEnv } from "./persistedEnv.js";
|
|
14
|
+
import { createContainerBrowserHost } from "./containerBrowserHost.js";
|
|
15
|
+
import { createDockerSandboxOrchestrator } from "./dockerSandboxOrchestrator.js";
|
|
16
|
+
import { createVolumeProfileStore } from "./volumeProfileStore.js";
|
|
13
17
|
import { createLocalBrowserHost } from "./localBrowserHost.js";
|
|
14
18
|
import { createLocalProfileStore } from "./localProfileStore.js";
|
|
15
|
-
import {
|
|
19
|
+
import { localOwnerScope } from "./ownerScope.js";
|
|
16
20
|
export { BROWSER_ENV_FILENAME };
|
|
17
21
|
export async function readStagePersistedBrowserEnv(runDir, stageId) {
|
|
18
22
|
return readPersistedBrowserEnv(path.join(stageDir(runDir, stageId), BROWSER_ENV_FILENAME));
|
|
19
23
|
}
|
|
20
24
|
let defaultSupport;
|
|
21
|
-
export function
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
export function hostLaunchOptions() {
|
|
26
|
+
const config = loadHostConfig();
|
|
27
|
+
return {
|
|
28
|
+
launchArgs: config.browserLaunchArgs,
|
|
29
|
+
...(config.browserExecutablePath !== undefined
|
|
30
|
+
? { executablePath: config.browserExecutablePath }
|
|
31
|
+
: {}),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function configuredBrowserSupport(liveView) {
|
|
35
|
+
const config = loadHostConfig();
|
|
36
|
+
if (browserHostForOwner(config, localOwnerScope()) === "container") {
|
|
37
|
+
const container = config.browserContainer;
|
|
38
|
+
return {
|
|
39
|
+
host: createContainerBrowserHost({
|
|
40
|
+
orchestrator: createDockerSandboxOrchestrator({
|
|
41
|
+
image: container.image,
|
|
42
|
+
shmSize: container.shmSize,
|
|
43
|
+
memory: container.memory,
|
|
44
|
+
pidsLimit: container.pidsLimit,
|
|
45
|
+
user: "1000:1000",
|
|
46
|
+
}),
|
|
47
|
+
liveView,
|
|
48
|
+
local: hostLaunchOptions(),
|
|
49
|
+
}),
|
|
50
|
+
profiles: createVolumeProfileStore(),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
host: createLocalBrowserHost({ liveView, ...hostLaunchOptions() }),
|
|
24
55
|
profiles: createLocalProfileStore(),
|
|
25
56
|
};
|
|
57
|
+
}
|
|
58
|
+
export function defaultStageBrowserSupport() {
|
|
59
|
+
defaultSupport ??= configuredBrowserSupport("none");
|
|
26
60
|
return defaultSupport;
|
|
27
61
|
}
|
|
62
|
+
let consoleSupport;
|
|
63
|
+
/** Browser support for a process that serves the live view routes (`sf ui`, `sf mcp`). */
|
|
64
|
+
export function consoleStageBrowserSupport() {
|
|
65
|
+
consoleSupport ??= configuredBrowserSupport("relay");
|
|
66
|
+
return consoleSupport;
|
|
67
|
+
}
|
|
28
68
|
/**
|
|
29
69
|
* Computed once per stage and persisted in the run dir: agent-browser silently
|
|
30
70
|
* relaunches the browser when a command's launch env differs from the daemon's,
|
|
@@ -34,11 +74,9 @@ export async function resolveStageBrowserEnv(support, input) {
|
|
|
34
74
|
const { browser } = input;
|
|
35
75
|
if (browser === undefined)
|
|
36
76
|
return undefined;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
throw noScreenError(screen.docker);
|
|
41
|
-
}
|
|
77
|
+
const capabilities = resolveStageHandoffCapabilities(support, {
|
|
78
|
+
...(input.humanLogin === true ? { humanLogin: true } : {}),
|
|
79
|
+
});
|
|
42
80
|
if ((browser.allow_domains?.length ?? 0) > 0 ||
|
|
43
81
|
browser.check !== undefined ||
|
|
44
82
|
browser.login_url !== undefined) {
|
|
@@ -46,10 +84,11 @@ export async function resolveStageBrowserEnv(support, input) {
|
|
|
46
84
|
}
|
|
47
85
|
const dir = stageDir(input.runDir, input.stageId);
|
|
48
86
|
const file = path.join(dir, BROWSER_ENV_FILENAME);
|
|
49
|
-
|
|
87
|
+
await mkdir(dir, { recursive: true });
|
|
88
|
+
await persistStageHandoff({ runDir: input.runDir, stageId: input.stageId }, capabilities);
|
|
50
89
|
const profile = browser.profile !== undefined
|
|
51
90
|
? await support.profiles.open({
|
|
52
|
-
scope:
|
|
91
|
+
scope: input.scope,
|
|
53
92
|
name: browser.profile,
|
|
54
93
|
})
|
|
55
94
|
: undefined;
|
|
@@ -97,10 +136,9 @@ export async function resolveStageBrowserEnv(support, input) {
|
|
|
97
136
|
if (persisted === undefined) {
|
|
98
137
|
await writeEnv(fresh);
|
|
99
138
|
if (browser.profile !== undefined) {
|
|
100
|
-
// TODO(multi-tenant): audit the run owner's scope.
|
|
101
139
|
await safeAudit(support.audit, {
|
|
102
140
|
event: "profile_used",
|
|
103
|
-
scope:
|
|
141
|
+
scope: input.scope,
|
|
104
142
|
profile: browser.profile,
|
|
105
143
|
runId: input.runId,
|
|
106
144
|
stageId: input.stageId,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { StageBrowserConfig } from "../types/stage.js";
|
|
2
|
+
import type { StageBrowserSupport } from "./browserHost.js";
|
|
3
|
+
import { type GateHandoff, type HostGateContext } from "./gateHandoff.js";
|
|
4
|
+
import { type BrowserHostCapabilities } from "./hostCapabilities.js";
|
|
5
|
+
/** The slice of the host capability record a stage's handoff depends on; persisted per stage. */
|
|
6
|
+
export type StageHandoffCapabilities = Pick<BrowserHostCapabilities, "display" | "liveView">;
|
|
7
|
+
type HandoffInput = {
|
|
8
|
+
runDir: string;
|
|
9
|
+
runId: string;
|
|
10
|
+
stageId: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Probes the display, applies the host's capabilities, and rejects a human-login
|
|
14
|
+
* stage that has neither a screen nor a live view. Pure of disk writes.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveStageHandoffCapabilities(support: StageBrowserSupport, options?: {
|
|
17
|
+
humanLogin?: boolean;
|
|
18
|
+
}): StageHandoffCapabilities;
|
|
19
|
+
export declare function persistStageHandoff(input: {
|
|
20
|
+
runDir: string;
|
|
21
|
+
stageId: string;
|
|
22
|
+
}, capabilities: StageHandoffCapabilities): Promise<void>;
|
|
23
|
+
/** Persisted capabilities of a stage; undefined when none were recorded (older runs, direct stage runs). */
|
|
24
|
+
export declare function readStageHandoff(input: {
|
|
25
|
+
runDir: string;
|
|
26
|
+
stageId: string;
|
|
27
|
+
}): Promise<StageHandoffCapabilities | undefined>;
|
|
28
|
+
export declare function gateHandoffFor(context: {
|
|
29
|
+
runId: string;
|
|
30
|
+
stageId: string;
|
|
31
|
+
capabilities: StageHandoffCapabilities;
|
|
32
|
+
}): GateHandoff | undefined;
|
|
33
|
+
/** What the Host stamps on every gate of a browser stage; the agent cannot supply it. */
|
|
34
|
+
export declare function hostGateContextFor(browser: StageBrowserConfig | undefined, context: {
|
|
35
|
+
runId: string;
|
|
36
|
+
stageId: string;
|
|
37
|
+
capabilities: StageHandoffCapabilities;
|
|
38
|
+
}): HostGateContext | undefined;
|
|
39
|
+
/** The handoff for a stage's gates and login prompt. `dirStageId` is the manifest id when it differs from the gate's stage id. */
|
|
40
|
+
export declare function stageGateHandoff(input: HandoffInput & {
|
|
41
|
+
dirStageId?: string;
|
|
42
|
+
}): Promise<GateHandoff | undefined>;
|
|
43
|
+
/** Host-owned gate fields for a stage; undefined for a stage without a browser. */
|
|
44
|
+
export declare function stageGateContext(input: HandoffInput & {
|
|
45
|
+
browser: StageBrowserConfig | undefined;
|
|
46
|
+
}): Promise<HostGateContext | undefined>;
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { stageDir } from "../runstore/paths.js";
|
|
4
|
+
import { browserSite, } from "./gateHandoff.js";
|
|
5
|
+
import { resolveBrowserHostCapabilities, } from "./hostCapabilities.js";
|
|
6
|
+
import { defaultDisplayProbe, noScreenError } from "./humanLogin.js";
|
|
7
|
+
const BROWSER_CAPABILITIES_FILENAME = "browser-capabilities.json";
|
|
8
|
+
/**
|
|
9
|
+
* Probes the display, applies the host's capabilities, and rejects a human-login
|
|
10
|
+
* stage that has neither a screen nor a live view. Pure of disk writes.
|
|
11
|
+
*/
|
|
12
|
+
export function resolveStageHandoffCapabilities(support, options = {}) {
|
|
13
|
+
const capabilities = resolveBrowserHostCapabilities(support.host.capabilities);
|
|
14
|
+
const probe = support.display ?? defaultDisplayProbe;
|
|
15
|
+
if (support.host.capabilities === undefined) {
|
|
16
|
+
capabilities.display = probe().hasDisplay ? "local_window" : "headless_only";
|
|
17
|
+
}
|
|
18
|
+
else if (capabilities.display === "local_window" && support.display !== undefined && !support.display().hasDisplay) {
|
|
19
|
+
capabilities.display = "headless_only";
|
|
20
|
+
}
|
|
21
|
+
if (options.humanLogin === true &&
|
|
22
|
+
capabilities.display === "headless_only" &&
|
|
23
|
+
capabilities.liveView === "none") {
|
|
24
|
+
throw noScreenError(probe().docker);
|
|
25
|
+
}
|
|
26
|
+
if (options.humanLogin === true &&
|
|
27
|
+
capabilities.display !== "local_window" &&
|
|
28
|
+
capabilities.viewerInput === "view_only") {
|
|
29
|
+
throw new Error("A human login stage needs an interactive live view, but this browser host offers a view-only one (missing capability viewer_input: interactive).");
|
|
30
|
+
}
|
|
31
|
+
return { display: capabilities.display, liveView: capabilities.liveView };
|
|
32
|
+
}
|
|
33
|
+
export async function persistStageHandoff(input, capabilities) {
|
|
34
|
+
const dir = stageDir(input.runDir, input.stageId);
|
|
35
|
+
await mkdir(dir, { recursive: true });
|
|
36
|
+
await writeFile(path.join(dir, BROWSER_CAPABILITIES_FILENAME), `${JSON.stringify({ display: capabilities.display, liveView: capabilities.liveView })}\n`, { mode: 0o600 });
|
|
37
|
+
}
|
|
38
|
+
/** Persisted capabilities of a stage; undefined when none were recorded (older runs, direct stage runs). */
|
|
39
|
+
export async function readStageHandoff(input) {
|
|
40
|
+
const file = path.join(stageDir(input.runDir, input.stageId), BROWSER_CAPABILITIES_FILENAME);
|
|
41
|
+
try {
|
|
42
|
+
const parsed = JSON.parse(await readFile(file, "utf8"));
|
|
43
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
44
|
+
const { display, liveView } = resolveBrowserHostCapabilities(parsed);
|
|
45
|
+
return { display, liveView };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
if (err.code !== "ENOENT")
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
export function gateHandoffFor(context) {
|
|
55
|
+
const { capabilities } = context;
|
|
56
|
+
if (capabilities.display === "local_window")
|
|
57
|
+
return { kind: "local_window" };
|
|
58
|
+
if (capabilities.liveView !== "none") {
|
|
59
|
+
return {
|
|
60
|
+
kind: "live_view",
|
|
61
|
+
url: `/api/runs/${encodeURIComponent(context.runId)}/stages/${encodeURIComponent(context.stageId)}/live-view`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
/** What the Host stamps on every gate of a browser stage; the agent cannot supply it. */
|
|
67
|
+
export function hostGateContextFor(browser, context) {
|
|
68
|
+
if (browser === undefined)
|
|
69
|
+
return undefined;
|
|
70
|
+
const site = browserSite(browser);
|
|
71
|
+
const handoff = gateHandoffFor(context);
|
|
72
|
+
return {
|
|
73
|
+
...(handoff !== undefined ? { handoff } : {}),
|
|
74
|
+
...(site !== undefined ? { site } : {}),
|
|
75
|
+
...(browser.profile !== undefined ? { profile: browser.profile } : {}),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// No record (older runs, direct stage runs) keeps its historical meaning: a local window.
|
|
79
|
+
const LEGACY_CAPABILITIES = { display: "local_window", liveView: "none" };
|
|
80
|
+
async function capabilitiesOf(input) {
|
|
81
|
+
return ((await readStageHandoff({ runDir: input.runDir, stageId: input.dirStageId ?? input.stageId })) ??
|
|
82
|
+
LEGACY_CAPABILITIES);
|
|
83
|
+
}
|
|
84
|
+
/** The handoff for a stage's gates and login prompt. `dirStageId` is the manifest id when it differs from the gate's stage id. */
|
|
85
|
+
export async function stageGateHandoff(input) {
|
|
86
|
+
return gateHandoffFor({
|
|
87
|
+
runId: input.runId,
|
|
88
|
+
stageId: input.stageId,
|
|
89
|
+
capabilities: await capabilitiesOf(input),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/** Host-owned gate fields for a stage; undefined for a stage without a browser. */
|
|
93
|
+
export async function stageGateContext(input) {
|
|
94
|
+
if (input.browser === undefined)
|
|
95
|
+
return undefined;
|
|
96
|
+
return hostGateContextFor(input.browser, {
|
|
97
|
+
runId: input.runId,
|
|
98
|
+
stageId: input.stageId,
|
|
99
|
+
capabilities: await capabilitiesOf(input),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -2,6 +2,7 @@ import type { StageBrowserSupport } from "./browserHost.js";
|
|
|
2
2
|
import type { ProfileLock, ProfileLockOwner, RunLiveness } from "./profileLock.js";
|
|
3
3
|
export declare function stageProfileLock(support: StageBrowserSupport): ProfileLock;
|
|
4
4
|
export type ProfileWaitInput = {
|
|
5
|
+
scope: string;
|
|
5
6
|
profile: string;
|
|
6
7
|
owner: ProfileLockOwner;
|
|
7
8
|
isRunLive?: RunLiveness;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createLocalProfileLock } from "./localProfileLock.js";
|
|
2
|
-
import { LOCAL_BROWSER_SCOPE } from "./profileStore.js";
|
|
3
2
|
const DEFAULT_LOCK_POLL_MS = 250;
|
|
4
3
|
let defaultLock;
|
|
5
4
|
export function stageProfileLock(support) {
|
|
@@ -14,8 +13,7 @@ export function stageProfileLock(support) {
|
|
|
14
13
|
*/
|
|
15
14
|
export async function acquireStageProfile(support, input) {
|
|
16
15
|
const locks = stageProfileLock(support);
|
|
17
|
-
|
|
18
|
-
const key = { scope: LOCAL_BROWSER_SCOPE, name: input.profile };
|
|
16
|
+
const key = { scope: input.scope, name: input.profile };
|
|
19
17
|
const pollMs = support.lockPollMs ?? DEFAULT_LOCK_POLL_MS;
|
|
20
18
|
let announced;
|
|
21
19
|
for (;;) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ProfileStore } from "./profileStore.js";
|
|
2
|
+
/**
|
|
3
|
+
* Profiles that live in a volume the sandbox orchestrator provisions. The handle
|
|
4
|
+
* carries a reference, never a folder; creating, sizing and removing the volume
|
|
5
|
+
* belong to the orchestrator, so `delete` only forgets the reference.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createVolumeProfileStore(): ProfileStore;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { validateProfileKey, validateProfileScope, } from "./profileStore.js";
|
|
2
|
+
/**
|
|
3
|
+
* Profiles that live in a volume the sandbox orchestrator provisions. The handle
|
|
4
|
+
* carries a reference, never a folder; creating, sizing and removing the volume
|
|
5
|
+
* belong to the orchestrator, so `delete` only forgets the reference.
|
|
6
|
+
*/
|
|
7
|
+
export function createVolumeProfileStore() {
|
|
8
|
+
const scopes = new Map();
|
|
9
|
+
return {
|
|
10
|
+
async open(rawKey) {
|
|
11
|
+
const key = validateProfileKey(rawKey);
|
|
12
|
+
let profiles = scopes.get(key.scope);
|
|
13
|
+
if (!profiles) {
|
|
14
|
+
profiles = new Map();
|
|
15
|
+
scopes.set(key.scope, profiles);
|
|
16
|
+
}
|
|
17
|
+
let handle = profiles.get(key.name);
|
|
18
|
+
if (!handle) {
|
|
19
|
+
handle = { key, volumeRef: `${key.scope}/${key.name}` };
|
|
20
|
+
profiles.set(key.name, handle);
|
|
21
|
+
}
|
|
22
|
+
return handle;
|
|
23
|
+
},
|
|
24
|
+
async list(scope) {
|
|
25
|
+
return [...(scopes.get(validateProfileScope(scope))?.keys() ?? [])].sort();
|
|
26
|
+
},
|
|
27
|
+
async delete(rawKey) {
|
|
28
|
+
const key = validateProfileKey(rawKey);
|
|
29
|
+
scopes.get(key.scope)?.delete(key.name);
|
|
30
|
+
},
|
|
31
|
+
async deleteScope(scope) {
|
|
32
|
+
scopes.delete(validateProfileScope(scope));
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -16,6 +16,8 @@ export declare const BROWSER_EXIT: {
|
|
|
16
16
|
export type BrowserCommandDeps = {
|
|
17
17
|
log: (line: string) => void;
|
|
18
18
|
error: (line: string) => void;
|
|
19
|
+
/** Owner scope of the command; the CLI runs as the local owner when unset. */
|
|
20
|
+
scope?: string;
|
|
19
21
|
store: ProfileStore;
|
|
20
22
|
host: BrowserHost;
|
|
21
23
|
locks: ProfileLock;
|
|
@@ -29,7 +31,7 @@ export type BrowserCommandDeps = {
|
|
|
29
31
|
/** True when a daemon session for this env is open. */
|
|
30
32
|
sessionOpen: (env: BrowserEnv) => boolean;
|
|
31
33
|
/** profile name -> ISO time of last recorded use (from the audit log). */
|
|
32
|
-
auditLastUsed: () => Promise<Record<string, string>>;
|
|
34
|
+
auditLastUsed: (scope: string) => Promise<Record<string, string>>;
|
|
33
35
|
confirm: (question: string) => Promise<boolean>;
|
|
34
36
|
interactive: boolean;
|
|
35
37
|
signal?: AbortSignal;
|
|
@@ -6,13 +6,13 @@ import { createInterface } from "node:readline";
|
|
|
6
6
|
import { localAuditLogPath } from "../browser/auditSink.js";
|
|
7
7
|
import { closeBrowserSession, defaultBrowserRunner } from "../browser/browserTeardown.js";
|
|
8
8
|
import { defaultDisplayProbe, noScreenError } from "../browser/humanLogin.js";
|
|
9
|
+
import { hostLaunchOptions } from "../browser/stageBrowserEnv.js";
|
|
9
10
|
import { createLocalBrowserHost } from "../browser/localBrowserHost.js";
|
|
10
11
|
import { createLocalProfileLock } from "../browser/localProfileLock.js";
|
|
11
12
|
import { createLocalProfileStore } from "../browser/localProfileStore.js";
|
|
12
13
|
import { matchLoginState, runLoginCheck } from "../browser/loginCheck.js";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
import { InvalidProfileKeyError, LOCAL_BROWSER_SCOPE, validateProfileName, } from "../browser/profileStore.js";
|
|
14
|
+
import { localOwnerScope } from "../browser/ownerScope.js";
|
|
15
|
+
import { InvalidProfileKeyError, validateProfileName, } from "../browser/profileStore.js";
|
|
16
16
|
import { cliRunLive, createRunLiveness } from "../browser/runLiveness.js";
|
|
17
17
|
import { loadHostConfig } from "../config/hostConfig.js";
|
|
18
18
|
import { BlockedSiteError, hostOf, isBlockedHost } from "../browser/sitePolicy.js";
|
|
@@ -41,6 +41,9 @@ const DEFAULT_LOGIN_TIMEOUT_SEC = 300;
|
|
|
41
41
|
const LOGIN_POLL_MS = 1000;
|
|
42
42
|
const LOGIN_CLOSED_AFTER_FAILED_READS = 3;
|
|
43
43
|
const STAGE_ID = "browser-cli";
|
|
44
|
+
function scopeOf(deps) {
|
|
45
|
+
return deps.scope ?? localOwnerScope();
|
|
46
|
+
}
|
|
44
47
|
class CliError extends Error {
|
|
45
48
|
code;
|
|
46
49
|
exit;
|
|
@@ -88,7 +91,7 @@ function defaultSessionOpen(env) {
|
|
|
88
91
|
return false;
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
|
-
async function defaultAuditLastUsed() {
|
|
94
|
+
async function defaultAuditLastUsed(scope) {
|
|
92
95
|
const out = {};
|
|
93
96
|
let text;
|
|
94
97
|
try {
|
|
@@ -103,7 +106,7 @@ async function defaultAuditLastUsed() {
|
|
|
103
106
|
try {
|
|
104
107
|
const rec = JSON.parse(line);
|
|
105
108
|
if (rec.event === "profile_used" &&
|
|
106
|
-
rec.scope ===
|
|
109
|
+
rec.scope === scope &&
|
|
107
110
|
typeof rec.profile === "string" &&
|
|
108
111
|
typeof rec.at === "string" &&
|
|
109
112
|
(out[rec.profile] === undefined || rec.at > out[rec.profile])) {
|
|
@@ -132,7 +135,7 @@ function defaultDeps() {
|
|
|
132
135
|
log: (line) => console.log(line),
|
|
133
136
|
error: (line) => console.error(line),
|
|
134
137
|
store: createLocalProfileStore(),
|
|
135
|
-
host: createLocalBrowserHost(),
|
|
138
|
+
host: createLocalBrowserHost(hostLaunchOptions()),
|
|
136
139
|
locks: createLocalProfileLock({ isRunLive }),
|
|
137
140
|
runner: defaultBrowserRunner,
|
|
138
141
|
display: () => defaultDisplayProbe(),
|
|
@@ -201,17 +204,17 @@ function newOwner() {
|
|
|
201
204
|
return { runId: `cli-${process.pid}-${randomBytes(4).toString("hex")}`, stageId: STAGE_ID };
|
|
202
205
|
}
|
|
203
206
|
async function withProfile(deps, name, fn, options) {
|
|
204
|
-
const names = await deps.store.list(
|
|
207
|
+
const names = await deps.store.list(scopeOf(deps));
|
|
205
208
|
if (!options.create && !names.includes(name)) {
|
|
206
209
|
throw new CliError(`Profile "${name}" does not exist. Create it with sf browser login.`, "not_found", BROWSER_EXIT.error);
|
|
207
210
|
}
|
|
208
211
|
const owner = newOwner();
|
|
209
|
-
const got = await deps.locks.acquire({ scope:
|
|
212
|
+
const got = await deps.locks.acquire({ scope: scopeOf(deps), name }, owner);
|
|
210
213
|
if (got.status === "queued") {
|
|
211
214
|
throw new CliError(`Profile "${name}" is in use by run ${got.holder.runId}${got.holder.stageId !== undefined ? ` stage ${got.holder.stageId}` : ""}. Try again when it finishes.`, "profile_busy", BROWSER_EXIT.busy);
|
|
212
215
|
}
|
|
213
216
|
try {
|
|
214
|
-
const handle = await deps.store.open({ scope:
|
|
217
|
+
const handle = await deps.store.open({ scope: scopeOf(deps), name });
|
|
215
218
|
return await fn(handle);
|
|
216
219
|
}
|
|
217
220
|
finally {
|
|
@@ -240,16 +243,18 @@ function closeOpts(deps) {
|
|
|
240
243
|
};
|
|
241
244
|
}
|
|
242
245
|
async function profilesCmd(deps, p) {
|
|
243
|
-
const names = await deps.store.list(
|
|
244
|
-
const audited = await deps.auditLastUsed().catch(() => ({}));
|
|
246
|
+
const names = await deps.store.list(scopeOf(deps));
|
|
247
|
+
const audited = await deps.auditLastUsed(scopeOf(deps)).catch(() => ({}));
|
|
245
248
|
const profiles = [];
|
|
246
249
|
for (const name of names) {
|
|
247
250
|
let last = audited[name];
|
|
248
251
|
try {
|
|
249
|
-
const handle = await deps.store.open({ scope:
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
last
|
|
252
|
+
const handle = await deps.store.open({ scope: scopeOf(deps), name });
|
|
253
|
+
if (handle.profileDir !== undefined) {
|
|
254
|
+
const iso = (await stat(handle.profileDir)).mtime.toISOString();
|
|
255
|
+
if (last === undefined || iso > last)
|
|
256
|
+
last = iso;
|
|
257
|
+
}
|
|
253
258
|
}
|
|
254
259
|
catch {
|
|
255
260
|
// store without a real folder
|
|
@@ -263,9 +268,9 @@ async function profilesCmd(deps, p) {
|
|
|
263
268
|
}
|
|
264
269
|
async function statusCmd(deps, p) {
|
|
265
270
|
const name = requireName(p);
|
|
266
|
-
const exists = (await deps.store.list(
|
|
271
|
+
const exists = (await deps.store.list(scopeOf(deps))).includes(name);
|
|
267
272
|
let lock = null;
|
|
268
|
-
const holder = await deps.locks.holder({ scope:
|
|
273
|
+
const holder = await deps.locks.holder({ scope: scopeOf(deps), name });
|
|
269
274
|
if (holder !== undefined) {
|
|
270
275
|
lock = {
|
|
271
276
|
run_id: holder.runId,
|
|
@@ -275,7 +280,7 @@ async function statusCmd(deps, p) {
|
|
|
275
280
|
}
|
|
276
281
|
let sessionOpen = false;
|
|
277
282
|
if (exists) {
|
|
278
|
-
const handle = await deps.store.open({ scope:
|
|
283
|
+
const handle = await deps.store.open({ scope: scopeOf(deps), name });
|
|
279
284
|
sessionOpen = deps.sessionOpen(await envFor(deps, handle, { profile: name }));
|
|
280
285
|
}
|
|
281
286
|
const doc = { name, exists, locked: lock !== null, lock, session_open: sessionOpen };
|
|
@@ -403,7 +408,7 @@ async function loginCmd(deps, p) {
|
|
|
403
408
|
}
|
|
404
409
|
async function clearCmd(deps, p) {
|
|
405
410
|
const name = requireName(p);
|
|
406
|
-
if (!(await deps.store.list(
|
|
411
|
+
if (!(await deps.store.list(scopeOf(deps))).includes(name)) {
|
|
407
412
|
throw new CliError(`Profile "${name}" does not exist.`, "not_found", BROWSER_EXIT.error);
|
|
408
413
|
}
|
|
409
414
|
if (!p.yes) {
|
|
@@ -418,7 +423,7 @@ async function clearCmd(deps, p) {
|
|
|
418
423
|
return withProfile(deps, name, async (handle) => {
|
|
419
424
|
const env = await envFor(deps, handle, { profile: name });
|
|
420
425
|
await closeBrowserSession(env, closeOpts(deps)).catch(() => undefined);
|
|
421
|
-
await deps.store.delete({ scope:
|
|
426
|
+
await deps.store.delete({ scope: scopeOf(deps), name });
|
|
422
427
|
emit(deps, p.json, { cleared: true, name }, [`Cleared profile "${name}".`]);
|
|
423
428
|
return BROWSER_EXIT.ok;
|
|
424
429
|
}, { create: false });
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RunStore } from "../runstore/port.js";
|
|
2
|
+
export type ResolvedCliProjectRoot = {
|
|
3
|
+
rootPath: string;
|
|
4
|
+
wireId: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function resolveCliProjectRootLocal(flag: string): ResolvedCliProjectRoot | null;
|
|
7
|
+
export declare function resolveCliProjectRoot(flag: string, store: RunStore): Promise<ResolvedCliProjectRoot>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { findCatalogRoot, resolveCatalogRoots } from "../config/resolveCatalogRoots.js";
|
|
3
|
+
import { WORKSPACE_CATALOG_ID, workspaceCatalogPath, } from "../project/workspaceCatalog.js";
|
|
4
|
+
export function resolveCliProjectRootLocal(flag) {
|
|
5
|
+
const trimmed = flag.trim();
|
|
6
|
+
if (trimmed.length === 0)
|
|
7
|
+
return null;
|
|
8
|
+
if (trimmed === WORKSPACE_CATALOG_ID) {
|
|
9
|
+
return { rootPath: workspaceCatalogPath(), wireId: WORKSPACE_CATALOG_ID };
|
|
10
|
+
}
|
|
11
|
+
if (path.isAbsolute(trimmed)) {
|
|
12
|
+
const resolved = path.resolve(trimmed);
|
|
13
|
+
return { rootPath: resolved, wireId: resolved };
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
export async function resolveCliProjectRoot(flag, store) {
|
|
18
|
+
const local = resolveCliProjectRootLocal(flag);
|
|
19
|
+
if (local)
|
|
20
|
+
return local;
|
|
21
|
+
const roots = await resolveCatalogRoots({ store });
|
|
22
|
+
const found = findCatalogRoot(roots, flag.trim());
|
|
23
|
+
if (found === undefined) {
|
|
24
|
+
throw new Error(`Unknown project_root: ${flag.trim()}`);
|
|
25
|
+
}
|
|
26
|
+
return { rootPath: found.path, wireId: found.project_root };
|
|
27
|
+
}
|
package/dist/cli/runCommand.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { StartRunResult } from "../runtime/runManager.js";
|
|
|
3
3
|
import type { TaskFile } from "../types/task.js";
|
|
4
4
|
import { type EnsureGlobalServiceResult } from "../server/ensureGlobalService.js";
|
|
5
5
|
import { type CliRunReportIo } from "./runOutput.js";
|
|
6
|
-
export declare const RUN_USAGE = "Usage:\n sf run --task <path> --pipeline <path> [--checkout <path>] [--repository <owner/repo>] [--ref <ref>] [--json] [--include stages] [--skip-gates] [--git-sha <sha>] [--ci-pr-url <url>] [--ci-job-url <url>] [--operator-cwd <path>] [--operator-agent-dir <path>]";
|
|
6
|
+
export declare const RUN_USAGE = "Usage:\n sf run --task <path> --pipeline <path> [--project-root <id-or-path>] [--checkout <path>] [--repository <owner/repo>] [--ref <ref>] [--json] [--include stages] [--skip-gates] [--git-sha <sha>] [--ci-pr-url <url>] [--ci-job-url <url>] [--operator-cwd <path>] [--operator-agent-dir <path>]";
|
|
7
7
|
export type RunCommandIo = CliRunReportIo;
|
|
8
8
|
export type StartRunFn = (input: {
|
|
9
9
|
task: string | TaskFile;
|