mixdog 1.4.0 → 1.4.2
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/package.json +3 -2
- package/scripts/build-shipped-history.mjs +115 -33
- package/scripts/check-voice-runtime-inputs.mjs +25 -0
- package/scripts/gate-local.mjs +183 -63
- package/scripts/latest-guard.mjs +21 -9
- package/scripts/lib/gate-scheduler.mjs +138 -32
- package/scripts/lib/gate-timing-reporter.mjs +16 -0
- package/scripts/lib/isolated-test-env.mjs +10 -5
- package/scripts/lib/run-node-tests.mjs +91 -66
- package/scripts/lib/scoped-temp.d.mts +16 -0
- package/scripts/lib/scoped-temp.mjs +124 -0
- package/scripts/lib/stale-temp.mjs +44 -0
- package/scripts/lib/test-failure-records.mjs +14 -1
- package/scripts/lib/test-failure-summary.mjs +16 -4
- package/scripts/lib/test-spawn-ledger.mjs +12 -3
- package/scripts/post-release-check.mjs +1 -6
- package/scripts/pptx-kit-index.mjs +17 -5
- package/scripts/suite-health.mjs +115 -24
- package/scripts/test-data-dir.mjs +10 -7
- package/scripts/test.mjs +9 -14
- package/src/agents/advisor/AGENT.md +4 -4
- package/src/agents/advisor/agent.json +1 -1
- package/src/agents/explore/AGENT.md +22 -0
- package/src/agents/explore/agent.json +6 -0
- package/src/app.mjs +4 -0
- package/src/cli.mjs +7 -0
- package/src/defaults/agents.json +7 -0
- package/src/defaults/shipped-history.json +23 -2
- package/src/defaults/skills/browser-use/SKILL.md +10 -5
- package/src/defaults/skills/code-tidy/SKILL.md +71 -102
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +66 -114
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +19 -35
- package/src/defaults/skills/code-tidy/references/dead-code.md +3 -9
- package/src/defaults/skills/code-tidy/references/test-suite-slop.md +16 -27
- package/src/defaults/skills/computer-use/SKILL.md +16 -4
- package/src/defaults/skills/goal-management/SKILL.md +1 -1
- package/src/defaults/skills/setup/references/actions.md +12 -6
- package/src/defaults/skills/setup/references/surfaces.md +2 -2
- package/src/defaults/skills/setup-guide/SKILL.md +5 -2
- package/src/defaults/skills/setup-guide/model-recommendations.json +13 -0
- package/src/defaults/skills/skill-creator/SKILL.md +85 -190
- package/src/defaults/skills/skill-creator/references/design.md +70 -162
- package/src/defaults/skills/skill-creator/references/evaluation.md +33 -92
- package/src/defaults/skills/test-suite/SKILL.md +194 -0
- package/src/defaults/skills/test-suite/references/case-management.md +51 -0
- package/src/defaults/skills/test-suite/references/flaky-policy.md +39 -0
- package/src/defaults/skills/test-suite/references/reliability.md +49 -0
- package/src/defaults/skills/test-suite/references/suite-design.md +78 -0
- package/src/defaults/skills/test-suite/scripts/inventory.mjs +335 -0
- package/src/defaults/skills/visualize/SKILL.md +6 -2
- package/src/lib/rules-builder.cjs +35 -42
- package/src/output-styles/common.md +2 -2
- package/src/output-styles/simple.md +1 -1
- package/src/rules/agent/AGENT.md +5 -4
- package/src/rules/lead/LEAD.md +12 -7
- package/src/rules/shared/10-tool-workflow.md +3 -1
- package/src/rules/shared/20-research.md +3 -6
- package/src/rules/shared/40-editing.md +1 -0
- package/src/rules/shared/45-destructive.md +0 -2
- package/src/rules/shared/50-execution.md +2 -0
- package/src/rules/shared/60-verification.md +10 -9
- package/src/rules/shared/70-delivery.md +8 -9
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/abort-link.mjs +5 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +10 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +156 -24
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -0
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
- package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/config.mjs +12 -7
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +8 -8
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +5 -26
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +3 -1
- package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +122 -12
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -3
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +13 -2
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +152 -58
- package/src/runtime/agent/orchestrator/mcp/client.mjs +51 -18
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +88 -54
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +42 -26
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +54 -8
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +37 -31
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +31 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +40 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider-keys.mjs +34 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +14 -7
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +248 -41
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
- package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +0 -18
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +138 -6
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/model-cache.mjs +100 -33
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/oauth-secret-fields.fixtures.mjs +45 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +160 -31
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +22 -12
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +49 -22
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +45 -2
- package/src/runtime/agent/orchestrator/providers/vertex-ai-auth.mjs +622 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai-maas.mjs +185 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai-models.mjs +445 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +567 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +41 -13
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -6
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +7 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +6 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +14 -5
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -0
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -5
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +171 -37
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +102 -12
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +160 -36
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +164 -23
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +139 -19
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +100 -22
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -6
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +33 -3
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +23 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +23 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +39 -21
- package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +13 -8
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +50 -28
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +30 -13
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +63 -6
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +35 -13
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +24 -14
- package/src/runtime/agent/orchestrator/session/manager/session-scratch-environment.mjs +70 -0
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +0 -3
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +10 -6
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +56 -29
- package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +30 -7
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +37 -8
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +252 -39
- package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +163 -64
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +164 -4
- package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +61 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -20
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +67 -31
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +25 -13
- package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +96 -20
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +99 -2
- package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +103 -13
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +49 -15
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +45 -27
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +34 -10
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +28 -4
- package/src/runtime/agent/orchestrator/tools/lib/wait-until-fixture.mjs +26 -0
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -2
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
- package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
- package/src/runtime/channels/lib/whisper-port.mjs +29 -11
- package/src/runtime/channels/lib/whisper-server.mjs +10 -1
- package/src/runtime/computer-bridge/action-schema.mjs +2 -2
- package/src/runtime/computer-bridge/core-actions.mjs +2 -2
- package/src/runtime/computer-bridge/error-recovery.mjs +5 -4
- package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
- package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
- package/src/runtime/connectors/connector-accounts-mcp.mjs +236 -103
- package/src/runtime/connectors/gmail-app-password.mjs +3 -3
- package/src/runtime/explore/explorer.mjs +93 -0
- package/src/runtime/explore/tool-defs.mjs +35 -0
- package/src/runtime/explore/tool.mjs +123 -0
- package/src/runtime/local-provider/server-process.mjs +2 -1
- package/src/runtime/media/adapters/gemini-video.mjs +4 -1
- package/src/runtime/media/adapters/xai-media.mjs +2 -1
- package/src/runtime/memory/index.mjs +4 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +10 -3
- package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
- package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
- package/src/runtime/office/design/design-art-direction.mjs +29 -27
- package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
- package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
- package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
- package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
- package/src/runtime/office/quality/assurance-structure-docx.mjs +4 -2
- package/src/runtime/office/quality/design-review.mjs +1 -5
- package/src/runtime/office/quality/document-brief.mjs +1 -5
- package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
- package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
- package/src/runtime/shared/agent-route-config.mjs +17 -2
- package/src/runtime/shared/atomic-file.mjs +103 -2
- package/src/runtime/shared/background-tasks.mjs +15 -6
- package/src/runtime/shared/bot-owned-work.mjs +56 -0
- package/src/runtime/shared/connector-accounts.mjs +5 -5
- package/src/runtime/shared/connector-catalog.d.mts +11 -4
- package/src/runtime/shared/connector-catalog.mjs +60 -52
- package/src/runtime/shared/custom-provider-keys.mjs +20 -0
- package/src/runtime/shared/defaults-separation.mjs +1 -4
- package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
- package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
- package/src/runtime/shared/file-has-data.mjs +26 -2
- package/src/runtime/shared/file-lock.mjs +13 -15
- package/src/runtime/shared/git-launch.d.mts +9 -0
- package/src/runtime/shared/git-launch.mjs +213 -0
- package/src/runtime/shared/git-runner-worker.mjs +2 -1
- package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
- package/src/runtime/shared/handoff-artifacts.mjs +18 -8
- package/src/runtime/shared/idle-auto-clear-gate.mjs +29 -7
- package/src/runtime/shared/llm/cost.mjs +11 -2
- package/src/runtime/shared/llm/http-agent.mjs +313 -29
- package/src/runtime/shared/llm/model-catalog-projection.mjs +3 -2
- package/src/runtime/shared/llm/model-catalog.mjs +46 -6
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +3 -0
- package/src/runtime/shared/llm/system-proxy.mjs +452 -0
- package/src/runtime/shared/loopback-listener.mjs +25 -2
- package/src/runtime/shared/model-route-chain.d.mts +7 -2
- package/src/runtime/shared/model-route-chain.mjs +22 -4
- package/src/runtime/shared/open-url.mjs +10 -0
- package/src/runtime/shared/provider-accounts.mjs +52 -2
- package/src/runtime/shared/session-activity-signal.mjs +14 -0
- package/src/runtime/shared/session-scratch.mjs +37 -0
- package/src/runtime/shared/staged-install-worker.mjs +5 -0
- package/src/runtime/shared/staged-update.mjs +47 -2
- package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
- package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
- package/src/runtime/shared/tool-result-summary.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +113 -36
- package/src/runtime/shared/tool-work-units.mjs +6 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
- package/src/runtime/shared/turn-worktree-snapshot.mjs +414 -54
- package/src/runtime/shared/worktrees/activity-gate.mjs +215 -318
- package/src/runtime/shared/worktrees/activity-ledger-worker.mjs +56 -0
- package/src/runtime/shared/worktrees/activity-ledger.mjs +391 -0
- package/src/runtime/shared/worktrees/branch-management.mjs +9 -3
- package/src/runtime/shared/worktrees/contract.mjs +4 -1
- package/src/runtime/shared/worktrees/git.mjs +72 -0
- package/src/runtime/shared/worktrees/manager.mjs +285 -168
- package/src/runtime/shared/worktrees/test-support.mjs +6 -10
- package/src/runtime/tidy/install.mjs +95 -15
- package/src/runtime/tidy/languages.mjs +17 -15
- package/src/runtime/tidy/process.mjs +7 -4
- package/src/runtime/tidy/resolve.mjs +19 -16
- package/src/runtime/tidy/runners/biome.mjs +3 -3
- package/src/runtime/tidy/tool.mjs +1 -1
- package/src/runtime/tidy/worktree.mjs +10 -6
- package/src/runtime/visualize-bridge/client.mjs +6 -1
- package/src/runtime/visualize-bridge/tool-defs.mjs +7 -2
- package/src/runtime/web-search/index.mjs +1 -1
- package/src/session-runtime/agent-home-delivery.mjs +41 -0
- package/src/session-runtime/agent-task-sessions.mjs +458 -312
- package/src/session-runtime/agent-tool-routing.mjs +8 -41
- package/src/session-runtime/boot/tools.mjs +7 -1
- package/src/session-runtime/bot-status.mjs +107 -101
- package/src/session-runtime/bot-target.mjs +38 -0
- package/src/session-runtime/channel-config-api.mjs +6 -1
- package/src/session-runtime/connector-merge-migration.mjs +18 -15
- package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
- package/src/session-runtime/conversation-import/discovery.mjs +401 -36
- package/src/session-runtime/conversation-import/service.mjs +35 -8
- package/src/session-runtime/handoff-attachments.mjs +82 -31
- package/src/session-runtime/held-requests.mjs +3 -1
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +12 -13
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +1 -0
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -0
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +145 -112
- package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
- package/src/session-runtime/lifecycle/session-catalog.mjs +9 -1
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -1
- package/src/session-runtime/lifecycle/teardown.mjs +1 -1
- package/src/session-runtime/mcp-glue/server-catalog.mjs +16 -10
- package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
- package/src/session-runtime/mixy-heartbeat.mjs +1 -1
- package/src/session-runtime/model-route/set-route.mjs +11 -2
- package/src/session-runtime/provider-auth/login.mjs +2 -2
- package/src/session-runtime/provider-catalog-cache.mjs +82 -36
- package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
- package/src/session-runtime/provider-models.mjs +4 -2
- package/src/session-runtime/resource-mcp-api.mjs +98 -35
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +44 -31
- package/src/session-runtime/resource-mcp-secrets.mjs +28 -11
- package/src/session-runtime/resource-mcp-servers.mjs +5 -4
- package/src/session-runtime/resource-recall-api.mjs +6 -1
- package/src/session-runtime/runtime-feature-gates.mjs +5 -1
- package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
- package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
- package/src/session-runtime/services/agent-tool/render.mjs +71 -17
- package/src/session-runtime/services/agent-tool/send-flow.mjs +10 -2
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +8 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +6 -2
- package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
- package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
- package/src/session-runtime/services/channel-worker/daemon-spawn.mjs +2 -0
- package/src/session-runtime/services/daemon-candidate.mjs +4 -1
- package/src/session-runtime/services/memory-runtime-proxy/daemon-spawn.mjs +4 -0
- package/src/session-runtime/services/plugin-admin.mjs +10 -6
- package/src/session-runtime/services/provider-admin.mjs +283 -11
- package/src/session-runtime/session-turn-api.mjs +2 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +4 -1
- package/src/session-runtime/settings-system-api.mjs +1 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +13 -3
- package/src/session-runtime/setup-tool/connector-widen-approval.mjs +96 -0
- package/src/session-runtime/setup-tool/executor.mjs +6 -2
- package/src/session-runtime/setup-tool/tool-defs.mjs +2 -1
- package/src/session-runtime/tool-policy/policy-stage.mjs +1 -0
- package/src/session-runtime/turn/agent-ops.mjs +23 -0
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +49 -11
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +25 -5
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
- package/src/session-runtime/workspace/activity-guards.mjs +176 -0
- package/src/session-runtime/workspace/session-view.mjs +149 -0
- package/src/session-runtime/workspace/side-checkouts.mjs +203 -0
- package/src/session-runtime/workspace/transition-runner.mjs +188 -0
- package/src/session-runtime/workspace/transitions.mjs +108 -1041
- package/src/session-runtime/workspace/workspace-merge.mjs +132 -0
- package/src/session-runtime/workspace/worktree-intents.mjs +242 -0
- package/src/session-runtime/workspace/worktree-maintenance.mjs +80 -0
- package/src/session-runtime/workspace/worktree-setup.mjs +85 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +73 -56
- package/src/standalone/daemon-desktop-runtime.mjs +2 -0
- package/src/standalone/daemon-session-control.mjs +169 -138
- package/src/standalone/daemon.mjs +7 -0
- package/src/standalone/desktop-service-registry.mjs +9 -0
- package/src/standalone/local-session-runtime.mjs +5 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +3 -0
- package/src/standalone/session-client.mjs +5 -1
- package/src/standalone/session-frame-stream.mjs +83 -11
- package/src/standalone/session-protocol.mjs +4 -0
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
- package/src/standalone/session-service/agent-tree.mjs +45 -9
- package/src/standalone/session-service/projection/transcript-window.mjs +700 -64
- package/src/standalone/session-service/session-calls/session-action.mjs +10 -3
- package/src/standalone/session-service/session-calls/session-views.mjs +160 -104
- package/src/standalone/session-service/stored-reader.mjs +127 -40
- package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
- package/src/tui/App.jsx +154 -164
- package/src/tui/app/app-view/floating-panel.jsx +10 -4
- package/src/tui/app/app-view/text-entry-labels.mjs +1 -0
- package/src/tui/app/clipboard.mjs +21 -10
- package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
- package/src/tui/app/model-picker/route-selection.mjs +5 -6
- package/src/tui/app/prompt-handlers/use-prompt-history-nav.mjs +1 -1
- package/src/tui/app/prompt-handlers/use-prompt-interrupt.mjs +3 -0
- package/src/tui/app/prompt-submit/provider-prompts.mjs +3 -1
- package/src/tui/app/provider-setup/api-key-actions.mjs +31 -1
- package/src/tui/app/provider-setup/oauth-actions.mjs +58 -21
- package/src/tui/app/provider-setup/provider-items.mjs +8 -2
- package/src/tui/app/provider-setup-picker.mjs +29 -18
- package/src/tui/app/shell-layout/row-budgets.mjs +12 -10
- package/src/tui/app/slash-commands.mjs +1 -0
- package/src/tui/app/slash-dispatch/session-commands.mjs +46 -0
- package/src/tui/app/slash-dispatch.mjs +1 -1
- package/src/tui/app/use-btw-panel.mjs +29 -0
- package/src/tui/app/use-context-panel-refresh.mjs +31 -0
- package/src/tui/app/use-model-catalog-cache.mjs +13 -10
- package/src/tui/app/use-mouse-input.mjs +11 -0
- package/src/tui/app/use-onboarding-start.mjs +8 -3
- package/src/tui/app/use-panel-transition.mjs +10 -1
- package/src/tui/app/use-pasted-buffers.mjs +28 -20
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -0
- package/src/tui/app/use-prompt-layout-rows.mjs +3 -3
- package/src/tui/app/use-prompt-queue-history.mjs +3 -2
- package/src/tui/app/use-selection-focus.mjs +11 -2
- package/src/tui/app/use-selection-geometry.mjs +18 -9
- package/src/tui/app/use-selection-paint.mjs +4 -4
- package/src/tui/app/use-selection-stitch.mjs +3 -3
- package/src/tui/app/use-slash-palette.mjs +62 -0
- package/src/tui/app/use-terminal-chrome.mjs +2 -2
- package/src/tui/app/use-transcript-activity.mjs +47 -32
- package/src/tui/app/use-transcript-follow.mjs +12 -4
- package/src/tui/app/use-transcript-scroll.mjs +46 -17
- package/src/tui/app/use-transcript-swap-reset.mjs +27 -0
- package/src/tui/app/use-transcript-window/geometry.mjs +19 -5
- package/src/tui/app/use-transcript-window/instance-state.mjs +4 -0
- package/src/tui/app/use-transcript-window/selection-shift.mjs +5 -1
- package/src/tui/app/use-transcript-window.mjs +21 -11
- package/src/tui/app/use-tui-ready.mjs +18 -0
- package/src/tui/app/use-welcome-prompt-hint.mjs +5 -0
- package/src/tui/app/use-workflow-tab-cycle.mjs +5 -3
- package/src/tui/components/Picker.jsx +27 -3
- package/src/tui/components/QuestionPanel.jsx +13 -4
- package/src/tui/components/SignInUrl.jsx +68 -0
- package/src/tui/components/TextEntryPanel.jsx +76 -46
- package/src/tui/components/sign-in-url-layout.mjs +24 -0
- package/src/tui/dist/index.mjs +1963 -1361
- package/src/tui/session/goal-turn-state.mjs +6 -2
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/extensions.mjs +8 -0
- package/src/tui/session/session-api/intake/abort.mjs +2 -1
- package/src/tui/session/session-api/intake.mjs +2 -0
- package/src/tui/session/session-api/integrations.mjs +2 -2
- package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
- package/src/tui/session/session-api/settings.mjs +2 -2
- package/src/tui/session/session-flow/queue/take-entries.mjs +11 -0
- package/src/tui/session/transcript-spill/spill-writer.mjs +15 -0
- package/src/tui/session/transcript-spill.mjs +92 -4
- package/src/tui/session/transcript-store.mjs +2 -0
- package/src/ui/statusline-agents.mjs +1 -3
- package/src/defaults/skills/skill-creator/references/source-review.md +0 -94
|
@@ -1,42 +1,148 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
1
3
|
import { availableParallelism } from 'node:os';
|
|
2
|
-
import { testConcurrencyArg } from './run-node-tests.mjs';
|
|
3
4
|
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
// The local gate runs every lane from one shared pool of workers: each lane's
|
|
6
|
+
// files are cut into shards, and whichever worker is free takes the longest
|
|
7
|
+
// shard left, whatever its lane. Shard sizes come from the previous run's
|
|
8
|
+
// measured per-file times, so the split stays balanced without hand-kept
|
|
9
|
+
// weights, and no worker idles while another lane still has work.
|
|
10
|
+
|
|
11
|
+
// The gate runs its whole process tree at below-normal priority, so a
|
|
12
|
+
// foreground app always wins the CPU. Priority does not bound what the
|
|
13
|
+
// desktop shell shares with every process (handles, desktop heap, work
|
|
14
|
+
// queues; explorer.exe has failed with "no work queue available" under many
|
|
15
|
+
// concurrent test processes), so the default also caps the worker count:
|
|
16
|
+
// half the cores, leaving the other half for what the budget does not count
|
|
17
|
+
// (each shard's runner, the processes tests spawn, the desktop build's own
|
|
18
|
+
// threads), and never more than DEFAULT_MAX_WORKERS. Each worker is three
|
|
19
|
+
// node processes (shard runner, its node --test batch, the test file) plus
|
|
20
|
+
// whatever the test spawns. Measured on a 20-core Windows PC with other work
|
|
21
|
+
// running: at 10 workers, test-file processes began dying within ~20 ms with
|
|
22
|
+
// no output (every remaining file of a shard, in 2 of 2 runs); at 8 and 6
|
|
23
|
+
// they did not.
|
|
24
|
+
// An explicit budget still wins: --test-concurrency (gate argument), then
|
|
25
|
+
// MIXDOG_TEST_CONCURRENCY.
|
|
26
|
+
const DEFAULT_MAX_WORKERS = 8;
|
|
27
|
+
export function gateTestBudget(argv = [], env = process.env, parallelism = availableParallelism()) {
|
|
28
|
+
const index = argv.findIndex((arg) => arg === '--test-concurrency' || arg.startsWith('--test-concurrency='));
|
|
29
|
+
let explicit = env.MIXDOG_TEST_CONCURRENCY || undefined;
|
|
30
|
+
// A flag without a value is an error, not "unspecified".
|
|
31
|
+
if (index >= 0) explicit = argv[index].includes('=') ? argv[index].split('=')[1] : (argv[index + 1] ?? '');
|
|
32
|
+
if (explicit === undefined) return Math.max(1, Math.min(DEFAULT_MAX_WORKERS, Math.floor(parallelism / 2)));
|
|
33
|
+
const budget = Number(explicit);
|
|
34
|
+
if (!Number.isInteger(budget) || budget < 1) throw new Error(`gate test budget must be a positive integer (got "${explicit}")`);
|
|
35
|
+
return budget;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Estimates for work with no recorded time yet: the next run records it.
|
|
39
|
+
export const DEFAULT_ESTIMATE_MS = { fast: 2_000, slow: 25_000, command: 60_000 };
|
|
40
|
+
|
|
41
|
+
// Previous runs' measured times ({ version, ms: { key: ms } }). A missing,
|
|
42
|
+
// unreadable or malformed file counts as no history, and an entry that is
|
|
43
|
+
// not a finite positive number is dropped (its work gets the default).
|
|
44
|
+
export function readTimingHistory(file) {
|
|
45
|
+
let ms;
|
|
46
|
+
try {
|
|
47
|
+
ms = JSON.parse(readFileSync(file, 'utf8'))?.ms;
|
|
48
|
+
} catch {
|
|
49
|
+
return {};
|
|
50
|
+
}
|
|
51
|
+
if (!ms || typeof ms !== 'object' || Array.isArray(ms)) return {};
|
|
52
|
+
return Object.fromEntries(
|
|
53
|
+
Object.entries(ms).filter(([, value]) => typeof value === 'number' && Number.isFinite(value) && value > 0)
|
|
54
|
+
);
|
|
9
55
|
}
|
|
10
56
|
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
export function
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
57
|
+
// Writes the history through a temp file of its own (concurrent gates in
|
|
58
|
+
// other worktrees share the file) and renames it into place. Never throws:
|
|
59
|
+
// returns the error, or null once saved; the timings only plan the next run.
|
|
60
|
+
export function saveTimingHistory(file, ms) {
|
|
61
|
+
const temp = `${file}.${process.pid}.${randomUUID()}.tmp`;
|
|
62
|
+
try {
|
|
63
|
+
writeFileSync(temp, `${JSON.stringify({ version: 1, ms })}\n`);
|
|
64
|
+
renameSync(temp, file);
|
|
65
|
+
return null;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
try {
|
|
68
|
+
rmSync(temp, { force: true });
|
|
69
|
+
} catch {
|
|
70
|
+
// The save error is the one reported.
|
|
20
71
|
}
|
|
21
|
-
|
|
72
|
+
return error;
|
|
22
73
|
}
|
|
23
|
-
return shares;
|
|
24
74
|
}
|
|
75
|
+
// Enough shards per worker that the pool's tail (the shards still running
|
|
76
|
+
// when the queue is empty) stays short; the floor bounds the per-shard runner
|
|
77
|
+
// start-up and process sweep (~1-2 s each).
|
|
78
|
+
const SHARDS_PER_WORKER = 8;
|
|
79
|
+
const MIN_SHARD_MS = 15_000;
|
|
25
80
|
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
export
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Array.from({ length: budget }, async () => {
|
|
35
|
-
while (next < legs.length) await runLeg(legs[next++], 1);
|
|
36
|
-
})
|
|
37
|
-
);
|
|
38
|
-
return;
|
|
81
|
+
// test.mjs selects every lane file whose path contains a given filter, so a
|
|
82
|
+
// file whose path contains another file's path has to share that file's shard
|
|
83
|
+
// or it would run twice. Returns the indivisible groups.
|
|
84
|
+
export function filterGroups(files) {
|
|
85
|
+
const parent = new Map(files.map((file) => [file, file]));
|
|
86
|
+
const find = (file) => (parent.get(file) === file ? file : find(parent.get(file)));
|
|
87
|
+
for (const inner of files) {
|
|
88
|
+
for (const outer of files) if (outer !== inner && outer.includes(inner)) parent.set(find(outer), find(inner));
|
|
39
89
|
}
|
|
40
|
-
const
|
|
41
|
-
|
|
90
|
+
const groups = new Map();
|
|
91
|
+
for (const file of files) groups.set(find(file), [...(groups.get(find(file)) ?? []), file]);
|
|
92
|
+
return [...groups.values()];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Cuts each lane ({ name, files, estimateOf }) into shards of about one
|
|
96
|
+
// target size, longest-first into the least-loaded shard, so a file longer
|
|
97
|
+
// than the target runs alone. Returns [{ lane, files, estimate }].
|
|
98
|
+
export function planShards(lanes, budget) {
|
|
99
|
+
const total = lanes.reduce((sum, lane) => sum + lane.files.reduce((s, file) => s + lane.estimateOf(file), 0), 0);
|
|
100
|
+
const target = Math.max(MIN_SHARD_MS, total / (budget * SHARDS_PER_WORKER));
|
|
101
|
+
const shards = [];
|
|
102
|
+
for (const lane of lanes) {
|
|
103
|
+
const items = filterGroups(lane.files)
|
|
104
|
+
.map((files) => ({ files, estimate: files.reduce((s, file) => s + lane.estimateOf(file), 0) }))
|
|
105
|
+
.sort((a, b) => b.estimate - a.estimate);
|
|
106
|
+
const laneTotal = items.reduce((sum, item) => sum + item.estimate, 0);
|
|
107
|
+
const bins = Array.from({ length: Math.min(items.length, Math.max(1, Math.round(laneTotal / target))) }, () => ({
|
|
108
|
+
lane: lane.name,
|
|
109
|
+
files: [],
|
|
110
|
+
estimate: 0,
|
|
111
|
+
}));
|
|
112
|
+
for (const item of items) {
|
|
113
|
+
const bin = bins.reduce((least, candidate) => (candidate.estimate < least.estimate ? candidate : least));
|
|
114
|
+
bin.files.push(...item.files);
|
|
115
|
+
bin.estimate += item.estimate;
|
|
116
|
+
}
|
|
117
|
+
shards.push(...bins);
|
|
118
|
+
}
|
|
119
|
+
return shards;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Per-file time of a finished shard: what its own tests reported plus an
|
|
123
|
+
// equal share of the rest of the shard's wall time (process start-up, module
|
|
124
|
+
// load, the runner's own work). Returns { file: ms }.
|
|
125
|
+
export function measuredFileTimes(files, reportedMs, wallMs) {
|
|
126
|
+
const reported = files.reduce((sum, file) => sum + (reportedMs[file] ?? 0), 0);
|
|
127
|
+
const overhead = Math.max(0, wallMs - reported) / files.length;
|
|
128
|
+
return Object.fromEntries(files.map((file) => [file, (reportedMs[file] ?? 0) + overhead]));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Runs every unit on `budget` workers, longest estimate first. A failed unit
|
|
132
|
+
// marked stopOnFail stops new starts (running units finish). Returns the
|
|
133
|
+
// units never started.
|
|
134
|
+
export async function runPool(units, budget, run) {
|
|
135
|
+
if (!Number.isInteger(budget) || budget < 1) throw new Error('gate test budget must be a positive integer');
|
|
136
|
+
const queue = [...units].sort((a, b) => b.estimate - a.estimate);
|
|
137
|
+
let stopped = false;
|
|
138
|
+
await Promise.all(
|
|
139
|
+
Array.from({ length: Math.min(budget, queue.length) }, async () => {
|
|
140
|
+
while (!stopped && queue.length > 0) {
|
|
141
|
+
const unit = queue.shift();
|
|
142
|
+
unit.ok = await run(unit);
|
|
143
|
+
if (!unit.ok && unit.stopOnFail) stopped = true;
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
);
|
|
147
|
+
return queue;
|
|
42
148
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// node:test reporter the local gate adds to each shard it runs: one JSON line
|
|
2
|
+
// per test file, { file, ms }, the file's top-level test time (relative to the
|
|
3
|
+
// package the run started in, like `scripts/test.mjs --list`). The gate turns
|
|
4
|
+
// these into the per-file estimates its next run plans shards from.
|
|
5
|
+
import { relative } from 'node:path';
|
|
6
|
+
|
|
7
|
+
export default async function* gateTimingReporter(source) {
|
|
8
|
+
const byFile = new Map();
|
|
9
|
+
for await (const event of source) {
|
|
10
|
+
if (event.type !== 'test:pass' && event.type !== 'test:fail') continue;
|
|
11
|
+
if (event.data.nesting !== 0 || !event.data.file) continue;
|
|
12
|
+
const file = relative(process.cwd(), String(event.data.file)).replaceAll('\\', '/');
|
|
13
|
+
byFile.set(file, (byFile.get(file) ?? 0) + (Number(event.data.details?.duration_ms) || 0));
|
|
14
|
+
}
|
|
15
|
+
for (const [file, ms] of byFile) yield `${JSON.stringify({ file, ms })}\n`;
|
|
16
|
+
}
|
|
@@ -2,15 +2,20 @@
|
|
|
2
2
|
// evaluates imports in order) so the data/home dirs resolve to a scratch dir
|
|
3
3
|
// before any module under test reads them, even under plain `node --test` from a
|
|
4
4
|
// shell that exports the signed-in profile's MIXDOG_DATA_DIR.
|
|
5
|
-
import { mkdtempSync
|
|
5
|
+
import { mkdtempSync } from 'node:fs';
|
|
6
6
|
import { tmpdir } from 'node:os';
|
|
7
7
|
import { join } from 'node:path';
|
|
8
|
+
import { registerOwnedTempDir } from './scoped-temp.mjs';
|
|
9
|
+
import { pruneStaleTempDirs, removeTempDirSync } from './stale-temp.mjs';
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
// The exit handler below never runs for a killed process, so each start sweeps
|
|
12
|
+
// what earlier killed processes left.
|
|
13
|
+
const PREFIX = 'mixdog-test-profile-';
|
|
14
|
+
pruneStaleTempDirs([PREFIX]);
|
|
15
|
+
const root = mkdtempSync(join(tmpdir(), PREFIX));
|
|
16
|
+
registerOwnedTempDir(root);
|
|
10
17
|
process.env.MIXDOG_DATA_DIR = join(root, 'data');
|
|
11
18
|
process.env.MIXDOG_HOME = join(root, 'home');
|
|
12
19
|
process.on('exit', () => {
|
|
13
|
-
|
|
14
|
-
rmSync(root, { recursive: true, force: true });
|
|
15
|
-
} catch {}
|
|
20
|
+
removeTempDirSync(root);
|
|
16
21
|
});
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { appendFile, mkdtemp,
|
|
2
|
+
import { appendFile, mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
3
3
|
import { availableParallelism, tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
6
|
+
import { pruneStaleTempDirs } from './stale-temp.mjs';
|
|
6
7
|
import { FAILURE_RECORDS_ENV, spawnLedgerPath } from './test-failure-records.mjs';
|
|
7
8
|
import { classifyRerunOutcomes, formatFailureSummary } from './test-failure-summary.mjs';
|
|
8
9
|
|
|
@@ -20,28 +21,14 @@ const HEAP_FLAG = /^--(?:max-old-space-size|max-semi-space-size)=\d+$/;
|
|
|
20
21
|
// An argument costs its own characters plus the separator and its quotes.
|
|
21
22
|
const argCost = (arg) => arg.length + 3;
|
|
22
23
|
|
|
23
|
-
//
|
|
24
|
+
// A green run removes its log directory; a failed run keeps it (the failure
|
|
25
|
+
// summary points at it) until the start-of-run prune finds it stale. The
|
|
26
|
+
// failure and scratch directories are removed when their run ends, so only a
|
|
27
|
+
// killed runner leaves them for that same prune.
|
|
24
28
|
const TEST_LOG_PREFIX = 'mixdog-test-output-';
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
let names = [];
|
|
29
|
-
try {
|
|
30
|
-
names = await readdir(root);
|
|
31
|
-
} catch {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
const cutoff = Date.now() - TEST_LOG_RETENTION_MS;
|
|
35
|
-
for (const name of names) {
|
|
36
|
-
if (!name.startsWith(TEST_LOG_PREFIX)) continue;
|
|
37
|
-
const path = join(root, name);
|
|
38
|
-
try {
|
|
39
|
-
if ((await stat(path)).mtimeMs < cutoff) await rm(path, { recursive: true, force: true });
|
|
40
|
-
} catch {
|
|
41
|
-
/* another run may own or be removing it */
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
29
|
+
const TEST_FAILURES_PREFIX = 'mixdog-test-failures-';
|
|
30
|
+
const TEST_SCRATCH_PREFIX = 'mixdog-test-scratch-';
|
|
31
|
+
export const RUNNER_TEMP_PREFIXES = [TEST_LOG_PREFIX, TEST_FAILURES_PREFIX, TEST_SCRATCH_PREFIX];
|
|
45
32
|
|
|
46
33
|
// Batches keep the file order Node was given; a single path larger than the
|
|
47
34
|
// budget still gets its own batch rather than being dropped. An empty list
|
|
@@ -95,7 +82,8 @@ const concurrencyFlag = (arg) => arg === '--test-concurrency' || arg.startsWith(
|
|
|
95
82
|
function concurrencyOf(runArgs, fileArgs) {
|
|
96
83
|
const args = [...runArgs, ...fileArgs];
|
|
97
84
|
const index = args.findIndex(concurrencyFlag);
|
|
98
|
-
|
|
85
|
+
let value = null;
|
|
86
|
+
if (index >= 0) value = positiveInt(args[index].includes('=') ? args[index].split('=')[1] : args[index + 1]);
|
|
99
87
|
return value ?? Math.max(1, availableParallelism() - 1);
|
|
100
88
|
}
|
|
101
89
|
|
|
@@ -202,17 +190,29 @@ async function processSnapshot() {
|
|
|
202
190
|
});
|
|
203
191
|
}
|
|
204
192
|
|
|
205
|
-
|
|
193
|
+
// How far a snapshot's creation time may sit from Date.now() read around the
|
|
194
|
+
// spawn. Windows reports creation to the microsecond from the same system
|
|
195
|
+
// clock, so only a clock tick separates them; POSIX ps reports whole elapsed
|
|
196
|
+
// seconds. No wider: Windows hands a freed pid to the next process within
|
|
197
|
+
// milliseconds, and a 2 s window made a process another run had just started
|
|
198
|
+
// pass for one of this run's exited spawns, so it was killed with them.
|
|
199
|
+
const clockSlackMs = (platform) => (platform === 'win32' ? 50 : 2000);
|
|
206
200
|
|
|
207
201
|
// The processes this run owns, out of a snapshot: an anchor (the batch, or a
|
|
208
202
|
// child a test process recorded in its spawn ledger) still alive under its
|
|
209
|
-
// pid, the orphans an exited anchor left (they still name it as their parent
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
//
|
|
203
|
+
// pid, the orphans an exited anchor left (they still name it as their parent),
|
|
204
|
+
// every descendant of those, and anything executing from the run's scratch
|
|
205
|
+
// root (e.g. the postgres the memory runtime downloads into MIXDOG_DATA_DIR).
|
|
206
|
+
// An anchor is { pid, before, since, exited? }: the clock just before and just
|
|
207
|
+
// after its spawn, and when its exit was seen. A pid alone never identifies a
|
|
208
|
+
// process: an anchor seen to exit no longer holds its pid, so whatever holds
|
|
209
|
+
// it now is another process; a live holder was created within its spawn; an
|
|
210
|
+
// orphan was created while its anchor lived (a later process that reuses the
|
|
211
|
+
// pid only has younger children); a descendant is younger than its parent.
|
|
213
212
|
// Command-line text never makes a process the run's: an editor or terminal
|
|
214
213
|
// opened on the scratch root is not.
|
|
215
|
-
export function ownedProcesses(snapshot, anchors, {
|
|
214
|
+
export function ownedProcesses(snapshot, anchors, { scratchDir, platform = process.platform }) {
|
|
215
|
+
const slack = clockSlackMs(platform);
|
|
216
216
|
const byPid = new Map(snapshot.map((entry) => [entry.pid, entry]));
|
|
217
217
|
const childrenOf = new Map();
|
|
218
218
|
for (const entry of snapshot) childrenOf.set(entry.ppid, [...(childrenOf.get(entry.ppid) ?? []), entry]);
|
|
@@ -223,14 +223,19 @@ export function ownedProcesses(snapshot, anchors, { batchStart, scratchDir, plat
|
|
|
223
223
|
owned.set(entry.pid, entry);
|
|
224
224
|
queue.push(entry);
|
|
225
225
|
};
|
|
226
|
-
for (const { pid, since } of anchors) {
|
|
226
|
+
for (const { pid, before, since, exited } of anchors) {
|
|
227
227
|
const holder = byPid.get(pid);
|
|
228
|
-
if (holder && holder.created >=
|
|
228
|
+
if (holder && exited === undefined && holder.created >= before - slack && holder.created <= since + slack) {
|
|
229
229
|
claim(holder);
|
|
230
230
|
continue;
|
|
231
231
|
}
|
|
232
232
|
for (const orphan of childrenOf.get(pid) ?? []) {
|
|
233
|
-
if (
|
|
233
|
+
if (
|
|
234
|
+
orphan.created >= before - slack &&
|
|
235
|
+
orphan.created <= (exited ?? Number.POSITIVE_INFINITY) + slack &&
|
|
236
|
+
(!holder || orphan.created < holder.created)
|
|
237
|
+
)
|
|
238
|
+
claim(orphan);
|
|
234
239
|
}
|
|
235
240
|
}
|
|
236
241
|
const fold = (path) => (platform === 'win32' ? path.toLowerCase() : path);
|
|
@@ -242,26 +247,30 @@ export function ownedProcesses(snapshot, anchors, { batchStart, scratchDir, plat
|
|
|
242
247
|
while (queue.length > 0) {
|
|
243
248
|
const parent = queue.pop();
|
|
244
249
|
for (const entry of childrenOf.get(parent.pid) ?? []) {
|
|
245
|
-
if (entry.created >= parent.created -
|
|
250
|
+
if (entry.created >= parent.created - slack) claim(entry);
|
|
246
251
|
}
|
|
247
252
|
}
|
|
248
253
|
return [...owned.values()];
|
|
249
254
|
}
|
|
250
255
|
|
|
256
|
+
// One anchor per recorded spawn (scripts/lib/test-spawn-ledger.mjs): its
|
|
257
|
+
// "<pid> <before> <since>" line, with "<exited>" from the line that repeats
|
|
258
|
+
// it once the exit was seen.
|
|
251
259
|
async function readSpawnLedger(path) {
|
|
252
260
|
const text = await readFile(path, 'utf8').catch(() => '');
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
.
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
261
|
+
const spawns = new Map();
|
|
262
|
+
for (const line of text.split('\n').filter(Boolean)) {
|
|
263
|
+
const [pid, before, since, exited] = line.split(' ').map(Number);
|
|
264
|
+
const key = `${pid} ${before} ${since}`;
|
|
265
|
+
spawns.set(key, { ...(spawns.get(key) ?? { pid, before, since }), ...(exited ? { exited } : {}) });
|
|
266
|
+
}
|
|
267
|
+
return [...spawns.values()];
|
|
260
268
|
}
|
|
261
269
|
|
|
262
270
|
// A process a test leaves running must not outlive its batch: it can hold a
|
|
263
271
|
// port, a database, or files in the scratch root the run removes at the end.
|
|
264
|
-
|
|
272
|
+
// `batch` is the batch's own anchor, its exit already seen.
|
|
273
|
+
async function reapLeftovers(child, { batch, ledger, scratchDir }) {
|
|
265
274
|
// The batch itself has exited: only its process group can remain.
|
|
266
275
|
if (process.platform !== 'win32' && child.pid) {
|
|
267
276
|
try {
|
|
@@ -270,12 +279,12 @@ async function reapLeftovers(child, { batchStart, ledger, scratchDir }) {
|
|
|
270
279
|
/* the group is empty */
|
|
271
280
|
}
|
|
272
281
|
}
|
|
273
|
-
const anchors = [...(child.pid ? [
|
|
274
|
-
await killOwned(anchors, {
|
|
282
|
+
const anchors = [...(child.pid ? [batch] : []), ...(await readSpawnLedger(ledger))];
|
|
283
|
+
await killOwned(anchors, { scratchDir });
|
|
275
284
|
}
|
|
276
285
|
|
|
277
|
-
async function killOwned(anchors, {
|
|
278
|
-
const leftovers = ownedProcesses(await processSnapshot(), anchors, {
|
|
286
|
+
async function killOwned(anchors, { scratchDir }) {
|
|
287
|
+
const leftovers = ownedProcesses(await processSnapshot(), anchors, { scratchDir });
|
|
279
288
|
if (leftovers.length === 0) return;
|
|
280
289
|
const pids = leftovers.map((entry) => entry.pid);
|
|
281
290
|
console.error(`Killing ${pids.length} process(es) a test left running: ${pids.join(' ')}`);
|
|
@@ -292,12 +301,13 @@ async function killOwned(anchors, { batchStart, scratchDir }) {
|
|
|
292
301
|
}
|
|
293
302
|
}
|
|
294
303
|
|
|
295
|
-
// Resolves { code, hung } once the batch exited and its leftovers are gone.
|
|
304
|
+
// Resolves { code, hung, pid } once the batch exited and its leftovers are gone.
|
|
296
305
|
// `scratchDir` '' = leave the scratch-root sweep to the caller (side-by-side batches).
|
|
297
306
|
function spawnBatch(args, env, { files, ledger, scratchDir, timeoutMs }) {
|
|
298
307
|
return new Promise((resolve) => {
|
|
299
|
-
const
|
|
308
|
+
const before = Date.now();
|
|
300
309
|
const child = spawn(process.execPath, args, { stdio: 'inherit', env, detached: process.platform !== 'win32' });
|
|
310
|
+
const since = Date.now();
|
|
301
311
|
let hung = '';
|
|
302
312
|
const watchdog = setTimeout(() => {
|
|
303
313
|
hung = `hung after ${minutes(timeoutMs)} min: ${files.join(' ') || '(discovered files)'}`;
|
|
@@ -315,10 +325,11 @@ function spawnBatch(args, env, { files, ledger, scratchDir, timeoutMs }) {
|
|
|
315
325
|
const signals = process.platform === 'win32' ? [] : ['SIGINT', 'SIGTERM'];
|
|
316
326
|
for (const signal of signals) process.on(signal, forward);
|
|
317
327
|
const settle = async (code) => {
|
|
328
|
+
const batch = { pid: child.pid, before, since, exited: Date.now() };
|
|
318
329
|
clearTimeout(watchdog);
|
|
319
330
|
for (const signal of signals) process.off(signal, forward);
|
|
320
|
-
await reapLeftovers(child, {
|
|
321
|
-
resolve({ code: hung ? 1 : code, hung });
|
|
331
|
+
await reapLeftovers(child, { batch, ledger, scratchDir });
|
|
332
|
+
resolve({ code: hung ? 1 : code, hung, pid: child.pid });
|
|
322
333
|
};
|
|
323
334
|
child.on('error', (error) => {
|
|
324
335
|
console.error(error);
|
|
@@ -331,14 +342,14 @@ function spawnBatch(args, env, { files, ledger, scratchDir, timeoutMs }) {
|
|
|
331
342
|
// rm's own maxRetries re-walks the tree on every nested retry, so one held
|
|
332
343
|
// file a few directories down multiplied its delay into hours. Each pass here
|
|
333
344
|
// is a single bounded walk.
|
|
334
|
-
async function
|
|
345
|
+
async function removeTempDir(dir) {
|
|
335
346
|
for (let attempt = 1; ; attempt += 1) {
|
|
336
347
|
try {
|
|
337
348
|
await rm(dir, { recursive: true, force: true });
|
|
338
349
|
return;
|
|
339
350
|
} catch (error) {
|
|
340
351
|
if (attempt === 5) {
|
|
341
|
-
console.error(`Test
|
|
352
|
+
console.error(`Test temp dir left behind (${error.code}): ${dir}`);
|
|
342
353
|
return;
|
|
343
354
|
}
|
|
344
355
|
await delay(200 * attempt);
|
|
@@ -372,21 +383,37 @@ async function readFailureRecords(path) {
|
|
|
372
383
|
// log can still name what failed.
|
|
373
384
|
//
|
|
374
385
|
// `testTimeoutMs` is the per-file --test-timeout default (see testTimeoutArg).
|
|
375
|
-
export async function runNodeTests(
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
) {
|
|
380
|
-
await pruneStaleTestLogs(tmpdir());
|
|
381
|
-
const logPath = join(await mkdtemp(join(tmpdir(), TEST_LOG_PREFIX)), 'full.log');
|
|
386
|
+
export async function runNodeTests(nodeArgs, fileArgs, options = {}) {
|
|
387
|
+
pruneStaleTempDirs(RUNNER_TEMP_PREFIXES);
|
|
388
|
+
const outputDir = await mkdtemp(join(tmpdir(), TEST_LOG_PREFIX));
|
|
389
|
+
const logPath = join(outputDir, 'full.log');
|
|
382
390
|
console.error(`Full test log: ${logPath}`);
|
|
383
391
|
// Failure records live outside the log directory: the log directory holds
|
|
384
392
|
// exactly one merged log, and these records are an internal artifact.
|
|
385
|
-
const failureDir = await mkdtemp(join(tmpdir(),
|
|
393
|
+
const failureDir = await mkdtemp(join(tmpdir(), TEST_FAILURES_PREFIX));
|
|
386
394
|
// Test processes get a private temp root that is removed after the run, so
|
|
387
395
|
// every fixture directory a test forgets to delete goes with it instead of
|
|
388
396
|
// piling up in the system temp directory.
|
|
389
|
-
const scratchDir = await mkdtemp(join(tmpdir(),
|
|
397
|
+
const scratchDir = await mkdtemp(join(tmpdir(), TEST_SCRATCH_PREFIX));
|
|
398
|
+
let status = 1;
|
|
399
|
+
try {
|
|
400
|
+
status = await executeRun(nodeArgs, fileArgs, options, { logPath, failureDir, scratchDir });
|
|
401
|
+
} finally {
|
|
402
|
+
await removeTempDir(failureDir);
|
|
403
|
+
// Leftover processes are already killed; what a process outside the run
|
|
404
|
+
// still holds stays behind.
|
|
405
|
+
await removeTempDir(scratchDir);
|
|
406
|
+
// A throw keeps the log too: the run did not pass.
|
|
407
|
+
if (status === 0) await removeTempDir(outputDir);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async function executeRun(
|
|
412
|
+
nodeArgs,
|
|
413
|
+
fileArgs,
|
|
414
|
+
{ argBudget = ARG_BUDGET, coverage = false, rerunFailed = 0, testTimeoutMs = DEFAULT_TEST_TIMEOUT_MS },
|
|
415
|
+
{ logPath, failureDir, scratchDir }
|
|
416
|
+
) {
|
|
390
417
|
// The explicit entry also accepts Node options alongside its file paths.
|
|
391
418
|
const concurrencyArg = testConcurrencyArg([...nodeArgs, ...fileArgs]);
|
|
392
419
|
const timeoutArg = testTimeoutArg([...nodeArgs, ...fileArgs], process.env, testTimeoutMs);
|
|
@@ -458,7 +485,7 @@ export async function runNodeTests(
|
|
|
458
485
|
);
|
|
459
486
|
// Peers were still running while each batch exited, so the scratch root's
|
|
460
487
|
// own processes (e.g. a postgres a test started) are swept once, here.
|
|
461
|
-
if (overlapped) await killOwned([], {
|
|
488
|
+
if (overlapped) await killOwned([], { scratchDir });
|
|
462
489
|
// Logs and records merge in batch order, whatever order the batches finished.
|
|
463
490
|
for (const [index, outcome] of outcomes.entries()) {
|
|
464
491
|
const batchLog = batchLogOf(index);
|
|
@@ -509,15 +536,13 @@ export async function runNodeTests(
|
|
|
509
536
|
status = 0;
|
|
510
537
|
process.exitCode = status;
|
|
511
538
|
|
|
512
|
-
await rm(failureDir, { recursive: true, force: true });
|
|
513
|
-
// Leftover processes are already killed; what a process outside the run
|
|
514
|
-
// still holds stays behind.
|
|
515
|
-
await removeScratch(scratchDir);
|
|
516
539
|
if (coverageDir) {
|
|
517
540
|
const { reportCoverage } = await import('./coverage.mjs');
|
|
518
541
|
await reportCoverage(coverageDir);
|
|
519
542
|
await rm(coverageDir, { recursive: true, force: true });
|
|
520
543
|
}
|
|
521
544
|
// Last line of the run, after every reporter and the coverage report.
|
|
522
|
-
|
|
545
|
+
const pids = outcomes.filter((outcome) => outcome.code !== 0 && outcome.pid).map((outcome) => outcome.pid);
|
|
546
|
+
process.stdout.write(`\n${formatFailureSummary({ failed, flaky, rerunFailed, hung, exitCode: status, pids })}`);
|
|
547
|
+
return status;
|
|
523
548
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function makeTempDirSync(prefix: string): string;
|
|
2
|
+
export function registerOwnedTempDir(dir: string): string;
|
|
3
|
+
export function redirectSystemTemp(prefix?: string): string;
|
|
4
|
+
export function removeTempDirSync(dir: string): void;
|
|
5
|
+
export function removeTempDir(dir: string): Promise<void>;
|
|
6
|
+
export function tempDirFor(t: { after(fn: () => unknown): void }, prefix: string): string;
|
|
7
|
+
export interface ScopedTempDir {
|
|
8
|
+
path: string;
|
|
9
|
+
join(...parts: string[]): string;
|
|
10
|
+
remove(): Promise<void>;
|
|
11
|
+
removeSync(): void;
|
|
12
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
13
|
+
[Symbol.dispose](): void;
|
|
14
|
+
}
|
|
15
|
+
export function scopedTempDir(prefix: string): ScopedTempDir;
|
|
16
|
+
export function withTempDir<T>(prefix: string, fn: (dir: string) => T | Promise<T>): Promise<T>;
|