@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.
Files changed (68) hide show
  1. package/dist/coding/local-git.js +9 -1
  2. package/dist/coding/registry/pypi.d.ts +25 -1
  3. package/dist/coding/registry/pypi.js +140 -16
  4. package/dist/coding/registry/types.d.ts +17 -3
  5. package/dist/config/providers.d.ts +3 -2
  6. package/dist/core/http-runtime.js +13 -6
  7. package/dist/help-index.json +207 -15
  8. package/dist/mcp/auth/ownership.d.ts +1 -1
  9. package/dist/mcp/tools/agents.js +1 -1
  10. package/dist/providers/coding-proxy/mock-upstream.d.ts +33 -0
  11. package/dist/providers/coding-proxy/mock-upstream.js +116 -0
  12. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +3 -0
  13. package/dist/providers/coding-proxy/registry/prisma-store.js +17 -0
  14. package/dist/providers/coding-proxy/registry/service.d.ts +4 -0
  15. package/dist/providers/coding-proxy/registry/service.js +20 -3
  16. package/dist/providers/coding-proxy/registry/store.d.ts +18 -1
  17. package/dist/providers/coding-proxy/registry/store.js +33 -0
  18. package/dist/providers/coding-proxy/runtime.js +12 -1
  19. package/dist/providers/coding-proxy/types.d.ts +2 -0
  20. package/dist/providers/jobs/kubernetes-isolation.d.ts +13 -0
  21. package/dist/providers/jobs/kubernetes-isolation.js +52 -5
  22. package/dist/providers/jobs/kubernetes.d.ts +20 -6
  23. package/dist/providers/jobs/kubernetes.js +43 -32
  24. package/dist/providers/llm/openai.js +0 -1
  25. package/dist/quickstart/coding-db.js +14 -2
  26. package/dist/quickstart/coding-doctor.d.ts +3 -0
  27. package/dist/quickstart/coding-doctor.js +3 -0
  28. package/dist/quickstart/coding-images.d.ts +9 -0
  29. package/dist/quickstart/coding-images.js +39 -0
  30. package/dist/quickstart/coding-seed.d.ts +13 -1
  31. package/dist/quickstart/coding-seed.js +25 -7
  32. package/dist/quickstart/coding.d.ts +2 -0
  33. package/dist/quickstart/coding.js +75 -3
  34. package/dist/quickstart/config.d.ts +2 -0
  35. package/dist/quickstart/config.js +4 -0
  36. package/dist/quickstart/images.d.ts +11 -0
  37. package/dist/quickstart/images.js +74 -12
  38. package/dist/quickstart/index.d.ts +0 -2
  39. package/dist/quickstart/index.js +10 -14
  40. package/dist/quickstart/manifests.d.ts +56 -0
  41. package/dist/quickstart/manifests.js +523 -0
  42. package/dist/quickstart/python-detect.d.ts +10 -0
  43. package/dist/quickstart/python-detect.js +32 -0
  44. package/dist/quickstart/repo-packages.d.ts +10 -0
  45. package/dist/quickstart/repo-packages.js +132 -0
  46. package/dist/quickstart/reviewer-prompt.d.ts +12 -0
  47. package/dist/quickstart/reviewer-prompt.js +84 -0
  48. package/dist/quickstart/scenarios.d.ts +19 -0
  49. package/dist/quickstart/scenarios.js +28 -0
  50. package/dist/quickstart/sync-reviewer-prompt.d.ts +1 -0
  51. package/dist/quickstart/sync-reviewer-prompt.js +10 -0
  52. package/dist/quickstart-images.json +1 -1
  53. package/dist/viewer/api-schema.d.ts +4 -4
  54. package/docs/coding-agent-setup.md +17 -0
  55. package/docs/coding-packages.md +101 -3
  56. package/docs/coding-worker-byo-images.md +47 -2
  57. package/docs/coding-worker-isolation.md +20 -9
  58. package/docs/getting-started.md +139 -11
  59. package/docs/knowledge.md +11 -0
  60. package/help/architecture-agent.md +136 -0
  61. package/help/build-worker-image.md +198 -0
  62. package/help/coding-packages.md +31 -2
  63. package/help/creating-agents.md +14 -0
  64. package/help/deploy-gke.md +29 -5
  65. package/help/deployment-targets.md +3 -3
  66. package/help/getting-started.md +19 -0
  67. package/help/local-repositories.md +36 -5
  68. 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
- if (claudeWorker === undefined && claudeToolRunner === undefined)
18
- return { runtime, worker };
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
- return { runtime, worker, claudeWorker, claudeToolRunner };
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
- return { runtime: runtimeImage, worker: workerImage, ...envClaude, source: "env" };
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 (existsSync(join(packageRoot, "deploy", "Dockerfile")) &&
51
- existsSync(join(packageRoot, "src", "coding-worker", "Dockerfile"))) {
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>;
@@ -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 nextStepLines(mcpConfigured))
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[];