@wardby/cli 0.4.0 → 0.5.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 +8 -1
- package/README.md +3 -1
- package/deploy/local/docker-compose.quickstart-coding.yml +27 -0
- package/dist/claude-coding-worker/driver.d.ts +2 -0
- package/dist/claude-coding-worker/driver.js +19 -3
- package/dist/claude-coding-worker/sdk.js +1 -1
- package/dist/cli.js +41 -22
- package/dist/coding/continuation-wording.d.ts +10 -0
- package/dist/coding/continuation-wording.js +13 -0
- package/dist/coding/docker-preflight.d.ts +12 -0
- package/dist/coding/docker-preflight.js +40 -0
- package/dist/coding/local-git.d.ts +22 -0
- package/dist/coding/local-git.js +102 -0
- package/dist/coding/local-repo-status.d.ts +7 -0
- package/dist/coding/local-repo-status.js +56 -0
- package/dist/coding/local-repo.d.ts +17 -0
- package/dist/coding/local-repo.js +65 -0
- package/dist/coding/observability.d.ts +1 -1
- package/dist/coding/observability.js +1 -0
- package/dist/coding/profile.d.ts +6 -0
- package/dist/coding/profile.js +13 -4
- package/dist/coding/protocol.d.ts +32 -7
- package/dist/coding/protocol.js +66 -4
- package/dist/coding-worker/artifact.d.ts +1 -0
- package/dist/coding-worker/errors.js +2 -0
- package/dist/config/providers.d.ts +13 -0
- package/dist/config/providers.js +21 -9
- package/dist/core/attribution.d.ts +13 -0
- package/dist/core/attribution.js +30 -0
- package/dist/core/budget-groups.d.ts +3 -1
- package/dist/core/budget-groups.js +53 -5
- package/dist/core/ci-context.d.ts +8 -0
- package/dist/core/ci-context.js +59 -0
- package/dist/core/delegation-siblings.d.ts +22 -0
- package/dist/core/delegation-siblings.js +40 -0
- package/dist/core/dispatch.d.ts +28 -4
- package/dist/core/dispatch.js +57 -15
- package/dist/core/engine-native.d.ts +16 -0
- package/dist/core/engine-native.js +49 -2
- package/dist/core/host-events.d.ts +25 -2
- package/dist/core/host-events.js +361 -19
- package/dist/core/host-status.d.ts +8 -3
- package/dist/core/host-status.js +38 -7
- package/dist/core/issue-events.d.ts +2 -6
- package/dist/core/issue-events.js +13 -19
- package/dist/core/pull-request-state-sync.d.ts +17 -0
- package/dist/core/pull-request-state-sync.js +53 -0
- package/dist/core/reconciler.d.ts +10 -1
- package/dist/core/reconciler.js +15 -2
- package/dist/core/related-pull-requests.d.ts +95 -0
- package/dist/core/related-pull-requests.js +338 -0
- package/dist/core/repo-access.d.ts +5 -1
- package/dist/core/repo-access.js +17 -1
- package/dist/core/review-fix-ledger.d.ts +20 -0
- package/dist/core/review-fix-ledger.js +41 -0
- package/dist/core/review-fix.d.ts +60 -0
- package/dist/core/review-fix.js +155 -0
- package/dist/core/review-host-tools.d.ts +13 -2
- package/dist/core/review-host-tools.js +72 -11
- package/dist/core/runner.d.ts +2 -2
- package/dist/core/runner.js +379 -184
- package/dist/core/serial-gate.d.ts +11 -0
- package/dist/core/serial-gate.js +19 -0
- package/dist/core/timing.d.ts +7 -0
- package/dist/core/timing.js +7 -0
- package/dist/env.js +6 -1
- package/dist/generated/prisma/browser.d.ts +23 -0
- package/dist/generated/prisma/client.d.ts +23 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +22 -0
- package/dist/generated/prisma/internal/class.d.ts +33 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +274 -1
- package/dist/generated/prisma/internal/prismaNamespace.js +47 -2
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +48 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +47 -2
- package/dist/generated/prisma/models/Agent.d.ts +422 -1
- package/dist/generated/prisma/models/AgentRepository.d.ts +120 -2
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +42 -1
- package/dist/generated/prisma/models/CodingRun.d.ts +205 -1
- package/dist/generated/prisma/models/DeferredReview.d.ts +1336 -0
- package/dist/generated/prisma/models/DeferredReview.js +1 -0
- package/dist/generated/prisma/models/LocalPullRequest.d.ts +1384 -0
- package/dist/generated/prisma/models/LocalPullRequest.js +1 -0
- package/dist/generated/prisma/models/LocalReview.d.ts +1315 -0
- package/dist/generated/prisma/models/LocalReview.js +1 -0
- package/dist/generated/prisma/models/Run.d.ts +223 -0
- package/dist/generated/prisma/models/RunHostCheck.d.ts +148 -1
- package/dist/generated/prisma/models.d.ts +3 -0
- package/dist/help-index.json +476 -37
- package/dist/import/neutral-schema.d.ts +2 -2
- package/dist/mcp/auth/access.d.ts +3 -1
- package/dist/mcp/auth/host-account-cli.js +2 -2
- package/dist/mcp/auth/repo-authorization.d.ts +6 -7
- package/dist/mcp/auth/repo-authorization.js +21 -0
- package/dist/mcp/index.js +4 -1
- package/dist/mcp/tools/agents.js +46 -4
- package/dist/mcp/tools/host-accounts.js +2 -2
- package/dist/mcp/tools/repositories.js +71 -12
- package/dist/mcp/tools/runs.js +23 -0
- package/dist/mcp/tools/trigger.js +153 -24
- package/dist/providers/engine/types.d.ts +11 -0
- package/dist/providers/executor/composition.js +12 -7
- package/dist/providers/executor/container.d.ts +40 -3
- package/dist/providers/executor/container.js +140 -9
- package/dist/providers/executor/types.d.ts +1 -1
- package/dist/providers/jobs/fake-kubernetes-api.d.ts +3 -2
- package/dist/providers/jobs/fake-kubernetes-api.js +3 -0
- package/dist/providers/jobs/kubernetes-api.d.ts +3 -1
- package/dist/providers/jobs/kubernetes-client.d.ts +2 -1
- package/dist/providers/jobs/kubernetes-client.js +3 -0
- package/dist/providers/jobs/kubernetes-isolation.d.ts +7 -0
- package/dist/providers/jobs/kubernetes-isolation.js +8 -3
- package/dist/providers/jobs/kubernetes-preflight.d.ts +8 -0
- package/dist/providers/jobs/kubernetes-preflight.js +7 -0
- package/dist/providers/jobs/kubernetes-quota.d.ts +31 -0
- package/dist/providers/jobs/kubernetes-quota.js +110 -0
- package/dist/providers/jobs/kubernetes.d.ts +9 -0
- package/dist/providers/jobs/kubernetes.js +50 -0
- package/dist/providers/jobs/types.d.ts +7 -0
- package/dist/providers/review-host/ci.d.ts +7 -0
- package/dist/providers/review-host/ci.js +20 -0
- package/dist/providers/review-host/github-events.d.ts +7 -0
- package/dist/providers/review-host/github-events.js +31 -7
- package/dist/providers/review-host/github.d.ts +19 -2
- package/dist/providers/review-host/github.js +145 -2
- package/dist/providers/review-host/index.d.ts +8 -2
- package/dist/providers/review-host/index.js +14 -3
- package/dist/providers/review-host/local.d.ts +72 -0
- package/dist/providers/review-host/local.js +434 -0
- package/dist/providers/review-host/types.d.ts +65 -3
- package/dist/providers/review-host/types.js +5 -3
- package/dist/providers/vcs/git.d.ts +10 -20
- package/dist/providers/vcs/git.js +71 -127
- package/dist/providers/vcs/github-remote.d.ts +67 -0
- package/dist/providers/vcs/github-remote.js +201 -0
- package/dist/providers/vcs/github.d.ts +81 -0
- package/dist/providers/vcs/github.js +203 -6
- package/dist/providers/vcs/index.d.ts +16 -1
- package/dist/providers/vcs/index.js +43 -15
- package/dist/providers/vcs/local-remote.d.ts +44 -0
- package/dist/providers/vcs/local-remote.js +108 -0
- package/dist/providers/vcs/remote.d.ts +58 -0
- package/dist/providers/vcs/remote.js +1 -0
- package/dist/providers/vcs/routing.d.ts +33 -0
- package/dist/providers/vcs/routing.js +52 -0
- package/dist/providers/vcs/types.d.ts +12 -1
- package/dist/quickstart/coding-db.d.ts +11 -0
- package/dist/quickstart/coding-db.js +75 -0
- package/dist/quickstart/coding-doctor.d.ts +13 -0
- package/dist/quickstart/coding-doctor.js +88 -0
- package/dist/quickstart/coding-images.d.ts +15 -0
- package/dist/quickstart/coding-images.js +79 -0
- package/dist/quickstart/coding-seed.d.ts +63 -0
- package/dist/quickstart/coding-seed.js +85 -0
- package/dist/quickstart/coding.d.ts +66 -0
- package/dist/quickstart/coding.js +430 -0
- package/dist/quickstart/images.d.ts +19 -0
- package/dist/quickstart/images.js +63 -0
- package/dist/quickstart/index.d.ts +6 -0
- package/dist/quickstart/index.js +72 -40
- package/dist/quickstart/starter-services.d.ts +45 -0
- package/dist/quickstart/starter-services.js +186 -0
- package/dist/quickstart-images.json +1 -0
- package/dist/serve.js +14 -1
- package/dist/viewer/api-schema.d.ts +176 -0
- package/dist/viewer/api-schema.js +27 -2
- package/dist/viewer/graph.d.ts +7 -2
- package/dist/viewer/graph.js +25 -11
- package/dist/viewer/http.d.ts +7 -1
- package/dist/viewer/http.js +8 -3
- package/dist/viewer/infra.d.ts +2 -0
- package/dist/viewer/infra.js +23 -0
- package/dist/viewer/run-detail.d.ts +2 -1
- package/dist/viewer/run-detail.js +2 -2
- package/docs/agent-recipes.md +123 -2
- package/docs/code-review-agents.md +293 -38
- package/docs/coding-agent-setup.md +141 -2
- package/docs/coding-packages.md +21 -0
- package/docs/coding-services.md +3 -0
- package/docs/coding-worker-isolation.md +87 -24
- package/docs/getting-started-gke.md +24 -0
- package/docs/getting-started.md +82 -8
- package/docs/jira-agents.md +26 -5
- package/docs/security-deployment.md +9 -0
- package/docs/viewer-api.md +21 -2
- package/help/admin-viewer.md +11 -1
- package/help/agent-recipes.md +25 -1
- package/help/builder-agent.md +4 -0
- package/help/code-review-agents.md +82 -2
- package/help/coding-packages.md +12 -1
- package/help/coding-services.md +4 -0
- package/help/cost-attribution.md +3 -1
- package/help/creating-agents.md +3 -0
- package/help/deploy-gke.md +2 -1
- package/help/errors/coding-provider-not-configured.md +60 -0
- package/help/errors/coding-turn-limit.md +25 -0
- package/help/errors/continuation-closed.md +75 -0
- package/help/errors/local-branch-conflict.md +37 -0
- package/help/errors/local-path-invalid.md +30 -0
- package/help/errors/local-ref-invalid.md +36 -0
- package/help/errors/local-ref-not-found.md +41 -0
- package/help/errors/local-repo-not-allowed.md +46 -0
- package/help/errors/local-repo-not-found.md +39 -0
- package/help/errors/model-unavailable.md +7 -6
- package/help/errors/vcs-github-not-configured.md +34 -0
- package/help/getting-started.md +4 -0
- package/help/github.md +22 -5
- package/help/jira.md +9 -2
- package/help/local-repositories.md +182 -0
- package/help/related-pull-requests.md +89 -0
- package/help/review-fix-rounds.md +69 -0
- package/help/troubleshooting/budgets.md +11 -0
- package/help/troubleshooting/coding-workers.md +22 -0
- package/help/troubleshooting/repository-access.md +5 -0
- package/package.json +5 -3
- package/prisma/migrations/20261004100000_delegations_and_coding_turns/migration.sql +12 -0
- package/prisma/migrations/20261005000000_review_fix_rounds/migration.sql +5 -0
- package/prisma/migrations/20261006000000_parallel_delegations/migration.sql +6 -0
- package/prisma/migrations/20261007000000_ci_rereview/migration.sql +4 -0
- package/prisma/migrations/20261008000000_review_after_ci/migration.sql +33 -0
- package/prisma/migrations/20261009000001_coding_run_local_branch/migration.sql +7 -0
- package/prisma/migrations/20261009000002_local_review/migration.sql +50 -0
- package/prisma/schema.prisma +98 -1
package/dist/quickstart/index.js
CHANGED
|
@@ -6,9 +6,12 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { parseArgs } from "node:util";
|
|
7
7
|
import { createInterface } from "node:readline/promises";
|
|
8
8
|
import { composeProjectName, defaultModel, ensureWardbyIgnored, generatedSecret, providerKeyName, quickstartPaths, readQuickstartEnv, readQuickstartState, resolveProjectDir, writeQuickstartEnv, writeQuickstartState, } from "./config.js";
|
|
9
|
+
import { codingStep, quickstartComposeFiles } from "./coding.js";
|
|
10
|
+
import { prismaCatalogImages, prismaLocalAgents, prismaSeedCodingAgents } from "./coding-db.js";
|
|
11
|
+
import { codingDoctorLines } from "./coding-doctor.js";
|
|
12
|
+
import { parseStarterChoice } from "./starter-services.js";
|
|
9
13
|
import { FETCH_NOTICE, migrateFailureReason, prismaMigrateInvocation, REGISTRY_FAILURE_MESSAGE, runPrismaMigrateInvocation, } from "./migrate.js";
|
|
10
14
|
const packageRoot = fileURLToPath(new URL("../../", import.meta.url));
|
|
11
|
-
const composeFile = join(packageRoot, "deploy/local/docker-compose.yml");
|
|
12
15
|
const wardbyBin = join(packageRoot, "bin/wardby.js");
|
|
13
16
|
const packageJson = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
|
|
14
17
|
const DEMO_AGENT = "hello-wardby";
|
|
@@ -33,17 +36,10 @@ function commandFailure(label, result) {
|
|
|
33
36
|
function runtimeEnv(paths, values = readQuickstartEnv(paths)) {
|
|
34
37
|
return { ...process.env, ...values, WARDBY_PROJECT_DIR: paths.projectDir };
|
|
35
38
|
}
|
|
39
|
+
/** The coding proxy's compose file joins once the coding step configured it, so ps/logs/down cover it too. */
|
|
36
40
|
function composeArgs(paths, state, args) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"--project-name",
|
|
40
|
-
state.composeProject,
|
|
41
|
-
"--env-file",
|
|
42
|
-
paths.envFile,
|
|
43
|
-
"--file",
|
|
44
|
-
composeFile,
|
|
45
|
-
...args,
|
|
46
|
-
];
|
|
41
|
+
const files = quickstartComposeFiles(packageRoot, readQuickstartEnv(paths)).flatMap((file) => ["--file", file]);
|
|
42
|
+
return ["compose", "--project-name", state.composeProject, "--env-file", paths.envFile, ...files, ...args];
|
|
47
43
|
}
|
|
48
44
|
function runCompose(paths, state, args, options = {}) {
|
|
49
45
|
return runCommand("docker", composeArgs(paths, state, args), {
|
|
@@ -85,7 +81,7 @@ async function selectPostgresPort(firstPort = 55432) {
|
|
|
85
81
|
}
|
|
86
82
|
async function startPostgres(paths, state, config) {
|
|
87
83
|
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
88
|
-
const up = runCompose(paths, state, ["up", "--detach", "--wait", "--wait-timeout", "90"]);
|
|
84
|
+
const up = runCompose(paths, state, ["up", "--detach", "--wait", "--wait-timeout", "90", "postgres"]);
|
|
89
85
|
if (up.status === 0)
|
|
90
86
|
return;
|
|
91
87
|
const output = `${up.stderr}\n${up.stdout}`;
|
|
@@ -170,6 +166,39 @@ function parseProvider(value) {
|
|
|
170
166
|
}
|
|
171
167
|
return value;
|
|
172
168
|
}
|
|
169
|
+
function parseCodingProvider(value) {
|
|
170
|
+
if (value === undefined)
|
|
171
|
+
return undefined;
|
|
172
|
+
if (value !== "codex" && value !== "claude-code") {
|
|
173
|
+
throw new Error(`--coding-provider must be codex or claude-code; got "${value}".`);
|
|
174
|
+
}
|
|
175
|
+
return value;
|
|
176
|
+
}
|
|
177
|
+
function codingDeps(paths) {
|
|
178
|
+
return {
|
|
179
|
+
run: runCommand,
|
|
180
|
+
prompts: { line: promptLine, yesNo: promptYesNo, secret: promptSecret },
|
|
181
|
+
log: (line) => console.log(line),
|
|
182
|
+
cwd: process.cwd(),
|
|
183
|
+
env: process.env,
|
|
184
|
+
packageRoot,
|
|
185
|
+
seed: (input) => prismaSeedCodingAgents(paths, input),
|
|
186
|
+
catalogImages: (services) => prismaCatalogImages(paths, services),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
async function codingSection(paths) {
|
|
190
|
+
return await codingDoctorLines(readQuickstartEnv(paths), {
|
|
191
|
+
run: runCommand,
|
|
192
|
+
listLocalAgents: async () => {
|
|
193
|
+
try {
|
|
194
|
+
return await prismaLocalAgents(paths);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
}
|
|
173
202
|
function parseClient(value) {
|
|
174
203
|
if (value === undefined)
|
|
175
204
|
return undefined;
|
|
@@ -263,6 +292,16 @@ function runDemo(paths) {
|
|
|
263
292
|
function clientInstalled(command) {
|
|
264
293
|
return runCommand(command, ["--version"]).status === 0;
|
|
265
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* Arguments for `<client> mcp add`. The server name comes right after `add`:
|
|
297
|
+
* Claude Code's `-e/--env <env...>` is variadic and would otherwise read the
|
|
298
|
+
* name as another KEY=value ("Invalid environment variable format: wardby").
|
|
299
|
+
*/
|
|
300
|
+
export function mcpAddArgs(client, projectDir, packageSpec) {
|
|
301
|
+
const env = `WARDBY_PROJECT_DIR=${projectDir}`;
|
|
302
|
+
const options = client === "codex" ? ["--env", env] : ["--scope", "local", "-e", env];
|
|
303
|
+
return ["mcp", "add", "wardby", ...options, "--", "npx", "--yes", packageSpec, "mcp"];
|
|
304
|
+
}
|
|
266
305
|
function configureOneMcpClient(client, paths) {
|
|
267
306
|
if (!clientInstalled(client)) {
|
|
268
307
|
console.warn(`! ${client} is not installed; skipped its MCP configuration.`);
|
|
@@ -273,34 +312,7 @@ function configureOneMcpClient(client, paths) {
|
|
|
273
312
|
console.log(`✓ ${client} already has an MCP server named wardby.`);
|
|
274
313
|
return true;
|
|
275
314
|
}
|
|
276
|
-
const
|
|
277
|
-
const args = client === "codex"
|
|
278
|
-
? [
|
|
279
|
-
"mcp",
|
|
280
|
-
"add",
|
|
281
|
-
"--env",
|
|
282
|
-
`WARDBY_PROJECT_DIR=${paths.projectDir}`,
|
|
283
|
-
"wardby",
|
|
284
|
-
"--",
|
|
285
|
-
"npx",
|
|
286
|
-
"--yes",
|
|
287
|
-
packageSpec,
|
|
288
|
-
"mcp",
|
|
289
|
-
]
|
|
290
|
-
: [
|
|
291
|
-
"mcp",
|
|
292
|
-
"add",
|
|
293
|
-
"--scope",
|
|
294
|
-
"local",
|
|
295
|
-
"-e",
|
|
296
|
-
`WARDBY_PROJECT_DIR=${paths.projectDir}`,
|
|
297
|
-
"wardby",
|
|
298
|
-
"--",
|
|
299
|
-
"npx",
|
|
300
|
-
"--yes",
|
|
301
|
-
packageSpec,
|
|
302
|
-
"mcp",
|
|
303
|
-
];
|
|
315
|
+
const args = mcpAddArgs(client, paths.projectDir, `@wardby/cli@${packageJson.version}`);
|
|
304
316
|
const result = runCommand(client, args, { cwd: paths.projectDir });
|
|
305
317
|
if (result.status !== 0)
|
|
306
318
|
commandFailure(`${client} MCP configuration`, result);
|
|
@@ -333,7 +345,12 @@ async function chooseClient(nonInteractive) {
|
|
|
333
345
|
export async function quickstartCommand(args) {
|
|
334
346
|
const { values } = parseArgs({
|
|
335
347
|
args,
|
|
348
|
+
allowNegative: true,
|
|
336
349
|
options: {
|
|
350
|
+
coding: { type: "boolean" },
|
|
351
|
+
trust: { type: "string", multiple: true },
|
|
352
|
+
"coding-provider": { type: "string" },
|
|
353
|
+
"starter-services": { type: "string" },
|
|
337
354
|
provider: { type: "string" },
|
|
338
355
|
model: { type: "string" },
|
|
339
356
|
budget: { type: "string" },
|
|
@@ -344,6 +361,13 @@ export async function quickstartCommand(args) {
|
|
|
344
361
|
},
|
|
345
362
|
});
|
|
346
363
|
const nonInteractive = values["non-interactive"] ?? false;
|
|
364
|
+
const codingOptions = {
|
|
365
|
+
nonInteractive,
|
|
366
|
+
coding: values.coding,
|
|
367
|
+
trust: values.trust ?? [],
|
|
368
|
+
provider: parseCodingProvider(values["coding-provider"]),
|
|
369
|
+
starterServices: values["starter-services"] === undefined ? undefined : parseStarterChoice(values["starter-services"]),
|
|
370
|
+
};
|
|
347
371
|
const skipDemo = values["skip-demo"] ?? false;
|
|
348
372
|
const budget = values.budget === undefined ? 1 : Number(values.budget);
|
|
349
373
|
if (!Number.isFinite(budget) || budget <= 0)
|
|
@@ -415,6 +439,7 @@ export async function quickstartCommand(args) {
|
|
|
415
439
|
else
|
|
416
440
|
console.log(`Skipped the billed run. Start it later with: npx @wardby/cli@latest run ${DEMO_AGENT}`);
|
|
417
441
|
}
|
|
442
|
+
await codingStep(paths, state, codingOptions, codingDeps(paths));
|
|
418
443
|
const client = parseClient(values.client) ?? (await chooseClient(nonInteractive));
|
|
419
444
|
const mcpConfigured = client !== "none" && configureMcpClients(client, paths);
|
|
420
445
|
console.log("\nWardby is ready.");
|
|
@@ -498,6 +523,10 @@ export async function doctorCommand(args) {
|
|
|
498
523
|
failed ||= !ok;
|
|
499
524
|
console.log(`${ok ? "✓" : "✗"} ${name}${detail ? ` (${detail})` : ""}`);
|
|
500
525
|
}
|
|
526
|
+
for (const line of await codingSection(paths)) {
|
|
527
|
+
failed ||= line.startsWith("✗");
|
|
528
|
+
console.log(line);
|
|
529
|
+
}
|
|
501
530
|
if (failed)
|
|
502
531
|
process.exitCode = 1;
|
|
503
532
|
}
|
|
@@ -517,6 +546,9 @@ export async function statusCommand(args) {
|
|
|
517
546
|
process.stdout.write(ps.stdout);
|
|
518
547
|
const healthy = await databaseHealthy(paths);
|
|
519
548
|
console.log(`database: ${healthy ? "healthy" : "unavailable"}`);
|
|
549
|
+
const coding = healthy ? await codingSection(paths) : [];
|
|
550
|
+
if (coding.length > 0)
|
|
551
|
+
console.log(["coding:", ...coding.map((line) => ` ${line}`)].join("\n"));
|
|
520
552
|
if (!healthy)
|
|
521
553
|
process.exitCode = 1;
|
|
522
554
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type DeclaredService } from "../coding/services/declaration.js";
|
|
2
|
+
export declare const STARTER_SERVICES_BRANCH = "wardby/quickstart-services";
|
|
3
|
+
export type StarterService = "postgres" | "redis";
|
|
4
|
+
export declare function starterServicesYaml(choices: readonly StarterService[]): string;
|
|
5
|
+
/** `--starter-services postgres,redis|none`. */
|
|
6
|
+
export declare function parseStarterChoice(value: string): StarterService[];
|
|
7
|
+
/** The services a starter choice declares, as parseServiceDeclaration would read them back. */
|
|
8
|
+
export declare function starterDeclaredServices(choices: readonly StarterService[]): DeclaredService[];
|
|
9
|
+
/** The checked-out branch and its commit; null when HEAD is detached or the branch has no commits. */
|
|
10
|
+
export declare function repoDefaultBranch(dir: string): Promise<{
|
|
11
|
+
branch: string;
|
|
12
|
+
sha: string;
|
|
13
|
+
} | null>;
|
|
14
|
+
export type DeclaredServicesStatus = {
|
|
15
|
+
kind: "absent";
|
|
16
|
+
} | {
|
|
17
|
+
kind: "valid";
|
|
18
|
+
services: DeclaredService[];
|
|
19
|
+
} | {
|
|
20
|
+
kind: "invalid";
|
|
21
|
+
reason: string;
|
|
22
|
+
};
|
|
23
|
+
/** The committed `.wardby/services.yaml` at `ref`, validated; `dir` must sit inside `roots`. */
|
|
24
|
+
export declare function inspectDeclaredServices(dir: string, roots: readonly string[], ref: string): Promise<DeclaredServicesStatus>;
|
|
25
|
+
export type StarterCommitResult = {
|
|
26
|
+
status: "created" | "replaced";
|
|
27
|
+
commit: string;
|
|
28
|
+
} | {
|
|
29
|
+
status: "kept";
|
|
30
|
+
commit: string;
|
|
31
|
+
} | {
|
|
32
|
+
status: "refused";
|
|
33
|
+
reason: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Commits `content` as `.wardby/services.yaml` on top of `baseSha` and points
|
|
37
|
+
* the starter branch at it. An existing branch is replaced only when
|
|
38
|
+
* `confirmReplace` says so, and only if it has not moved since it was read.
|
|
39
|
+
*/
|
|
40
|
+
export declare function commitStarterServices(input: {
|
|
41
|
+
dir: string;
|
|
42
|
+
baseSha: string;
|
|
43
|
+
content: string;
|
|
44
|
+
confirmReplace: (existingSha: string) => Promise<boolean>;
|
|
45
|
+
}): Promise<StarterCommitResult>;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The quickstart's `.wardby/services.yaml` handling for a local repository:
|
|
3
|
+
* read the committed declaration on the default branch, or write a starter one
|
|
4
|
+
* onto its own branch with git plumbing.
|
|
5
|
+
*
|
|
6
|
+
* The write never touches the user's working tree, index, or HEAD: it builds
|
|
7
|
+
* the tree in a scratch index (GIT_INDEX_FILE in a temp dir), creates the
|
|
8
|
+
* commit with commit-tree, and moves only refs/heads/wardby/quickstart-services
|
|
9
|
+
* with a compare-and-swap update-ref. git runs through the hardened runner
|
|
10
|
+
* (GIT_* stripped, hooks/fsmonitor off, no signing), with an explicit identity
|
|
11
|
+
* so no user configuration is needed.
|
|
12
|
+
*/
|
|
13
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
import { localGit, localGitWrite } from "../coding/local-git.js";
|
|
17
|
+
import { LOCAL_REPO_PREFIX } from "../coding/local-repo.js";
|
|
18
|
+
import { MAX_SERVICE_DECLARATION_BYTES, SERVICE_DECLARATION_PATH, ServiceDeclarationError, parseServiceDeclaration, } from "../coding/services/declaration.js";
|
|
19
|
+
import { LocalRemote } from "../providers/vcs/local-remote.js";
|
|
20
|
+
export const STARTER_SERVICES_BRANCH = "wardby/quickstart-services";
|
|
21
|
+
const STARTER_REF = `refs/heads/${STARTER_SERVICES_BRANCH}`;
|
|
22
|
+
const COMMIT_MESSAGE = "Add starter .wardby/services.yaml (wardby quickstart)";
|
|
23
|
+
const IDENTITY = {
|
|
24
|
+
GIT_AUTHOR_NAME: "wardby quickstart",
|
|
25
|
+
GIT_AUTHOR_EMAIL: "quickstart@wardby.invalid",
|
|
26
|
+
GIT_COMMITTER_NAME: "wardby quickstart",
|
|
27
|
+
GIT_COMMITTER_EMAIL: "quickstart@wardby.invalid",
|
|
28
|
+
};
|
|
29
|
+
const OBJECT_ID = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
30
|
+
/** Catalog versions (src/coding/services/builtins.ts); the catalog pins each to an image digest. */
|
|
31
|
+
const STARTER_VERSIONS = { postgres: "16", redis: "7" };
|
|
32
|
+
export function starterServicesYaml(choices) {
|
|
33
|
+
const lines = [
|
|
34
|
+
"# Services wardby starts next to each coding run. Each name and version must",
|
|
35
|
+
"# exist in the wardby service catalog, and the agent must allow the name",
|
|
36
|
+
"# (codingProfile.services). See docs/coding-services.md.",
|
|
37
|
+
"services:",
|
|
38
|
+
...[...new Set(choices)].sort().map((name) => ` ${name}: "${STARTER_VERSIONS[name]}"`),
|
|
39
|
+
];
|
|
40
|
+
return `${lines.join("\n")}\n`;
|
|
41
|
+
}
|
|
42
|
+
/** `--starter-services postgres,redis|none`. */
|
|
43
|
+
export function parseStarterChoice(value) {
|
|
44
|
+
const names = value
|
|
45
|
+
.split(",")
|
|
46
|
+
.map((part) => part.trim().toLowerCase())
|
|
47
|
+
.filter(Boolean);
|
|
48
|
+
if (names.length === 1 && names[0] === "none")
|
|
49
|
+
return [];
|
|
50
|
+
const chosen = new Set();
|
|
51
|
+
for (const name of names) {
|
|
52
|
+
if (name !== "postgres" && name !== "redis") {
|
|
53
|
+
throw new Error(`--starter-services must list postgres, redis, or none; got "${value}".`);
|
|
54
|
+
}
|
|
55
|
+
chosen.add(name);
|
|
56
|
+
}
|
|
57
|
+
return [...chosen].sort();
|
|
58
|
+
}
|
|
59
|
+
/** The services a starter choice declares, as parseServiceDeclaration would read them back. */
|
|
60
|
+
export function starterDeclaredServices(choices) {
|
|
61
|
+
return parseServiceDeclaration(starterServicesYaml(choices));
|
|
62
|
+
}
|
|
63
|
+
/** The checked-out branch and its commit; null when HEAD is detached or the branch has no commits. */
|
|
64
|
+
export async function repoDefaultBranch(dir) {
|
|
65
|
+
let branch;
|
|
66
|
+
try {
|
|
67
|
+
branch = (await localGit(dir, ["symbolic-ref", "--quiet", "--short", "HEAD"], 64 * 1024)).trim();
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
if (!branch)
|
|
73
|
+
return null;
|
|
74
|
+
try {
|
|
75
|
+
const sha = (await localGit(dir, ["rev-parse", "--verify", "--quiet", "--end-of-options", `refs/heads/${branch}^{commit}`])).trim();
|
|
76
|
+
return OBJECT_ID.test(sha) ? { branch, sha } : null;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** The committed `.wardby/services.yaml` at `ref`, validated; `dir` must sit inside `roots`. */
|
|
83
|
+
export async function inspectDeclaredServices(dir, roots, ref) {
|
|
84
|
+
let text;
|
|
85
|
+
try {
|
|
86
|
+
text = await new LocalRemote({ roots: () => roots }).readRepositoryFile({
|
|
87
|
+
repository: `${LOCAL_REPO_PREFIX}${dir}`,
|
|
88
|
+
ref,
|
|
89
|
+
path: SERVICE_DECLARATION_PATH,
|
|
90
|
+
maxBytes: MAX_SERVICE_DECLARATION_BYTES,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
return { kind: "invalid", reason: error instanceof Error ? error.message : String(error) };
|
|
95
|
+
}
|
|
96
|
+
if (text === null)
|
|
97
|
+
return { kind: "absent" };
|
|
98
|
+
try {
|
|
99
|
+
return { kind: "valid", services: parseServiceDeclaration(text) };
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (error instanceof ServiceDeclarationError)
|
|
103
|
+
return { kind: "invalid", reason: error.reason };
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function currentRef(dir) {
|
|
108
|
+
try {
|
|
109
|
+
const sha = (await localGit(dir, ["rev-parse", "--verify", "--quiet", "--end-of-options", STARTER_REF])).trim();
|
|
110
|
+
return OBJECT_ID.test(sha) ? sha : null;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** True when any worktree of the repository (this one included) has the starter branch checked out. */
|
|
117
|
+
async function branchCheckedOut(dir) {
|
|
118
|
+
const listing = await localGit(dir, ["worktree", "list", "--porcelain"]);
|
|
119
|
+
return listing.split("\n").some((line) => line.trim() === `branch ${STARTER_REF}`);
|
|
120
|
+
}
|
|
121
|
+
/** The starter commit on top of `baseSha`, built in a scratch index; writes objects only, never a ref. */
|
|
122
|
+
async function buildStarterCommit(dir, baseSha, content) {
|
|
123
|
+
const scratch = await mkdtemp(join(tmpdir(), "wardby-quickstart-index-"));
|
|
124
|
+
try {
|
|
125
|
+
const env = { ...IDENTITY, GIT_INDEX_FILE: join(scratch, "index") };
|
|
126
|
+
await localGitWrite(dir, ["read-tree", "--end-of-options", baseSha], { env });
|
|
127
|
+
const blob = (await localGitWrite(dir, ["hash-object", "-w", "--no-filters", "--stdin"], { env, input: content })).trim();
|
|
128
|
+
if (!OBJECT_ID.test(blob))
|
|
129
|
+
throw new Error("git hash-object returned no object id");
|
|
130
|
+
await localGitWrite(dir, ["update-index", "--add", "--cacheinfo", `100644,${blob},${SERVICE_DECLARATION_PATH}`], {
|
|
131
|
+
env,
|
|
132
|
+
});
|
|
133
|
+
const tree = (await localGitWrite(dir, ["write-tree"], { env })).trim();
|
|
134
|
+
if (!OBJECT_ID.test(tree))
|
|
135
|
+
throw new Error("git write-tree returned no tree id");
|
|
136
|
+
const commit = (await localGitWrite(dir, ["commit-tree", "--no-gpg-sign", "-p", baseSha, "-m", COMMIT_MESSAGE, tree], { env })).trim();
|
|
137
|
+
if (!OBJECT_ID.test(commit))
|
|
138
|
+
throw new Error("git commit-tree returned no commit id");
|
|
139
|
+
return commit;
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
await rm(scratch, { recursive: true, force: true });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Commits `content` as `.wardby/services.yaml` on top of `baseSha` and points
|
|
147
|
+
* the starter branch at it. An existing branch is replaced only when
|
|
148
|
+
* `confirmReplace` says so, and only if it has not moved since it was read.
|
|
149
|
+
*/
|
|
150
|
+
export async function commitStarterServices(input) {
|
|
151
|
+
const { dir, baseSha, content } = input;
|
|
152
|
+
if (!OBJECT_ID.test(baseSha))
|
|
153
|
+
return { status: "refused", reason: "the base commit id is not valid" };
|
|
154
|
+
// Moving a checked-out branch would change that worktree's HEAD commit under it.
|
|
155
|
+
if (await branchCheckedOut(dir)) {
|
|
156
|
+
return { status: "refused", reason: `${STARTER_SERVICES_BRANCH} is checked out; switch away from it first` };
|
|
157
|
+
}
|
|
158
|
+
const existing = await currentRef(dir);
|
|
159
|
+
if (existing && !(await input.confirmReplace(existing)))
|
|
160
|
+
return { status: "kept", commit: existing };
|
|
161
|
+
let commit;
|
|
162
|
+
try {
|
|
163
|
+
commit = await buildStarterCommit(dir, baseSha, content);
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
const detail = (error.stderr || error.message).trim().split("\n")[0];
|
|
167
|
+
return { status: "refused", reason: `git could not build the starter commit: ${detail}` };
|
|
168
|
+
}
|
|
169
|
+
// Re-checked right before the move: a checkout since the first check would otherwise be updated under it.
|
|
170
|
+
if (await branchCheckedOut(dir)) {
|
|
171
|
+
return { status: "refused", reason: `${STARTER_SERVICES_BRANCH} is checked out; switch away from it first` };
|
|
172
|
+
}
|
|
173
|
+
const expected = existing ?? "0".repeat(baseSha.length);
|
|
174
|
+
try {
|
|
175
|
+
await localGitWrite(dir, ["update-ref", "--no-deref", "-m", "wardby quickstart", STARTER_REF, commit, expected], {
|
|
176
|
+
env: IDENTITY,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return {
|
|
181
|
+
status: "refused",
|
|
182
|
+
reason: `${STARTER_SERVICES_BRANCH} changed while quickstart was writing it (or a conflicting ref exists); left as is`,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return { status: existing ? "replaced" : "created", commit };
|
|
186
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"runtime":"ghcr.io/wardby/wardby/wardby-runtime@sha256:b6d6403338725abc9fc109c5a026e654c88e72ae8db43788db0cfa86f18c1635","worker":"ghcr.io/wardby/wardby/wardby-coding-worker@sha256:45acf6f2ac4b43b46dd3b9e1a89cd3e3369fd4898f0ce177cf063806f98f65d1","claudeWorker":"ghcr.io/wardby/wardby/wardby-claude-coding-worker@sha256:701abc30b689aed61b8d2028523d53847cbc33dea23ce16ddd22b302a5d964e2","claudeToolRunner":"ghcr.io/wardby/wardby/wardby-claude-tool-runner@sha256:7c4574fffc1adba27d3c86949b62d8f8a95f8198fbbca0710ed68e812ad2f357"}
|
package/dist/serve.js
CHANGED
|
@@ -20,6 +20,7 @@ import { loadCodingConcurrencyConfig, loadMcpConfig } from "./config/providers.j
|
|
|
20
20
|
import { drainCodingQueue } from "./core/coding-queue.js";
|
|
21
21
|
import { prisma } from "./core/db.js";
|
|
22
22
|
import { startReconciler } from "./core/reconciler.js";
|
|
23
|
+
import { createRepoAccessGate } from "./core/repo-access.js";
|
|
23
24
|
import { startScheduler } from "./core/scheduler.js";
|
|
24
25
|
import { buildMcpProviders, startMcp } from "./mcp/index.js";
|
|
25
26
|
export async function startServe(options = {}) {
|
|
@@ -33,10 +34,22 @@ export async function startServe(options = {}) {
|
|
|
33
34
|
const concurrency = loadCodingConcurrencyConfig();
|
|
34
35
|
const providers = options.providers ?? buildMcpProviders().providers;
|
|
35
36
|
const mcp = await startMcp({ providers, schedulerAttached: true });
|
|
37
|
+
const reviewHosts = providers.reviewHosts;
|
|
36
38
|
const reconciler = startReconciler({
|
|
37
39
|
executor: providers.executor,
|
|
38
|
-
reviewHosts
|
|
40
|
+
reviewHosts,
|
|
39
41
|
issueTrackers: providers.issueTrackers,
|
|
42
|
+
...(reviewHosts
|
|
43
|
+
? {
|
|
44
|
+
deferredReviews: {
|
|
45
|
+
db: prisma,
|
|
46
|
+
executor: providers.executor,
|
|
47
|
+
hosts: reviewHosts,
|
|
48
|
+
repoAccess: providers.repoAccess ?? createRepoAccessGate({ db: prisma, hosts: reviewHosts }),
|
|
49
|
+
...(providers.issueTrackers ? { issueTrackers: providers.issueTrackers } : {}),
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
: {}),
|
|
40
53
|
});
|
|
41
54
|
const selfDefects = { db: prisma, issueTrackers: providers.issueTrackers };
|
|
42
55
|
const scheduler = startScheduler({
|