@wardby/cli 0.5.2 → 0.5.3
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/dist/coding/local-git.js +9 -1
- package/dist/coding/registry/pypi.d.ts +25 -1
- package/dist/coding/registry/pypi.js +140 -16
- package/dist/coding/registry/types.d.ts +17 -3
- package/dist/config/providers.d.ts +3 -2
- package/dist/core/http-runtime.js +13 -6
- package/dist/help-index.json +207 -15
- package/dist/mcp/auth/ownership.d.ts +1 -1
- package/dist/mcp/tools/agents.js +1 -1
- package/dist/providers/coding-proxy/mock-upstream.d.ts +33 -0
- package/dist/providers/coding-proxy/mock-upstream.js +116 -0
- package/dist/providers/coding-proxy/registry/prisma-store.d.ts +3 -0
- package/dist/providers/coding-proxy/registry/prisma-store.js +17 -0
- package/dist/providers/coding-proxy/registry/service.d.ts +4 -0
- package/dist/providers/coding-proxy/registry/service.js +20 -3
- package/dist/providers/coding-proxy/registry/store.d.ts +18 -1
- package/dist/providers/coding-proxy/registry/store.js +33 -0
- package/dist/providers/coding-proxy/runtime.js +12 -1
- package/dist/providers/coding-proxy/types.d.ts +2 -0
- package/dist/providers/jobs/kubernetes-isolation.d.ts +13 -0
- package/dist/providers/jobs/kubernetes-isolation.js +52 -5
- package/dist/providers/jobs/kubernetes.d.ts +20 -6
- package/dist/providers/jobs/kubernetes.js +43 -32
- package/dist/providers/llm/openai.js +0 -1
- package/dist/quickstart/coding-db.js +14 -2
- package/dist/quickstart/coding-doctor.d.ts +3 -0
- package/dist/quickstart/coding-doctor.js +3 -0
- package/dist/quickstart/coding-images.d.ts +9 -0
- package/dist/quickstart/coding-images.js +39 -0
- package/dist/quickstart/coding-seed.d.ts +13 -1
- package/dist/quickstart/coding-seed.js +25 -7
- package/dist/quickstart/coding.d.ts +2 -0
- package/dist/quickstart/coding.js +75 -3
- package/dist/quickstart/config.d.ts +2 -0
- package/dist/quickstart/config.js +4 -0
- package/dist/quickstart/images.d.ts +11 -0
- package/dist/quickstart/images.js +74 -12
- package/dist/quickstart/index.d.ts +0 -2
- package/dist/quickstart/index.js +10 -14
- package/dist/quickstart/manifests.d.ts +56 -0
- package/dist/quickstart/manifests.js +523 -0
- package/dist/quickstart/python-detect.d.ts +10 -0
- package/dist/quickstart/python-detect.js +32 -0
- package/dist/quickstart/repo-packages.d.ts +10 -0
- package/dist/quickstart/repo-packages.js +132 -0
- package/dist/quickstart/reviewer-prompt.d.ts +12 -0
- package/dist/quickstart/reviewer-prompt.js +84 -0
- package/dist/quickstart/scenarios.d.ts +19 -0
- package/dist/quickstart/scenarios.js +28 -0
- package/dist/quickstart/sync-reviewer-prompt.d.ts +1 -0
- package/dist/quickstart/sync-reviewer-prompt.js +10 -0
- package/dist/quickstart-images.json +1 -1
- package/dist/viewer/api-schema.d.ts +4 -4
- package/docs/coding-agent-setup.md +17 -0
- package/docs/coding-packages.md +101 -3
- package/docs/coding-worker-byo-images.md +47 -2
- package/docs/coding-worker-isolation.md +20 -9
- package/docs/getting-started.md +139 -11
- package/docs/knowledge.md +11 -0
- package/help/architecture-agent.md +136 -0
- package/help/build-worker-image.md +198 -0
- package/help/coding-packages.md +31 -2
- package/help/creating-agents.md +14 -0
- package/help/deploy-gke.md +29 -5
- package/help/deployment-targets.md +3 -3
- package/help/getting-started.md +19 -0
- package/help/local-repositories.md +36 -5
- package/package.json +3 -2
|
@@ -7,25 +7,69 @@ function readPackageImages(packageRoot) {
|
|
|
7
7
|
const parsed = JSON.parse(readFileSync(join(packageRoot, "dist", "quickstart-images.json"), "utf8"));
|
|
8
8
|
if (typeof parsed !== "object" || parsed === null)
|
|
9
9
|
return undefined;
|
|
10
|
-
const { runtime, worker, claudeWorker, claudeToolRunner } = parsed;
|
|
10
|
+
const { runtime, worker, claudeWorker, claudeToolRunner, workerNodePython, claudeToolRunnerNodePython, driver } = parsed;
|
|
11
11
|
if (typeof runtime !== "string" || typeof worker !== "string")
|
|
12
12
|
return undefined;
|
|
13
13
|
// An image is never resolved by tag: a tag can be repointed after release.
|
|
14
14
|
if (!DIGEST_REF.test(runtime) || !DIGEST_REF.test(worker))
|
|
15
15
|
return undefined;
|
|
16
16
|
// The Claude Code images are optional, but both or neither, and pinned when present.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
const result = { runtime, worker };
|
|
18
|
+
if (claudeWorker !== undefined || claudeToolRunner !== undefined) {
|
|
19
|
+
if (typeof claudeWorker !== "string" || typeof claudeToolRunner !== "string")
|
|
20
|
+
return undefined;
|
|
21
|
+
if (!DIGEST_REF.test(claudeWorker) || !DIGEST_REF.test(claudeToolRunner))
|
|
22
|
+
return undefined;
|
|
23
|
+
result.claudeWorker = claudeWorker;
|
|
24
|
+
result.claudeToolRunner = claudeToolRunner;
|
|
25
|
+
}
|
|
26
|
+
// The Node + Python images are each optional on their own (an older release has neither).
|
|
27
|
+
if (workerNodePython !== undefined) {
|
|
28
|
+
if (typeof workerNodePython !== "string" || !DIGEST_REF.test(workerNodePython))
|
|
29
|
+
return undefined;
|
|
30
|
+
result.workerNodePython = workerNodePython;
|
|
31
|
+
}
|
|
32
|
+
if (claudeToolRunnerNodePython !== undefined) {
|
|
33
|
+
if (typeof claudeToolRunnerNodePython !== "string" || !DIGEST_REF.test(claudeToolRunnerNodePython)) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
result.claudeToolRunnerNodePython = claudeToolRunnerNodePython;
|
|
37
|
+
}
|
|
38
|
+
// The driver is optional (an older release has none), and pinned when present.
|
|
39
|
+
if (driver !== undefined) {
|
|
40
|
+
if (typeof driver !== "string" || !DIGEST_REF.test(driver))
|
|
41
|
+
return undefined;
|
|
42
|
+
result.driver = driver;
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
24
45
|
}
|
|
25
46
|
catch {
|
|
26
47
|
return undefined;
|
|
27
48
|
}
|
|
28
49
|
}
|
|
50
|
+
/** The digest-pinned image a worker Dockerfile's first FROM line names; undefined when it is not pinned. */
|
|
51
|
+
export function driverFromDockerfile(text) {
|
|
52
|
+
const image = /^FROM\s+(\S+)/im.exec(text)?.[1];
|
|
53
|
+
return image && /^[a-z0-9][a-z0-9._/:-]*@sha256:[0-9a-f]{64}$/.test(image) ? image : undefined;
|
|
54
|
+
}
|
|
55
|
+
function hasSource(packageRoot) {
|
|
56
|
+
return (existsSync(join(packageRoot, "deploy", "Dockerfile")) &&
|
|
57
|
+
existsSync(join(packageRoot, "src", "coding-worker", "Dockerfile")));
|
|
58
|
+
}
|
|
59
|
+
function sourceDriver(packageRoot) {
|
|
60
|
+
try {
|
|
61
|
+
return driverFromDockerfile(readFileSync(join(packageRoot, "src", "coding-worker", "Dockerfile"), "utf8"));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** `wardby doctor`'s line naming the base image for your own worker images; undefined when it is not known. */
|
|
68
|
+
export function baseImageLine(resolved) {
|
|
69
|
+
if ("unavailable" in resolved || !resolved.driver)
|
|
70
|
+
return undefined;
|
|
71
|
+
return `Base image for your own worker images: ${resolved.driver}`;
|
|
72
|
+
}
|
|
29
73
|
/**
|
|
30
74
|
* Where the coding step gets its runtime and worker images: explicit env
|
|
31
75
|
* overrides, the digest-pinned refs written at release time, or a local build
|
|
@@ -40,20 +84,38 @@ export function resolveQuickstartImages(opts) {
|
|
|
40
84
|
const workerImage = imageVariable(env.CODING_WORKER_IMAGE);
|
|
41
85
|
const claudeWorker = imageVariable(env.CODING_CLAUDE_WORKER_IMAGE);
|
|
42
86
|
const claudeToolRunner = imageVariable(env.CODING_CLAUDE_TOOL_RUNNER_IMAGE);
|
|
87
|
+
const workerNodePython = imageVariable(env.CODING_WORKER_IMAGE_NODE_PYTHON_3_12);
|
|
88
|
+
const claudeToolRunnerNodePython = imageVariable(env.CODING_CLAUDE_TOOL_RUNNER_IMAGE_NODE_PYTHON_3_12);
|
|
89
|
+
// Each Node + Python image is independent: an env override beats the package or local build.
|
|
90
|
+
const envNodePython = {
|
|
91
|
+
...(workerNodePython ? { workerNodePython } : {}),
|
|
92
|
+
...(claudeToolRunnerNodePython ? { claudeToolRunnerNodePython } : {}),
|
|
93
|
+
};
|
|
43
94
|
const envClaude = claudeWorker && claudeToolRunner ? { claudeWorker, claudeToolRunner } : {};
|
|
44
95
|
if (runtimeImage && workerImage) {
|
|
45
|
-
|
|
96
|
+
const driver = readPackageImages(packageRoot)?.driver ?? (hasSource(packageRoot) ? sourceDriver(packageRoot) : undefined);
|
|
97
|
+
return {
|
|
98
|
+
runtime: runtimeImage,
|
|
99
|
+
worker: workerImage,
|
|
100
|
+
...envClaude,
|
|
101
|
+
...envNodePython,
|
|
102
|
+
...(driver ? { driver } : {}),
|
|
103
|
+
source: "env",
|
|
104
|
+
};
|
|
46
105
|
}
|
|
47
106
|
const pinned = readPackageImages(packageRoot);
|
|
48
107
|
if (pinned)
|
|
49
|
-
return { ...pinned, ...envClaude, source: "package" };
|
|
50
|
-
if (
|
|
51
|
-
|
|
108
|
+
return { ...pinned, ...envClaude, ...envNodePython, source: "package" };
|
|
109
|
+
if (hasSource(packageRoot)) {
|
|
110
|
+
const driver = sourceDriver(packageRoot);
|
|
52
111
|
return {
|
|
53
112
|
runtime: "wardby-runtime:local",
|
|
54
113
|
worker: "wardby-coding-worker:local",
|
|
55
114
|
claudeWorker: "wardby-claude-coding-worker:local",
|
|
56
115
|
claudeToolRunner: "wardby-claude-tool-runner:local",
|
|
116
|
+
workerNodePython: "wardby-coding-worker-node-python:local",
|
|
117
|
+
claudeToolRunnerNodePython: "wardby-claude-tool-runner-node-python:local",
|
|
118
|
+
...(driver ? { driver } : {}),
|
|
57
119
|
source: "build",
|
|
58
120
|
};
|
|
59
121
|
}
|
|
@@ -5,8 +5,6 @@
|
|
|
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
|
-
/** The closing "build your first agent" hint; the assistant prompts only make sense once an MCP client is configured. */
|
|
9
|
-
export declare function nextStepLines(mcpConfigured: boolean): string[];
|
|
10
8
|
export declare function doctorCommand(args: string[]): Promise<void>;
|
|
11
9
|
export declare function statusCommand(args: string[]): Promise<void>;
|
|
12
10
|
export declare function logsCommand(args: string[]): Promise<void>;
|
package/dist/quickstart/index.js
CHANGED
|
@@ -9,6 +9,8 @@ import { composeProjectName, defaultModel, ensureWardbyIgnored, generatedSecret,
|
|
|
9
9
|
import { codingStep, quickstartComposeFiles } from "./coding.js";
|
|
10
10
|
import { prismaCatalogImages, prismaLocalAgents, prismaSeedCodingAgents } from "./coding-db.js";
|
|
11
11
|
import { codingDoctorLines } from "./coding-doctor.js";
|
|
12
|
+
import { baseImageLine, resolveQuickstartImages } from "./images.js";
|
|
13
|
+
import { scenarioMenuLines } from "./scenarios.js";
|
|
12
14
|
import { parseStarterChoice } from "./starter-services.js";
|
|
13
15
|
import { FETCH_NOTICE, migrateFailureReason, prismaMigrateInvocation, REGISTRY_FAILURE_MESSAGE, runPrismaMigrateInvocation, } from "./migrate.js";
|
|
14
16
|
const packageRoot = fileURLToPath(new URL("../../", import.meta.url));
|
|
@@ -351,6 +353,7 @@ export async function quickstartCommand(args) {
|
|
|
351
353
|
trust: { type: "string", multiple: true },
|
|
352
354
|
"coding-provider": { type: "string" },
|
|
353
355
|
"starter-services": { type: "string" },
|
|
356
|
+
"allow-repo-packages": { type: "boolean" },
|
|
354
357
|
provider: { type: "string" },
|
|
355
358
|
model: { type: "string" },
|
|
356
359
|
budget: { type: "string" },
|
|
@@ -367,6 +370,7 @@ export async function quickstartCommand(args) {
|
|
|
367
370
|
trust: values.trust ?? [],
|
|
368
371
|
provider: parseCodingProvider(values["coding-provider"]),
|
|
369
372
|
starterServices: values["starter-services"] === undefined ? undefined : parseStarterChoice(values["starter-services"]),
|
|
373
|
+
allowRepoPackages: values["allow-repo-packages"],
|
|
370
374
|
};
|
|
371
375
|
const skipDemo = values["skip-demo"] ?? false;
|
|
372
376
|
const budget = values.budget === undefined ? 1 : Number(values.budget);
|
|
@@ -439,28 +443,17 @@ export async function quickstartCommand(args) {
|
|
|
439
443
|
else
|
|
440
444
|
console.log(`Skipped the billed run. Start it later with: npx @wardby/cli@latest run ${DEMO_AGENT}`);
|
|
441
445
|
}
|
|
442
|
-
await codingStep(paths, state, codingOptions, codingDeps(paths));
|
|
446
|
+
const coding = await codingStep(paths, state, codingOptions, codingDeps(paths));
|
|
447
|
+
const codingRan = coding?.seed !== undefined && coding.seed.builder.status !== "skipped" && coding.seed.reviewer.status !== "skipped";
|
|
443
448
|
const client = parseClient(values.client) ?? (await chooseClient(nonInteractive));
|
|
444
449
|
const mcpConfigured = client !== "none" && configureMcpClients(client, paths);
|
|
445
450
|
console.log("\nWardby is ready.");
|
|
446
451
|
console.log(" npx @wardby/cli@latest status");
|
|
447
452
|
console.log(" npx @wardby/cli@latest doctor");
|
|
448
453
|
console.log(" npx @wardby/cli@latest down");
|
|
449
|
-
for (const line of
|
|
454
|
+
for (const line of scenarioMenuLines({ codingRan, mcpConfigured }))
|
|
450
455
|
console.log(line);
|
|
451
456
|
}
|
|
452
|
-
/** The closing "build your first agent" hint; the assistant prompts only make sense once an MCP client is configured. */
|
|
453
|
-
export function nextStepLines(mcpConfigured) {
|
|
454
|
-
const guide = "the guide: npx @wardby/cli@latest help open agent-recipes";
|
|
455
|
-
const lines = ["", "Next: build your first agent."];
|
|
456
|
-
if (mcpConfigured) {
|
|
457
|
-
lines.push("Ask your assistant one of:", ' "Set up the Wardby architecture keeper for this repository"', ' "Set up a Wardby builder for this repository"', `Or read ${guide}`);
|
|
458
|
-
}
|
|
459
|
-
else {
|
|
460
|
-
lines.push(`Read ${guide}`);
|
|
461
|
-
}
|
|
462
|
-
return lines;
|
|
463
|
-
}
|
|
464
457
|
async function databaseHealthy(paths) {
|
|
465
458
|
const env = readQuickstartEnv(paths);
|
|
466
459
|
if (!env.DATABASE_URL)
|
|
@@ -527,6 +520,9 @@ export async function doctorCommand(args) {
|
|
|
527
520
|
failed ||= line.startsWith("✗");
|
|
528
521
|
console.log(line);
|
|
529
522
|
}
|
|
523
|
+
const base = baseImageLine(resolveQuickstartImages({ env: runtimeEnv(paths), packageRoot }));
|
|
524
|
+
if (base)
|
|
525
|
+
console.log(base);
|
|
530
526
|
if (failed)
|
|
531
527
|
process.exitCode = 1;
|
|
532
528
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure readers for a repository's declared dependencies, used by the
|
|
3
|
+
* quickstart to offer them as local-builder's package allowlist:
|
|
4
|
+
* `pyproject.toml`, `requirements*.txt` and `package.json`. Each returns bare
|
|
5
|
+
* top-level names as written (a Python requirement keeps the extras it names,
|
|
6
|
+
* also as written: `psycopg[binary]`); repo-packages.ts validates, normalizes,
|
|
7
|
+
* de-duplicates and caps them.
|
|
8
|
+
*
|
|
9
|
+
* pyproject.toml is read by a deliberately narrow TOML scanner (no new
|
|
10
|
+
* dependency): it understands tables, arrays of tables, dotted and quoted
|
|
11
|
+
* keys, all four string forms, arrays and inline tables, and treats every
|
|
12
|
+
* other scalar (numbers, booleans, dates) as opaque. Anything it cannot scan
|
|
13
|
+
* throws, and the caller skips the file with a warning.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* The project name of a PEP 508 requirement ("requests[socks]>=2; python_version<'4'"),
|
|
17
|
+
* or null for anything that is not a plain registry requirement: a direct URL
|
|
18
|
+
* reference (`name @ url`), a path, a URL, or an archive file.
|
|
19
|
+
*/
|
|
20
|
+
export declare function requirementName(spec: string): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* A PEP 508 requirement's name with the extras it names, as written
|
|
23
|
+
* ("psycopg[binary]" from "psycopg[binary]>=3.2"), or just the name when it
|
|
24
|
+
* names none; null when requirementName is. The extras are not validated here.
|
|
25
|
+
*/
|
|
26
|
+
export declare function requirementSpec(spec: string): string | null;
|
|
27
|
+
/** Names from a pip requirements file; options (`-r`, `-c`, `-e`, `--…`), URLs and paths are skipped. */
|
|
28
|
+
export declare function requirementsNames(text: string): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Names from package.json's dependencies, devDependencies and
|
|
31
|
+
* optionalDependencies (peer dependencies are the consumer's to install).
|
|
32
|
+
* An `npm:` alias names the real package; file, link, workspace, git, GitHub
|
|
33
|
+
* and URL specs are not registry packages and are skipped.
|
|
34
|
+
*/
|
|
35
|
+
export declare function packageJsonNames(text: string): string[];
|
|
36
|
+
/** package.json's own `name`, when it is a string: the quickstart never offers
|
|
37
|
+
* the repository's own package for its allowlist (dependency confusion). */
|
|
38
|
+
export declare function packageJsonOwnNames(text: string): string[];
|
|
39
|
+
/**
|
|
40
|
+
* Names from pyproject.toml: `[project] dependencies`, every
|
|
41
|
+
* `[project.optional-dependencies]` group, `[tool.poetry.dependencies]`
|
|
42
|
+
* (except python), `[tool.poetry.group.*.dependencies]` and the legacy
|
|
43
|
+
* `[tool.poetry.dev-dependencies]`, plus the packages pip needs to build the
|
|
44
|
+
* project: `[build-system] requires`, or setuptools and wheel when the file has
|
|
45
|
+
* no `[build-system]` table. Throws when the file cannot be scanned or
|
|
46
|
+
* one of those keys has an unexpected shape.
|
|
47
|
+
*/
|
|
48
|
+
export declare function pyprojectNames(text: string): string[];
|
|
49
|
+
/**
|
|
50
|
+
* The project's own names in pyproject.toml (`[project].name` and
|
|
51
|
+
* `[tool.poetry].name`), as written: the quickstart never offers the
|
|
52
|
+
* repository's own project for its allowlist (dependency confusion), even when
|
|
53
|
+
* an optional-dependency group refers back to it with extras. Throws when the
|
|
54
|
+
* file cannot be scanned.
|
|
55
|
+
*/
|
|
56
|
+
export declare function pyprojectOwnNames(text: string): string[];
|