@wardby/cli 0.7.0 → 0.9.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/.env.example +5 -5
- package/README.md +6 -1
- package/deploy/local/docker-compose.quickstart-coding.yml +11 -0
- package/dist/claude-coding-worker/driver.d.ts +12 -0
- package/dist/claude-coding-worker/driver.js +29 -7
- package/dist/claude-coding-worker/main.js +5 -1
- package/dist/claude-coding-worker/sdk.js +17 -1
- package/dist/claude-plugin/command.d.ts +64 -0
- package/dist/claude-plugin/command.js +379 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +2 -1
- package/dist/cli.js +71 -3
- package/dist/coding/code-host.d.ts +16 -0
- package/dist/coding/code-host.js +60 -0
- package/dist/coding/mcp-token.d.ts +12 -0
- package/dist/coding/mcp-token.js +26 -0
- package/dist/coding/protocol.js +2 -0
- package/dist/coding/redact.js +6 -1
- package/dist/coding/transcript/recorder.d.ts +5 -4
- package/dist/coding/transcript/recorder.js +3 -9
- package/dist/coding-worker/debug-trace.d.ts +3 -1
- package/dist/coding-worker/debug-trace.js +4 -2
- package/dist/coding-worker/driver.js +8 -1
- package/dist/coding-worker/main.js +5 -1
- package/dist/coding-worker/mcp-config.d.ts +27 -0
- package/dist/coding-worker/mcp-config.js +32 -0
- package/dist/coding-worker/sdk.d.ts +36 -0
- package/dist/coding-worker/sdk.js +28 -1
- package/dist/coding-worker/types.d.ts +4 -0
- package/dist/config/mcp-call-limit.d.ts +8 -0
- package/dist/config/mcp-call-limit.js +18 -0
- package/dist/config/mcp-servers.d.ts +8 -0
- package/dist/config/mcp-servers.js +12 -0
- package/dist/config/test-simulators.d.ts +10 -0
- package/dist/config/test-simulators.js +28 -0
- package/dist/config/tool-timeout.d.ts +1 -0
- package/dist/config/tool-timeout.js +18 -0
- package/dist/core/coding-mcp.d.ts +36 -0
- package/dist/core/coding-mcp.js +253 -0
- package/dist/core/coding-repository.d.ts +46 -0
- package/dist/core/coding-repository.js +92 -0
- package/dist/core/delegation-slots.d.ts +27 -0
- package/dist/core/delegation-slots.js +44 -0
- package/dist/core/dispatch.d.ts +6 -2
- package/dist/core/dispatch.js +39 -11
- package/dist/core/engine-native.d.ts +2 -0
- package/dist/core/engine-native.js +13 -0
- package/dist/core/error-code.js +2 -0
- package/dist/core/grants.d.ts +28 -3
- package/dist/core/grants.js +46 -4
- package/dist/core/host-events.js +7 -3
- package/dist/core/host-status.js +2 -2
- package/dist/core/issue-status.js +4 -3
- package/dist/core/listen-address.d.ts +10 -0
- package/dist/core/listen-address.js +14 -0
- package/dist/core/mark-ready.d.ts +1 -1
- package/dist/core/mark-ready.js +1 -1
- package/dist/core/mcp-client/client.d.ts +47 -0
- package/dist/core/mcp-client/client.js +141 -0
- package/dist/core/mcp-client/jsonrpc.d.ts +15 -0
- package/dist/core/mcp-client/jsonrpc.js +54 -0
- package/dist/core/mcp-client/results.d.ts +13 -0
- package/dist/core/mcp-client/results.js +42 -0
- package/dist/core/mcp-servers.d.ts +90 -0
- package/dist/core/mcp-servers.js +230 -0
- package/dist/core/mcp-tools.d.ts +108 -0
- package/dist/core/mcp-tools.js +287 -0
- package/dist/core/merge-order-check.js +38 -19
- package/dist/core/notification-dispatcher.js +2 -1
- package/dist/core/power-idle.d.ts +8 -2
- package/dist/core/power-idle.js +17 -3
- package/dist/core/power.d.ts +5 -0
- package/dist/core/power.js +5 -0
- package/dist/core/related-pull-requests.js +26 -10
- package/dist/core/retention-days.d.ts +5 -0
- package/dist/core/retention-days.js +14 -0
- package/dist/core/review-fix.js +2 -2
- package/dist/core/review-host-tools.js +3 -1
- package/dist/core/run-steps/config.d.ts +25 -0
- package/dist/core/run-steps/config.js +40 -0
- package/dist/core/run-steps/control-chars.d.ts +16 -0
- package/dist/core/run-steps/control-chars.js +26 -0
- package/dist/core/run-steps/detail.d.ts +32 -0
- package/dist/core/run-steps/detail.js +240 -0
- package/dist/core/run-steps/extract.d.ts +52 -0
- package/dist/core/run-steps/extract.js +467 -0
- package/dist/core/run-steps/ingress.d.ts +51 -0
- package/dist/core/run-steps/ingress.js +160 -0
- package/dist/core/run-steps/last-step.d.ts +15 -0
- package/dist/core/run-steps/last-step.js +35 -0
- package/dist/core/run-steps/native.d.ts +48 -0
- package/dist/core/run-steps/native.js +164 -0
- package/dist/core/run-steps/seen.d.ts +16 -0
- package/dist/core/run-steps/seen.js +35 -0
- package/dist/core/run-steps/summarize.d.ts +57 -0
- package/dist/core/run-steps/summarize.js +304 -0
- package/dist/core/run-steps/sweep.d.ts +21 -0
- package/dist/core/run-steps/sweep.js +41 -0
- package/dist/core/run-steps/types.d.ts +89 -0
- package/dist/core/run-steps/types.js +39 -0
- package/dist/core/run-steps/wire.d.ts +35 -0
- package/dist/core/run-steps/wire.js +47 -0
- package/dist/core/run-steps/writer.d.ts +49 -0
- package/dist/core/run-steps/writer.js +279 -0
- package/dist/core/runner.d.ts +70 -16
- package/dist/core/runner.js +296 -74
- package/dist/core/secret-broker-config.d.ts +2 -0
- package/dist/core/secret-broker-config.js +1 -1
- package/dist/core/secret-guard.d.ts +5 -2
- package/dist/core/secret-guard.js +22 -7
- package/dist/core/secrets.d.ts +11 -1
- package/dist/core/secrets.js +27 -1
- package/dist/core/serializable-retry.d.ts +18 -0
- package/dist/core/serializable-retry.js +43 -0
- package/dist/core/tool-admin.d.ts +5 -1
- package/dist/core/tool-admin.js +7 -2
- package/dist/core/tool-names.d.ts +13 -0
- package/dist/core/tool-names.js +20 -0
- package/dist/core/webhooks.d.ts +4 -0
- package/dist/core/webhooks.js +68 -49
- package/dist/core/workflow-run-events.js +2 -1
- package/dist/generated/prisma/browser.d.ts +39 -0
- package/dist/generated/prisma/client.d.ts +39 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +80 -50
- package/dist/generated/prisma/enums.d.ts +6 -0
- package/dist/generated/prisma/enums.js +5 -0
- package/dist/generated/prisma/internal/class.d.ts +77 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +632 -1
- package/dist/generated/prisma/internal/prismaNamespace.js +94 -3
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +98 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +94 -3
- package/dist/generated/prisma/models/Agent.d.ts +372 -1
- package/dist/generated/prisma/models/AgentMcpServer.d.ts +1412 -0
- package/dist/generated/prisma/models/AgentMcpServer.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +40 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +1 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +0 -7
- package/dist/generated/prisma/models/CodingProxySession.d.ts +159 -2
- package/dist/generated/prisma/models/CodingRun.d.ts +76 -1
- package/dist/generated/prisma/models/HostProvider.d.ts +2176 -0
- package/dist/generated/prisma/models/HostProvider.js +1 -0
- package/dist/generated/prisma/models/HostProviderCredential.d.ts +1285 -0
- package/dist/generated/prisma/models/HostProviderCredential.js +1 -0
- package/dist/generated/prisma/models/HostProviderOperation.d.ts +1470 -0
- package/dist/generated/prisma/models/HostProviderOperation.js +1 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.d.ts +1145 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.js +1 -0
- package/dist/generated/prisma/models/McpServer.d.ts +1500 -0
- package/dist/generated/prisma/models/McpServer.js +1 -0
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +39 -1
- package/dist/generated/prisma/models/Principal.d.ts +167 -0
- package/dist/generated/prisma/models/ResourceGrant.d.ts +1 -1
- package/dist/generated/prisma/models/Run.d.ts +503 -1
- package/dist/generated/prisma/models/RunStep.d.ts +1453 -0
- package/dist/generated/prisma/models/RunStep.js +1 -0
- package/dist/generated/prisma/models/Secret.d.ts +387 -0
- package/dist/generated/prisma/models/Tool.d.ts +223 -1
- package/dist/generated/prisma/models.d.ts +7 -0
- package/dist/help/search.js +12 -3
- package/dist/help-index.json +812 -52
- package/dist/import/create.js +16 -1
- package/dist/import/neutral-schema.d.ts +1 -0
- package/dist/import/neutral-schema.js +2 -0
- package/dist/mcp/auth/access.d.ts +1 -0
- package/dist/mcp/auth/grants-cli.d.ts +6 -4
- package/dist/mcp/auth/grants-cli.js +42 -3
- package/dist/mcp/auth/resource-server.d.ts +3 -1
- package/dist/mcp/auth/resource-server.js +17 -2
- package/dist/mcp/context.d.ts +6 -0
- package/dist/mcp/index.js +21 -0
- package/dist/mcp/tools/agents.js +135 -20
- package/dist/mcp/tools/grants.js +66 -28
- package/dist/mcp/tools/host-providers.d.ts +2 -0
- package/dist/mcp/tools/host-providers.js +215 -0
- package/dist/mcp/tools/mcp-servers.d.ts +26 -0
- package/dist/mcp/tools/mcp-servers.js +481 -0
- package/dist/mcp/tools/power.js +2 -2
- package/dist/mcp/tools/repositories.js +50 -10
- package/dist/mcp/tools/run-access.d.ts +7 -0
- package/dist/mcp/tools/run-access.js +14 -0
- package/dist/mcp/tools/run-steps.d.ts +16 -0
- package/dist/mcp/tools/run-steps.js +85 -0
- package/dist/mcp/tools/runs.js +186 -18
- package/dist/mcp/tools/secret-elicitation.d.ts +2 -0
- package/dist/mcp/tools/secret-elicitation.js +25 -2
- package/dist/mcp/tools/secrets.js +38 -4
- package/dist/mcp/tools/tools.d.ts +6 -4
- package/dist/mcp/tools/tools.js +40 -8
- package/dist/mcp/tools/trigger.js +28 -6
- package/dist/mcp/transport/http-limits.d.ts +2 -0
- package/dist/mcp/transport/http-limits.js +6 -2
- package/dist/mcp/webhooks/ingress.js +10 -0
- package/dist/native-worker/gateway-server.d.ts +18 -1
- package/dist/native-worker/gateway-server.js +38 -9
- package/dist/native-worker/gateway.d.ts +5 -0
- package/dist/native-worker/gateway.js +13 -1
- package/dist/native-worker/http-server.d.ts +6 -4
- package/dist/native-worker/http-server.js +53 -8
- package/dist/native-worker/ledger.d.ts +6 -2
- package/dist/native-worker/ledger.js +5 -4
- package/dist/native-worker/protocol.d.ts +1 -0
- package/dist/native-worker/protocol.js +4 -1
- package/dist/providers/coding-proxy/capability-hash.d.ts +2 -0
- package/dist/providers/coding-proxy/capability-hash.js +5 -0
- package/dist/providers/coding-proxy/mcp-route.d.ts +34 -0
- package/dist/providers/coding-proxy/mcp-route.js +166 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +2 -0
- package/dist/providers/coding-proxy/memory-ledger.js +16 -0
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +1 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +36 -2
- package/dist/providers/coding-proxy/proxy.d.ts +33 -1
- package/dist/providers/coding-proxy/proxy.js +266 -75
- package/dist/providers/coding-proxy/run-step-forwarder.d.ts +42 -0
- package/dist/providers/coding-proxy/run-step-forwarder.js +206 -0
- package/dist/providers/coding-proxy/runtime.d.ts +13 -0
- package/dist/providers/coding-proxy/runtime.js +87 -4
- package/dist/providers/coding-proxy/server.d.ts +3 -0
- package/dist/providers/coding-proxy/server.js +10 -0
- package/dist/providers/coding-proxy/types.d.ts +18 -0
- package/dist/providers/engine/types.d.ts +26 -0
- package/dist/providers/executor/composition.js +5 -0
- package/dist/providers/executor/container.d.ts +47 -1
- package/dist/providers/executor/container.js +84 -8
- package/dist/providers/host-tools/invoke.d.ts +37 -0
- package/dist/providers/host-tools/invoke.js +117 -0
- package/dist/providers/host-tools/operations.d.ts +65 -0
- package/dist/providers/host-tools/operations.js +149 -0
- package/dist/providers/host-tools/registry.d.ts +79 -0
- package/dist/providers/host-tools/registry.js +362 -0
- package/dist/providers/host-tools/request-guard.d.ts +17 -0
- package/dist/providers/host-tools/request-guard.js +99 -0
- package/dist/providers/host-tools/self-test.d.ts +64 -0
- package/dist/providers/host-tools/self-test.js +142 -0
- package/dist/providers/jobs/docker-isolation.d.ts +2 -2
- package/dist/providers/jobs/docker-isolation.js +30 -8
- package/dist/providers/jobs/docker.d.ts +8 -0
- package/dist/providers/jobs/docker.js +33 -9
- package/dist/providers/jobs/kubernetes-isolation.d.ts +3 -1
- package/dist/providers/jobs/kubernetes-isolation.js +15 -2
- package/dist/providers/jobs/kubernetes.d.ts +13 -0
- package/dist/providers/jobs/kubernetes.js +28 -5
- package/dist/providers/jobs/types.d.ts +6 -0
- package/dist/providers/review-host/types.d.ts +6 -4
- package/dist/providers/review-host/types.js +1 -1
- package/dist/providers/vcs/git.d.ts +6 -2
- package/dist/providers/vcs/git.js +15 -4
- package/dist/providers/vcs/github.d.ts +2 -71
- package/dist/providers/vcs/github.js +8 -11
- package/dist/providers/vcs/index.d.ts +1 -1
- package/dist/providers/vcs/index.js +1 -1
- package/dist/providers/vcs/remote.d.ts +4 -0
- package/dist/providers/vcs/remote.js +7 -1
- package/dist/providers/vcs/routing.d.ts +12 -6
- package/dist/providers/vcs/routing.js +21 -6
- package/dist/providers/vcs/types.d.ts +71 -3
- package/dist/quickstart/index.d.ts +5 -0
- package/dist/quickstart/index.js +21 -0
- package/dist/quickstart-images.json +1 -1
- package/dist/sandbox/eval-core.d.ts +12 -2
- package/dist/sandbox/eval-core.js +20 -4
- package/dist/sandbox/host-functions.d.ts +4 -0
- package/dist/sandbox/host-functions.js +7 -2
- package/dist/sandbox/limits.d.ts +4 -1
- package/dist/sandbox/limits.js +4 -1
- package/dist/sandbox/run-in-sandbox.d.ts +6 -0
- package/dist/sandbox/run-in-sandbox.js +14 -2
- package/dist/sandbox/safe-fetch.d.ts +2 -0
- package/dist/sandbox/safe-fetch.js +1 -1
- package/dist/sandbox/secret-broker.d.ts +1 -0
- package/dist/sandbox/secret-broker.js +1 -1
- package/dist/sandbox/tool-timeout.d.ts +32 -0
- package/dist/sandbox/tool-timeout.js +52 -0
- package/dist/sandbox/user-tool.d.ts +8 -1
- package/dist/sandbox/user-tool.js +9 -3
- package/dist/serve.js +26 -18
- package/dist/viewer/api-schema.d.ts +66 -0
- package/dist/viewer/api-schema.js +31 -0
- package/dist/viewer/graph.js +2 -1
- package/dist/viewer/openapi.js +2 -1
- package/dist/viewer/run-detail.d.ts +2 -0
- package/dist/viewer/run-detail.js +26 -1
- package/dist/wardby-bin.js +5 -0
- package/docs/README.md +11 -1
- package/docs/agent-recipes.md +37 -4
- package/docs/architecture-runtime.md +1 -1
- package/docs/claude-code-plugin.md +208 -0
- package/docs/code-review-agents.md +6 -0
- package/docs/coding-agent-setup.md +94 -1
- package/docs/coding-worker-byo-images.md +4 -2
- package/docs/coding-worker-isolation.md +94 -7
- package/docs/debugging-agent-runs.md +354 -0
- package/docs/getting-started-gke.md +33 -11
- package/docs/getting-started-identity-provider.md +43 -37
- package/docs/getting-started.md +8 -0
- package/docs/host-providers.md +294 -0
- package/docs/mcp-servers.md +263 -0
- package/docs/native-sandbox.md +31 -9
- package/docs/security-deployment.md +161 -31
- package/docs/tool-secrets.md +3 -0
- package/docs/viewer-api.md +27 -12
- package/docs/viewer-api.openapi.yml +78 -2
- package/help/admin-viewer.md +5 -0
- package/help/agent-recipes.md +33 -2
- package/help/brokered-secrets.md +3 -0
- package/help/build-claude-code-image.md +1 -1
- package/help/claude-code-run-pane.md +63 -0
- package/help/deploy-gke.md +5 -0
- package/help/errors/coding-mcp-gateway-unavailable.md +46 -0
- package/help/errors/coding-mcp-pins-unavailable.md +35 -0
- package/help/errors/database-auth-invalid.md +1 -1
- package/help/errors/host-provider-error.md +40 -0
- package/help/errors/host-tool-failed.md +36 -0
- package/help/errors/mcp-call-limit.md +22 -0
- package/help/errors/mcp-call-timeout.md +22 -0
- package/help/errors/mcp-server-auth-failed.md +22 -0
- package/help/errors/mcp-server-credentials-unavailable.md +45 -0
- package/help/errors/mcp-server-error.md +28 -0
- package/help/errors/mcp-server-unreachable.md +22 -0
- package/help/errors/mcp-servers-off.md +21 -0
- package/help/errors/mcp-tool-not-allowed.md +26 -0
- package/help/errors/mcp-tool-removed.md +22 -0
- package/help/errors/mcp-unavailable.md +25 -0
- package/help/errors/native-sandbox-capacity.md +1 -1
- package/help/errors/native-sandbox-deadline-exceeded.md +1 -1
- package/help/errors/native-sandbox-docker-failed.md +1 -1
- package/help/errors/native-sandbox-gateway-error.md +1 -1
- package/help/errors/native-sandbox-gateway-unavailable.md +1 -1
- package/help/errors/native-sandbox-image-not-pinned.md +1 -1
- package/help/errors/native-sandbox-isolation-mismatch.md +1 -1
- package/help/errors/native-sandbox-network-unenforced.md +1 -1
- package/help/errors/native-sandbox-requires-catalog.md +1 -1
- package/help/errors/native-sandbox-unavailable.md +1 -1
- package/help/errors/native-sandbox-warm-delivery-failed.md +1 -1
- package/help/errors/native-sandbox-worker-exited.md +1 -1
- package/help/errors/native-sandbox-worker-lost.md +1 -1
- package/help/errors/native-sandbox-worker-unready.md +1 -1
- package/help/errors/repository-not-listed.md +36 -0
- package/help/errors/repository-required.md +36 -0
- package/help/errors/tool-timeout.md +38 -0
- package/help/host-providers.md +40 -0
- package/help/identity-and-access.md +4 -1
- package/help/low-power-mode.md +19 -8
- package/help/mcp-servers.md +73 -0
- package/help/mcp.md +9 -2
- package/help/native-capabilities.md +27 -1
- package/help/native-sandbox.md +1 -1
- package/help/operating-agents.md +16 -0
- package/help/related-pull-requests.md +7 -3
- package/help/repo-instructions-and-skills.md +3 -1
- package/help/resume-runs-locally.md +7 -0
- package/help/run-steps.md +140 -0
- package/help/stop-run.md +1 -1
- package/help/troubleshooting/coding-workers.md +9 -1
- package/help/troubleshooting/debugging-agent-runs.md +29 -0
- package/package.json +2 -1
- package/prisma/migrations/20261102000000_host_providers/migration.sql +56 -0
- package/prisma/migrations/20261104000000_coding_agent_repository_list/migration.sql +5 -0
- package/prisma/migrations/20261106000000_tool_timeout/migration.sql +4 -0
- package/prisma/migrations/20261107000000_mcp_servers/migration.sql +33 -0
- package/prisma/migrations/20261108000000_coding_mcp/migration.sql +5 -0
- package/prisma/migrations/20261109000000_task_run_index/migration.sql +3 -0
- package/prisma/migrations/20261110000000_run_steps/migration.sql +26 -0
- package/prisma/migrations/20261111000000_run_steps_viewer_notify/migration.sql +59 -0
- package/prisma/migrations/20261112000000_run_steps_detail/migration.sql +9 -0
- package/prisma/schema.prisma +211 -14
- package/scripts/git-askpass.sh +1 -1
|
@@ -10,12 +10,13 @@ import { simulatorGitEnvironment } from "../../config/test-simulators.js";
|
|
|
10
10
|
import { globRegex } from "../../core/glob.js";
|
|
11
11
|
import { redactExactSecretValues } from "../../core/secret-spans.js";
|
|
12
12
|
import { MAX_REDACTED_SPAN, redactTokenShapedValues, normalizeGitRef } from "../../coding/protocol.js";
|
|
13
|
-
import {
|
|
13
|
+
import { isSafeGitAuthToken } from "./remote.js";
|
|
14
14
|
export const DEFAULT_MAX_CHANGED_FILES = 1_000;
|
|
15
15
|
export const DEFAULT_MAX_DIFF_BYTES = 1024 * 1024;
|
|
16
16
|
const MAX_GIT_OUTPUT_BYTES = 2 * 1024 * 1024;
|
|
17
17
|
const MAX_WORKSPACE_ENTRIES = 100_000;
|
|
18
18
|
const SAFE_COMMIT_SHA = /^[0-9a-f]{40}$/;
|
|
19
|
+
const SAFE_GIT_USERNAME = /^[A-Za-z0-9._-]{1,64}$/;
|
|
19
20
|
const SAFE_RUN_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
|
|
20
21
|
/** Where a continuation's target branch is fetched to (never under refs/remotes, so baseCommit's ref is untouched). */
|
|
21
22
|
const MERGE_TARGET_REF = "refs/wardby/merge-target";
|
|
@@ -82,9 +83,12 @@ export class NodeGitCommandRunner {
|
|
|
82
83
|
options.askPassPath ?? fileURLToPath(new URL("../../../scripts/git-askpass.sh", import.meta.url));
|
|
83
84
|
}
|
|
84
85
|
async run(args, options = {}) {
|
|
85
|
-
if (options.authToken !== undefined && !
|
|
86
|
+
if (options.authToken !== undefined && !isSafeGitAuthToken(options.authToken)) {
|
|
86
87
|
throw new Error("git_auth_token_invalid");
|
|
87
88
|
}
|
|
89
|
+
if (options.authUsername !== undefined && !SAFE_GIT_USERNAME.test(options.authUsername)) {
|
|
90
|
+
throw new Error("git_auth_username_invalid");
|
|
91
|
+
}
|
|
88
92
|
const env = {
|
|
89
93
|
PATH: this.path,
|
|
90
94
|
HOME: this.options.homeDir,
|
|
@@ -97,6 +101,7 @@ export class NodeGitCommandRunner {
|
|
|
97
101
|
GIT_TERMINAL_PROMPT: "0",
|
|
98
102
|
GCM_INTERACTIVE: "never",
|
|
99
103
|
GIT_ASKPASS: options.authToken ? this.askPassPath : "/bin/false",
|
|
104
|
+
...(options.authToken && options.authUsername ? { WARDBY_GIT_USERNAME: options.authUsername } : {}),
|
|
100
105
|
SSH_ASKPASS: "/bin/false",
|
|
101
106
|
GIT_LFS_SKIP_SMUDGE: "1",
|
|
102
107
|
// WARDBY_TEST_SIMULATORS (test only): the simulator's throwaway CA; empty otherwise.
|
|
@@ -293,7 +298,7 @@ export class GitVcsProvider {
|
|
|
293
298
|
let mergeFetched = false;
|
|
294
299
|
try {
|
|
295
300
|
await this.remote.withAccess(normalized.repository, async (token) => {
|
|
296
|
-
const auth = token
|
|
301
|
+
const auth = this.gitAuth(token);
|
|
297
302
|
if (this.remote.refNotFoundError) {
|
|
298
303
|
const listed = await this.git.run([
|
|
299
304
|
...HARDENED_GIT_CONFIG,
|
|
@@ -937,10 +942,16 @@ export class GitVcsProvider {
|
|
|
937
942
|
throw new Error("vcs_git_config_unsafe");
|
|
938
943
|
}
|
|
939
944
|
}
|
|
945
|
+
/** The git credentials for a remote's access token; none when the remote needs no token. */
|
|
946
|
+
gitAuth(token) {
|
|
947
|
+
if (token === undefined)
|
|
948
|
+
return {};
|
|
949
|
+
return { authToken: token, ...(this.remote.gitUsername ? { authUsername: this.remote.gitUsername } : {}) };
|
|
950
|
+
}
|
|
940
951
|
async pushOnce(workspace, commitSha) {
|
|
941
952
|
this.remote.assertPushRef?.(workspace.headRef);
|
|
942
953
|
await this.remote.withAccess(workspace.repository, async (token) => {
|
|
943
|
-
const auth = token
|
|
954
|
+
const auth = this.gitAuth(token);
|
|
944
955
|
const remote = await this.remoteHead(workspace, auth);
|
|
945
956
|
if (remote === commitSha)
|
|
946
957
|
return;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type PackageAuditReport } from "../../coding/registry/audit-mode.js";
|
|
2
2
|
import { RELATED_SECTION_END, RELATED_SECTION_START } from "./pr-description.js";
|
|
3
|
+
import type { DescriptionBaseline, DescriptionUpdateResult, RelatedPullRequestEntry, RelatedPullRequestState, RelatedPullRequestsInput, RepositoryFileInput } from "./types.js";
|
|
4
|
+
export type { DescriptionBaseline, DescriptionUpdateResult, RelatedPullRequestEntry, RelatedPullRequestState, RelatedPullRequestsInput, RepositoryFileInput, };
|
|
3
5
|
export { RELATED_SECTION_END, RELATED_SECTION_START };
|
|
4
6
|
export interface GitHubAppConfig {
|
|
5
7
|
appId: string;
|
|
@@ -106,22 +108,6 @@ export interface PullRequestResult {
|
|
|
106
108
|
*/
|
|
107
109
|
descriptionSha256?: string;
|
|
108
110
|
}
|
|
109
|
-
/** What wardby last wrote into a pull request description (control-plane records). */
|
|
110
|
-
export interface DescriptionBaseline {
|
|
111
|
-
/** Null = legacy pull request (no recorded write). */
|
|
112
|
-
recordedSha256: string | null;
|
|
113
|
-
/**
|
|
114
|
-
* Legacy pull requests only: the hash of the body wardby would have written
|
|
115
|
-
* for the root run, rebuilt from stored records. A current body with this
|
|
116
|
-
* hash is unedited. Absent when the records can't rebuild it (then the
|
|
117
|
-
* description counts as edited).
|
|
118
|
-
*/
|
|
119
|
-
legacySha256?: string;
|
|
120
|
-
/** Stored summary of the run whose text the description carries (root run for legacy). */
|
|
121
|
-
previousAgentText?: string;
|
|
122
|
-
/** That run's id. */
|
|
123
|
-
agentTextRunId: string;
|
|
124
|
-
}
|
|
125
111
|
/**
|
|
126
112
|
* A fix round's description update. `runId` is this (latest) run; the pull
|
|
127
113
|
* request is re-found by `rootRunId`'s marker. `related` is ignored: the
|
|
@@ -138,23 +124,6 @@ export interface UpdateDescriptionInput extends Omit<PullRequestInput, "runId" |
|
|
|
138
124
|
*/
|
|
139
125
|
headSha?: string;
|
|
140
126
|
}
|
|
141
|
-
/** `agentTextRunId` is the run whose summary the description now carries. */
|
|
142
|
-
export type DescriptionUpdateResult = {
|
|
143
|
-
kind: "replaced" | "unchanged";
|
|
144
|
-
sha256: string;
|
|
145
|
-
agentTextRunId: string;
|
|
146
|
-
} | {
|
|
147
|
-
kind: "refreshed";
|
|
148
|
-
reason: "empty" | "too_short" | "missing_closing_keyword";
|
|
149
|
-
sha256: string;
|
|
150
|
-
agentTextRunId: string;
|
|
151
|
-
} | {
|
|
152
|
-
kind: "edited_by_hand";
|
|
153
|
-
commentPosted: boolean;
|
|
154
|
-
} | {
|
|
155
|
-
kind: "skipped";
|
|
156
|
-
reason: "not_open" | "too_long" | "error";
|
|
157
|
-
};
|
|
158
127
|
/** See VcsProvider.notifyContinuationStarted/Finished (types.ts) — these back that GitHub-specific mechanism. */
|
|
159
128
|
export interface ContinuationStatusCommentInput {
|
|
160
129
|
/** This continuation round's own run id — the comment's identity/marker key. */
|
|
@@ -185,15 +154,6 @@ export interface ContinuationCheckRunCompleteInput {
|
|
|
185
154
|
*/
|
|
186
155
|
headRef?: string;
|
|
187
156
|
}
|
|
188
|
-
/** One file at one ref, read through the API without a clone. */
|
|
189
|
-
export interface RepositoryFileInput {
|
|
190
|
-
repository: string;
|
|
191
|
-
ref: string;
|
|
192
|
-
/** Repository-relative POSIX path. */
|
|
193
|
-
path: string;
|
|
194
|
-
/** Refused above this many bytes. */
|
|
195
|
-
maxBytes: number;
|
|
196
|
-
}
|
|
197
157
|
export interface GitHubRepositoryAccess {
|
|
198
158
|
withRepositoryToken<T>(repository: string, action: (token: string) => Promise<T>): Promise<T>;
|
|
199
159
|
createOrFindDraftPullRequest(input: PullRequestInput): Promise<PullRequestResult>;
|
|
@@ -264,8 +224,6 @@ export interface GitHubRepositoryAccess {
|
|
|
264
224
|
readFileAtRef?(input: RepositoryFileInput): Promise<string | null>;
|
|
265
225
|
}
|
|
266
226
|
type Fetch = typeof globalThis.fetch;
|
|
267
|
-
/** Accepts bounded opaque installation tokens without allowing control characters. */
|
|
268
|
-
export declare function isSafeGitHubInstallationToken(value: unknown): value is string;
|
|
269
227
|
/**
|
|
270
228
|
* Same shape as ISSUE_KEY (issue-tracker/types.ts); duplicated so the VCS layer
|
|
271
229
|
* stays free of issue-tracker imports (a test keeps the two in step). The
|
|
@@ -279,33 +237,6 @@ export declare function pullRequestTitle(input: PullRequestInput): string;
|
|
|
279
237
|
export declare const MAX_RELATED_PULL_REQUESTS = 20;
|
|
280
238
|
/** GitHub rejects a pull request body longer than this. */
|
|
281
239
|
export declare const MAX_PULL_REQUEST_BODY_CHARS = 65536;
|
|
282
|
-
export type RelatedPullRequestState = "open" | "draft" | "merged" | "closed";
|
|
283
|
-
/** One pull request of the request's set, from stored control-plane rows only. */
|
|
284
|
-
export interface RelatedPullRequestEntry {
|
|
285
|
-
repository: string;
|
|
286
|
-
/** Absent only for `self` while the pull request is being created. */
|
|
287
|
-
number?: number;
|
|
288
|
-
/** Live state; absent when unknown (at creation). */
|
|
289
|
-
state?: RelatedPullRequestState;
|
|
290
|
-
/** The pull request whose description this section is in. */
|
|
291
|
-
self?: boolean;
|
|
292
|
-
/**
|
|
293
|
-
* Step in the delegating agent's declared merge order (1-99), from
|
|
294
|
-
* CodingRun.mergeOrder; absent = the delegating agent set no order
|
|
295
|
-
* (an open entry is listed under "Not ordered:" when any other entry,
|
|
296
|
-
* open or done, has one).
|
|
297
|
-
*/
|
|
298
|
-
mergeOrder?: number;
|
|
299
|
-
}
|
|
300
|
-
export interface RelatedPullRequestsInput {
|
|
301
|
-
/** In merge order: by mergeOrder when set, else the order the request's coding runs were dispatched. */
|
|
302
|
-
entries: readonly RelatedPullRequestEntry[];
|
|
303
|
-
issue?: {
|
|
304
|
-
key: string;
|
|
305
|
-
url?: string;
|
|
306
|
-
trackerName?: string;
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
240
|
/**
|
|
310
241
|
* The "Related pull requests" block, markers included, built only from
|
|
311
242
|
* stored rows (never agent text). Open (and draft, and not-yet-known) pull
|
|
@@ -2,8 +2,10 @@ import { createPrivateKey } from "node:crypto";
|
|
|
2
2
|
import { importPKCS8, SignJWT } from "jose";
|
|
3
3
|
import { isGitHubLogin } from "../../config/github-allowed-accounts.js";
|
|
4
4
|
import { testSimulatorBase } from "../../config/test-simulators.js";
|
|
5
|
+
import { codeProviderOf, normalizeHostedRepository, pullRequestWebUrl } from "../../coding/code-host.js";
|
|
5
6
|
import { ACCEPTED_ADVISORY_ID } from "../../coding/registry/allowlist.js";
|
|
6
7
|
import { packageAuditRows, packageAuditWording, summarizePackageAudit, } from "../../coding/registry/audit-mode.js";
|
|
8
|
+
import { isSafeGitAuthToken } from "./remote.js";
|
|
7
9
|
import { refusalMeaning } from "../../coding/registry/refusal-hints.js";
|
|
8
10
|
import { DEPENDENCY_CACHE_PATH, normalizeGitHubRepository, normalizeGitRef, truncateText, } from "../../coding/protocol.js";
|
|
9
11
|
import { LATEST_RUN_MARKER_PREFIX, RELATED_SECTION_END, RELATED_SECTION_START, assessFinalMessage, descriptionAppliedComment, descriptionFallbackComment, descriptionSha256, extractRelatedBlock, findDescriptionComments, hasClosingKeyword, isUneditedDescription, supersededDescriptionComment, } from "./pr-description.js";
|
|
@@ -33,13 +35,6 @@ function safeApiError(response) {
|
|
|
33
35
|
const requestId = response.headers.get("x-github-request-id");
|
|
34
36
|
return new Error(`github_api_error:${response.status}${requestId ? `:${requestId}` : ""}`);
|
|
35
37
|
}
|
|
36
|
-
/** Accepts bounded opaque installation tokens without allowing control characters. */
|
|
37
|
-
export function isSafeGitHubInstallationToken(value) {
|
|
38
|
-
return (typeof value === "string" &&
|
|
39
|
-
value.length >= 20 &&
|
|
40
|
-
Buffer.byteLength(value, "utf8") <= 512 &&
|
|
41
|
-
/^[A-Za-z0-9._-]+$/.test(value));
|
|
42
|
-
}
|
|
43
38
|
/**
|
|
44
39
|
* Same shape as ISSUE_KEY (issue-tracker/types.ts); duplicated so the VCS layer
|
|
45
40
|
* stays free of issue-tracker imports (a test keeps the two in step). The
|
|
@@ -109,10 +104,10 @@ export const MAX_RELATED_PULL_REQUESTS = 20;
|
|
|
109
104
|
/** GitHub rejects a pull request body longer than this. */
|
|
110
105
|
export const MAX_PULL_REQUEST_BODY_CHARS = 65_536;
|
|
111
106
|
const RELATED_STATES = new Set(["open", "draft", "merged", "closed"]);
|
|
112
|
-
/** The repository normalized, or undefined when it isn't a
|
|
107
|
+
/** The repository normalized, or undefined when it isn't a hosted repository id safe inside a link. */
|
|
113
108
|
function safeRepository(value) {
|
|
114
109
|
try {
|
|
115
|
-
const repository =
|
|
110
|
+
const repository = normalizeHostedRepository(value);
|
|
116
111
|
return isSafeMarkdownFragment(repository) && !/[[\]()<>\s]/.test(repository) ? repository : undefined;
|
|
117
112
|
}
|
|
118
113
|
catch {
|
|
@@ -129,7 +124,9 @@ function relatedLine(entry, prefix) {
|
|
|
129
124
|
return `${prefix} **This pull request**${state}`;
|
|
130
125
|
if (!Number.isSafeInteger(entry.number) || (entry.number ?? 0) <= 0)
|
|
131
126
|
return undefined;
|
|
132
|
-
|
|
127
|
+
const label = `${repository}#${entry.number}`;
|
|
128
|
+
const url = pullRequestWebUrl(codeProviderOf(repository), repository, entry.number);
|
|
129
|
+
return `${prefix} ${url ? `[${label}](${url})` : label}${state}`;
|
|
133
130
|
}
|
|
134
131
|
const isDone = (entry) => entry.state === "merged" || entry.state === "closed";
|
|
135
132
|
/**
|
|
@@ -1103,7 +1100,7 @@ export class GitHubAppClient {
|
|
|
1103
1100
|
const expiresAt = typeof payload.expires_at === "string" ? Date.parse(payload.expires_at) : Number.NaN;
|
|
1104
1101
|
const unexpectedPermission = Object.entries(grantedPermissions).some(([permissionName, level]) => !isAllowedPermission(permissionName, level));
|
|
1105
1102
|
const requestedGranted = Object.entries(permissions).every(([permissionName, level]) => grantedPermissions[permissionName] === level);
|
|
1106
|
-
if (!
|
|
1103
|
+
if (!isSafeGitAuthToken(payload.token) ||
|
|
1107
1104
|
!Number.isFinite(expiresAt) ||
|
|
1108
1105
|
expiresAt <= this.now().getTime() + 60_000 ||
|
|
1109
1106
|
!requestedGranted ||
|
|
@@ -5,7 +5,7 @@ export { GitHubAppClient } from "./github.js";
|
|
|
5
5
|
export { GitVcsProvider, NodeGitCommandRunner } from "./git.js";
|
|
6
6
|
export { GitHubRemote } from "./github-remote.js";
|
|
7
7
|
export { LocalRemote } from "./local-remote.js";
|
|
8
|
-
export { RoutingVcsProvider, VCS_GITHUB_NOT_CONFIGURED_ERROR } from "./routing.js";
|
|
8
|
+
export { RoutingVcsProvider, VCS_GITHUB_NOT_CONFIGURED_ERROR, VCS_HOST_PROVIDER_NOT_CONFIGURED_ERROR, } from "./routing.js";
|
|
9
9
|
export type { GitRemote, PublishInput, PublishResult } from "./remote.js";
|
|
10
10
|
export interface BuildVcsProviderOptions {
|
|
11
11
|
/** Startup warnings; the module logger by default. */
|
|
@@ -14,7 +14,7 @@ export { GitHubAppClient } from "./github.js";
|
|
|
14
14
|
export { GitVcsProvider, NodeGitCommandRunner } from "./git.js";
|
|
15
15
|
export { GitHubRemote } from "./github-remote.js";
|
|
16
16
|
export { LocalRemote } from "./local-remote.js";
|
|
17
|
-
export { RoutingVcsProvider, VCS_GITHUB_NOT_CONFIGURED_ERROR } from "./routing.js";
|
|
17
|
+
export { RoutingVcsProvider, VCS_GITHUB_NOT_CONFIGURED_ERROR, VCS_HOST_PROVIDER_NOT_CONFIGURED_ERROR, } from "./routing.js";
|
|
18
18
|
/**
|
|
19
19
|
* GitHub is built only with App credentials, local repositories only with
|
|
20
20
|
* LOCAL_REPO_ROOTS; neither being set is allowed (the server still starts) and
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { DescriptionWriteOutcome, FinalizeChangesDetails, PreparedWorkspace, VcsProvider } from "./types.js";
|
|
2
|
+
/** Accepts bounded opaque git auth tokens without allowing control characters. */
|
|
3
|
+
export declare function isSafeGitAuthToken(value: unknown): value is string;
|
|
2
4
|
/** What a remote needs to publish a pushed commit (GitHub: open or find the draft PR). */
|
|
3
5
|
export interface PublishInput {
|
|
4
6
|
/** The validated workspace whose headRef was just pushed. */
|
|
@@ -25,6 +27,8 @@ export type PublishResult = {
|
|
|
25
27
|
* fetch-compare-push) stays in GitVcsProvider and applies to every remote.
|
|
26
28
|
*/
|
|
27
29
|
export interface GitRemote {
|
|
30
|
+
/** The username git sends with the token (GitHub: unset, git-askpass.sh's default). */
|
|
31
|
+
readonly gitUsername?: string;
|
|
28
32
|
/** Extra `-c` pairs appended after HARDENED_GIT_CONFIG for clone/push/ls-remote (GitHub: none). */
|
|
29
33
|
readonly gitConfig: readonly string[];
|
|
30
34
|
/** Syntactic normalization of a repository id for this remote (GitHub: owner/name; local: local:/abs/path). */
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
/** Accepts bounded opaque git auth tokens without allowing control characters. */
|
|
2
|
+
export function isSafeGitAuthToken(value) {
|
|
3
|
+
return (typeof value === "string" &&
|
|
4
|
+
value.length >= 20 &&
|
|
5
|
+
Buffer.byteLength(value, "utf8") <= 512 &&
|
|
6
|
+
/^[A-Za-z0-9._-]+$/.test(value));
|
|
7
|
+
}
|
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
import type { RepositoryFileInput } from "./
|
|
1
|
+
import type { RepositoryFileInput } from "./types.js";
|
|
2
2
|
import type { ContinuationFinishedDetails, ContinuationOutcome, ContinuationTarget, DescriptionWriteOutcome, FinalizeChangesDetails, FinalizeChangesResult, PreparedWorkspace, VcsPrepareInput, VcsProvider } from "./types.js";
|
|
3
3
|
/** Keeps the `vcs_github_not_configured` code prefix that error matching and categorization key on. */
|
|
4
4
|
export declare const VCS_GITHUB_NOT_CONFIGURED_ERROR = "vcs_github_not_configured: set GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY to run coding agents on GitHub repositories";
|
|
5
|
+
/** Keeps a stable code prefix for error matching. The provider name comes from a validated repository id. */
|
|
6
|
+
export declare const VCS_HOST_PROVIDER_NOT_CONFIGURED_ERROR = "vcs_host_provider_not_configured";
|
|
5
7
|
export interface RoutingVcsOptions {
|
|
6
8
|
/** Null when no GitHub App is configured. */
|
|
7
9
|
github: VcsProvider | null;
|
|
8
10
|
/** Null when no local repository roots are configured. */
|
|
9
11
|
local: VcsProvider | null;
|
|
12
|
+
/** Registered host providers, by name. */
|
|
13
|
+
hosted?: ReadonlyMap<string, VcsProvider>;
|
|
10
14
|
}
|
|
11
15
|
/**
|
|
12
|
-
* One VcsProvider over
|
|
13
|
-
* local provider,
|
|
14
|
-
*
|
|
15
|
-
* only
|
|
16
|
-
*
|
|
16
|
+
* One VcsProvider over every repository kind: `local:/abs/path` goes to the
|
|
17
|
+
* local provider, `<provider>:<workspace>/<repo>` to the registered host
|
|
18
|
+
* provider of that name, and everything else to GitHub. A kind that is not
|
|
19
|
+
* configured fails only when a repository of that kind is actually used, so a
|
|
20
|
+
* server with only some of them still starts. A hosted repository never falls
|
|
21
|
+
* back to GitHub. The repository name is not re-resolved here: the local
|
|
22
|
+
* provider verifies it against the trusted roots.
|
|
17
23
|
*/
|
|
18
24
|
export declare class RoutingVcsProvider implements VcsProvider {
|
|
19
25
|
private readonly options;
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import { codeProviderOf } from "../../coding/code-host.js";
|
|
1
2
|
import { LocalRepoError, isLocalRepository } from "../../coding/local-repo.js";
|
|
2
3
|
/** Keeps the `vcs_github_not_configured` code prefix that error matching and categorization key on. */
|
|
3
4
|
export const VCS_GITHUB_NOT_CONFIGURED_ERROR = "vcs_github_not_configured: set GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY to run coding agents on GitHub repositories";
|
|
5
|
+
/** Keeps a stable code prefix for error matching. The provider name comes from a validated repository id. */
|
|
6
|
+
export const VCS_HOST_PROVIDER_NOT_CONFIGURED_ERROR = "vcs_host_provider_not_configured";
|
|
4
7
|
/**
|
|
5
|
-
* One VcsProvider over
|
|
6
|
-
* local provider,
|
|
7
|
-
*
|
|
8
|
-
* only
|
|
9
|
-
*
|
|
8
|
+
* One VcsProvider over every repository kind: `local:/abs/path` goes to the
|
|
9
|
+
* local provider, `<provider>:<workspace>/<repo>` to the registered host
|
|
10
|
+
* provider of that name, and everything else to GitHub. A kind that is not
|
|
11
|
+
* configured fails only when a repository of that kind is actually used, so a
|
|
12
|
+
* server with only some of them still starts. A hosted repository never falls
|
|
13
|
+
* back to GitHub. The repository name is not re-resolved here: the local
|
|
14
|
+
* provider verifies it against the trusted roots.
|
|
10
15
|
*/
|
|
11
16
|
export class RoutingVcsProvider {
|
|
12
17
|
options;
|
|
@@ -20,13 +25,23 @@ export class RoutingVcsProvider {
|
|
|
20
25
|
}
|
|
21
26
|
return this.options.local;
|
|
22
27
|
}
|
|
28
|
+
const provider = codeProviderOf(repository);
|
|
29
|
+
if (provider !== "github") {
|
|
30
|
+
const hosted = this.options.hosted?.get(provider);
|
|
31
|
+
if (!hosted)
|
|
32
|
+
throw new Error(`${VCS_HOST_PROVIDER_NOT_CONFIGURED_ERROR}: no host provider named ${provider}`);
|
|
33
|
+
return hosted;
|
|
34
|
+
}
|
|
23
35
|
if (!this.options.github)
|
|
24
36
|
throw new Error(VCS_GITHUB_NOT_CONFIGURED_ERROR);
|
|
25
37
|
return this.options.github;
|
|
26
38
|
}
|
|
27
39
|
/** The target when configured, else null (best-effort calls that must never throw). */
|
|
28
40
|
optionalTarget(repository) {
|
|
29
|
-
|
|
41
|
+
if (isLocalRepository(repository))
|
|
42
|
+
return this.options.local ?? null;
|
|
43
|
+
const provider = codeProviderOf(repository);
|
|
44
|
+
return (provider === "github" ? this.options.github : this.options.hosted?.get(provider)) ?? null;
|
|
30
45
|
}
|
|
31
46
|
async prepareWorkspace(input) {
|
|
32
47
|
return this.target(input.repository).prepareWorkspace(input);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { PackageAuditReport } from "../../coding/registry/audit-mode.js";
|
|
2
|
-
import type { DescriptionBaseline, DescriptionUpdateResult, RelatedPullRequestsInput, RepositoryFileInput } from "./github.js";
|
|
3
2
|
/** What happened to a pull request description: wardby just wrote it (a new PR) or updated a fix round's. */
|
|
4
3
|
export type DescriptionWriteOutcome = {
|
|
5
4
|
kind: "written";
|
|
@@ -168,8 +167,8 @@ export interface VcsProvider {
|
|
|
168
167
|
* Best-effort "wardby is working on this" signal for a continuation
|
|
169
168
|
* a no-op when `workspace.continuation` is unset, since a fresh run has
|
|
170
169
|
* no PR to attach anything to until its one commit lands. Optional
|
|
171
|
-
* because
|
|
172
|
-
* a
|
|
170
|
+
* because not every host has this signal (GitHub does; others may too) --
|
|
171
|
+
* a provider (or a test double) can simply omit it,
|
|
173
172
|
* mirroring `Executor.resolveCodingWorkerImage?` (dispatch.ts calls it
|
|
174
173
|
* with `?.()`). Implementations MUST NOT throw: this is strictly
|
|
175
174
|
* observability, never allowed to affect the real coding run.
|
|
@@ -248,3 +247,72 @@ export interface ContinuationFinishedDetails {
|
|
|
248
247
|
pullRequestNumber?: number;
|
|
249
248
|
};
|
|
250
249
|
}
|
|
250
|
+
/** What wardby last wrote into a pull request description (control-plane records). */
|
|
251
|
+
export interface DescriptionBaseline {
|
|
252
|
+
/** Null = legacy pull request (no recorded write). */
|
|
253
|
+
recordedSha256: string | null;
|
|
254
|
+
/**
|
|
255
|
+
* Legacy pull requests only: the hash of the body wardby would have written
|
|
256
|
+
* for the root run, rebuilt from stored records. A current body with this
|
|
257
|
+
* hash is unedited. Absent when the records can't rebuild it (then the
|
|
258
|
+
* description counts as edited).
|
|
259
|
+
*/
|
|
260
|
+
legacySha256?: string;
|
|
261
|
+
/** Stored summary of the run whose text the description carries (root run for legacy). */
|
|
262
|
+
previousAgentText?: string;
|
|
263
|
+
/** That run's id. */
|
|
264
|
+
agentTextRunId: string;
|
|
265
|
+
}
|
|
266
|
+
/** `agentTextRunId` is the run whose summary the description now carries. */
|
|
267
|
+
export type DescriptionUpdateResult = {
|
|
268
|
+
kind: "replaced" | "unchanged";
|
|
269
|
+
sha256: string;
|
|
270
|
+
agentTextRunId: string;
|
|
271
|
+
} | {
|
|
272
|
+
kind: "refreshed";
|
|
273
|
+
reason: "empty" | "too_short" | "missing_closing_keyword";
|
|
274
|
+
sha256: string;
|
|
275
|
+
agentTextRunId: string;
|
|
276
|
+
} | {
|
|
277
|
+
kind: "edited_by_hand";
|
|
278
|
+
commentPosted: boolean;
|
|
279
|
+
} | {
|
|
280
|
+
kind: "skipped";
|
|
281
|
+
reason: "not_open" | "too_long" | "error";
|
|
282
|
+
};
|
|
283
|
+
/** One file at one ref, read through the API without a clone. */
|
|
284
|
+
export interface RepositoryFileInput {
|
|
285
|
+
repository: string;
|
|
286
|
+
ref: string;
|
|
287
|
+
/** Repository-relative POSIX path. */
|
|
288
|
+
path: string;
|
|
289
|
+
/** Refused above this many bytes. */
|
|
290
|
+
maxBytes: number;
|
|
291
|
+
}
|
|
292
|
+
export type RelatedPullRequestState = "open" | "draft" | "merged" | "closed";
|
|
293
|
+
/** One pull request of the request's set, from stored control-plane rows only. */
|
|
294
|
+
export interface RelatedPullRequestEntry {
|
|
295
|
+
repository: string;
|
|
296
|
+
/** Absent only for `self` while the pull request is being created. */
|
|
297
|
+
number?: number;
|
|
298
|
+
/** Live state; absent when unknown (at creation). */
|
|
299
|
+
state?: RelatedPullRequestState;
|
|
300
|
+
/** The pull request whose description this section is in. */
|
|
301
|
+
self?: boolean;
|
|
302
|
+
/**
|
|
303
|
+
* Step in the delegating agent's declared merge order (1-99), from
|
|
304
|
+
* CodingRun.mergeOrder; absent = the delegating agent set no order
|
|
305
|
+
* (an open entry is listed under "Not ordered:" when any other entry,
|
|
306
|
+
* open or done, has one).
|
|
307
|
+
*/
|
|
308
|
+
mergeOrder?: number;
|
|
309
|
+
}
|
|
310
|
+
export interface RelatedPullRequestsInput {
|
|
311
|
+
/** In merge order: by mergeOrder when set, else the order the request's coding runs were dispatched. */
|
|
312
|
+
entries: readonly RelatedPullRequestEntry[];
|
|
313
|
+
issue?: {
|
|
314
|
+
key: string;
|
|
315
|
+
url?: string;
|
|
316
|
+
trackerName?: string;
|
|
317
|
+
};
|
|
318
|
+
}
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export declare function mcpAddArgs(client: "codex" | "claude", projectDir: string, packageSpec: string): string[];
|
|
7
7
|
export declare function quickstartCommand(args: string[]): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Doctor's Claude Code pane section. The pane is optional, so its lines are information: a "✗"
|
|
10
|
+
* from them would be read as a failure, so none reaches the output.
|
|
11
|
+
*/
|
|
12
|
+
export declare function claudePaneDoctorLines<D>(lines: (deps: D) => string[], deps: D): string[];
|
|
8
13
|
export declare function doctorCommand(args: string[]): Promise<void>;
|
|
9
14
|
export declare function statusCommand(args: string[]): Promise<void>;
|
|
10
15
|
export declare function logsCommand(args: string[]): Promise<void>;
|
package/dist/quickstart/index.js
CHANGED
|
@@ -360,6 +360,7 @@ export async function quickstartCommand(args) {
|
|
|
360
360
|
client: { type: "string" },
|
|
361
361
|
"non-interactive": { type: "boolean" },
|
|
362
362
|
"skip-demo": { type: "boolean" },
|
|
363
|
+
"claude-plugin": { type: "boolean" },
|
|
363
364
|
yes: { type: "boolean", short: "y" },
|
|
364
365
|
},
|
|
365
366
|
});
|
|
@@ -447,6 +448,15 @@ export async function quickstartCommand(args) {
|
|
|
447
448
|
const codingRan = coding?.seed !== undefined && coding.seed.builder.status !== "skipped" && coding.seed.reviewer.status !== "skipped";
|
|
448
449
|
const client = parseClient(values.client) ?? (await chooseClient(nonInteractive));
|
|
449
450
|
const mcpConfigured = client !== "none" && configureMcpClients(client, paths);
|
|
451
|
+
// With Claude Code connected, offer the runs pane (the wardby-runs plugin): off unless asked for.
|
|
452
|
+
{
|
|
453
|
+
const { claudePluginDeps, installPlugin, quickstartPaneStep } = await import("../claude-plugin/command.js");
|
|
454
|
+
await quickstartPaneStep({ client, mcpConfigured, flag: values["claude-plugin"], nonInteractive, yes: values.yes ?? false }, {
|
|
455
|
+
ask: (question) => promptYesNo(question, false),
|
|
456
|
+
install: (options) => installPlugin(options, claudePluginDeps()),
|
|
457
|
+
warn: (line) => console.warn(line),
|
|
458
|
+
});
|
|
459
|
+
}
|
|
450
460
|
console.log("\nWardby is ready.");
|
|
451
461
|
console.log(" npx @wardby/cli@latest status");
|
|
452
462
|
console.log(" npx @wardby/cli@latest doctor");
|
|
@@ -471,6 +481,13 @@ async function databaseHealthy(paths) {
|
|
|
471
481
|
await db.$disconnect();
|
|
472
482
|
}
|
|
473
483
|
}
|
|
484
|
+
/**
|
|
485
|
+
* Doctor's Claude Code pane section. The pane is optional, so its lines are information: a "✗"
|
|
486
|
+
* from them would be read as a failure, so none reaches the output.
|
|
487
|
+
*/
|
|
488
|
+
export function claudePaneDoctorLines(lines, deps) {
|
|
489
|
+
return lines(deps).map((line) => (line.startsWith("✗") ? `!${line.slice(1)}` : line));
|
|
490
|
+
}
|
|
474
491
|
export async function doctorCommand(args) {
|
|
475
492
|
parseArgs({ args, options: {} });
|
|
476
493
|
const paths = quickstartPaths(resolveProjectDir());
|
|
@@ -520,6 +537,10 @@ export async function doctorCommand(args) {
|
|
|
520
537
|
failed ||= line.startsWith("✗");
|
|
521
538
|
console.log(line);
|
|
522
539
|
}
|
|
540
|
+
// The Claude Code runs pane is optional: its lines never fail doctor.
|
|
541
|
+
const { claudePluginDeps, claudePluginDoctorLines } = await import("../claude-plugin/command.js");
|
|
542
|
+
for (const line of claudePaneDoctorLines(claudePluginDoctorLines, claudePluginDeps()))
|
|
543
|
+
console.log(line);
|
|
523
544
|
const base = baseImageLine(resolveQuickstartImages({ env: runtimeEnv(paths), packageRoot }));
|
|
524
545
|
if (base)
|
|
525
546
|
console.log(base);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"runtime":"ghcr.io/wardby/wardby/wardby-runtime@sha256:
|
|
1
|
+
{"runtime":"ghcr.io/wardby/wardby/wardby-runtime@sha256:d5cb8fcfbfd43c230418374e9a5e203528b7d7fd372c01c8545284bc261edd58","worker":"ghcr.io/wardby/wardby/wardby-coding-worker@sha256:943a4023bcdc86d84fb923ebd97e3b0b0b5c0055ea81542cb9972ddc6ee36e69","claudeWorker":"ghcr.io/wardby/wardby/wardby-claude-coding-worker@sha256:861ef8f23bd70c329cde7013191371737d266b854678942d328906b29242d08f","claudeToolRunner":"ghcr.io/wardby/wardby/wardby-claude-tool-runner@sha256:247a870862a3b20d02b6d0a2a87ef7b3d2ae57a30478b6d517ae58535bf54260","workerNodePython":"ghcr.io/wardby/wardby/wardby-coding-worker-node-python@sha256:9684306a84da26ea057b6ccdaaa73f7377820a74bb30fe1740afb8c734da4300","claudeToolRunnerNodePython":"ghcr.io/wardby/wardby/wardby-claude-tool-runner-node-python@sha256:71bd16fd9635eee62b561771da9568120db25ec259807aa070c2eb7865dbbf0f","nativeWorker":"ghcr.io/wardby/wardby/wardby-native-worker@sha256:33606de7284c11dee354a44b6563cc7999fd135d71545c1e63244d867f1a7b2c","driver":"ghcr.io/wardby/wardby/wardby-coding-worker-driver@sha256:281a7cabc53d6ff1977d1b68c1420b2ef598f90da3995cb13df27395a58a60ef"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* compilation/validation is pure computation, no host I/O at all).
|
|
7
7
|
*/
|
|
8
8
|
import type { QuickJSContext, QuickJSRuntime } from "quickjs-emscripten";
|
|
9
|
-
export type SandboxErrorKind = "thrown" | "timeout" | "memory" | "cpu" | "non_serializable";
|
|
9
|
+
export type SandboxErrorKind = "thrown" | "timeout" | "memory" | "cpu" | "non_serializable" | "cancelled";
|
|
10
10
|
export type SandboxResult = {
|
|
11
11
|
ok: true;
|
|
12
12
|
value: unknown;
|
|
@@ -34,4 +34,14 @@ export declare function classifyEvalError(dumped: {
|
|
|
34
34
|
name?: string;
|
|
35
35
|
message?: string;
|
|
36
36
|
}): SandboxResult;
|
|
37
|
-
export
|
|
37
|
+
export interface EvalOptions {
|
|
38
|
+
/**
|
|
39
|
+
* Ends the evaluation early when it aborts (a stopped run): the result is `aborted(reason)`,
|
|
40
|
+
* and every host call still in flight is aborted with it.
|
|
41
|
+
*/
|
|
42
|
+
signal?: AbortSignal;
|
|
43
|
+
aborted?: (reason: unknown) => SandboxResult;
|
|
44
|
+
/** The wall-time error's text; defaults to naming the limit. */
|
|
45
|
+
timeoutMessage?: (limitMs: number) => string;
|
|
46
|
+
}
|
|
47
|
+
export declare function evalToJson(code: string, limitsOverride: Partial<SandboxLimits> | undefined, installExtras: (context: QuickJSContext, runtime: QuickJSRuntime, signal: AbortSignal) => void, options?: EvalOptions): Promise<SandboxResult>;
|
|
@@ -56,9 +56,14 @@ export function classifyEvalError(dumped) {
|
|
|
56
56
|
errorMessage: dumped.message ? `${dumped.name ?? "Error"}: ${dumped.message}` : JSON.stringify(dumped),
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
const defaultAborted = () => ({ ok: false, errorKind: "cancelled", errorMessage: "Cancelled." });
|
|
60
|
+
export async function evalToJson(code, limitsOverride, installExtras, options = {}) {
|
|
60
61
|
const limits = { ...DEFAULT_SANDBOX_LIMITS, ...limitsOverride };
|
|
62
|
+
const aborted = options.aborted ?? defaultAborted;
|
|
63
|
+
if (options.signal?.aborted)
|
|
64
|
+
return aborted(options.signal.reason);
|
|
61
65
|
const controller = new AbortController();
|
|
66
|
+
let onAbort;
|
|
62
67
|
let timer;
|
|
63
68
|
const deadline = Date.now() + limits.wallTimeLimitMs;
|
|
64
69
|
const qjsModule = await getQuickJsModule();
|
|
@@ -68,7 +73,7 @@ export async function evalToJson(code, limitsOverride, installExtras) {
|
|
|
68
73
|
let steps = 0;
|
|
69
74
|
runtime.setInterruptHandler(() => {
|
|
70
75
|
steps += 1;
|
|
71
|
-
return steps > limits.maxInterruptChecks || Date.now() >= deadline;
|
|
76
|
+
return steps > limits.maxInterruptChecks || Date.now() >= deadline || options.signal?.aborted === true;
|
|
72
77
|
});
|
|
73
78
|
const context = runtime.newContext();
|
|
74
79
|
let disposed = false;
|
|
@@ -139,10 +144,19 @@ export async function evalToJson(code, limitsOverride, installExtras) {
|
|
|
139
144
|
timer = setTimeout(() => resolve({
|
|
140
145
|
ok: false,
|
|
141
146
|
errorKind: "timeout",
|
|
142
|
-
errorMessage:
|
|
147
|
+
errorMessage: options.timeoutMessage
|
|
148
|
+
? options.timeoutMessage(limits.wallTimeLimitMs)
|
|
149
|
+
: `Exceeded the ${limits.wallTimeLimitMs}ms wall-time limit.`,
|
|
143
150
|
}), limits.wallTimeLimitMs);
|
|
144
151
|
});
|
|
145
|
-
|
|
152
|
+
const external = options.signal;
|
|
153
|
+
const abortPromise = new Promise((resolve) => {
|
|
154
|
+
if (!external)
|
|
155
|
+
return;
|
|
156
|
+
onAbort = () => resolve(aborted(external.reason));
|
|
157
|
+
external.addEventListener("abort", onAbort, { once: true });
|
|
158
|
+
});
|
|
159
|
+
return await Promise.race([runPromise, timeoutPromise, abortPromise]);
|
|
146
160
|
}
|
|
147
161
|
catch (err) {
|
|
148
162
|
return {
|
|
@@ -153,6 +167,8 @@ export async function evalToJson(code, limitsOverride, installExtras) {
|
|
|
153
167
|
}
|
|
154
168
|
finally {
|
|
155
169
|
clearTimeout(timer);
|
|
170
|
+
if (onAbort)
|
|
171
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
156
172
|
controller.abort();
|
|
157
173
|
disposeAll();
|
|
158
174
|
}
|
|
@@ -43,6 +43,8 @@ export interface HostFunctionOptions {
|
|
|
43
43
|
redactSecretValues?: readonly string[];
|
|
44
44
|
/** Overrides the network call — tests only. */
|
|
45
45
|
fetchImpl?: typeof safeFetch;
|
|
46
|
+
/** Each fetch's timeout (fetchTimeoutFor the tool's limit); the default fetch timeout when absent. */
|
|
47
|
+
fetchTimeoutMs?: number;
|
|
46
48
|
}
|
|
47
49
|
/** One host bridge: JSON-string arguments in, a JSON-serializable value out (bridge.ts). */
|
|
48
50
|
export type HostBridge = (argsJson: string) => Promise<unknown>;
|
|
@@ -63,6 +65,8 @@ export declare function createPrivilegedHost(options: PrivilegedHostOptions): Pr
|
|
|
63
65
|
export interface SandboxApiOptions {
|
|
64
66
|
privileged: PrivilegedHost;
|
|
65
67
|
signal?: AbortSignal;
|
|
68
|
+
/** The tool's time limit, which also caps sleep(); the default wall-time cap when absent. */
|
|
69
|
+
wallTimeLimitMs?: number;
|
|
66
70
|
/** Overrides the shared default parser-worker pool — mainly for tests. */
|
|
67
71
|
parserPool?: ParserWorkerPool;
|
|
68
72
|
}
|