@kradle/cli 0.0.17 → 0.2.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.
Files changed (62) hide show
  1. package/README.md +93 -65
  2. package/dist/commands/agent/list.d.ts +4 -0
  3. package/dist/commands/agent/list.js +6 -4
  4. package/dist/commands/challenge/build.d.ts +9 -1
  5. package/dist/commands/challenge/build.js +40 -12
  6. package/dist/commands/challenge/create.d.ts +5 -1
  7. package/dist/commands/challenge/create.js +17 -18
  8. package/dist/commands/challenge/delete.d.ts +4 -1
  9. package/dist/commands/challenge/delete.js +5 -5
  10. package/dist/commands/challenge/list.d.ts +5 -0
  11. package/dist/commands/challenge/list.js +11 -10
  12. package/dist/commands/challenge/run.d.ts +8 -1
  13. package/dist/commands/challenge/run.js +13 -8
  14. package/dist/commands/challenge/watch.d.ts +4 -1
  15. package/dist/commands/challenge/watch.js +8 -8
  16. package/dist/commands/{evaluation → experiment}/create.d.ts +4 -0
  17. package/dist/commands/{evaluation → experiment}/create.js +22 -21
  18. package/dist/commands/{evaluation → experiment}/list.js +17 -19
  19. package/dist/commands/experiment/recordings.d.ts +19 -0
  20. package/dist/commands/experiment/recordings.js +416 -0
  21. package/dist/commands/experiment/run.d.ts +17 -0
  22. package/dist/commands/experiment/run.js +67 -0
  23. package/dist/commands/init.js +2 -2
  24. package/dist/lib/api-client.d.ts +51 -10
  25. package/dist/lib/api-client.js +108 -39
  26. package/dist/lib/arguments.d.ts +3 -2
  27. package/dist/lib/arguments.js +5 -3
  28. package/dist/lib/challenge.d.ts +13 -18
  29. package/dist/lib/challenge.js +58 -62
  30. package/dist/lib/experiment/experimenter.d.ts +92 -0
  31. package/dist/lib/experiment/experimenter.js +368 -0
  32. package/dist/lib/{evaluation → experiment}/index.d.ts +1 -1
  33. package/dist/lib/{evaluation → experiment}/index.js +1 -1
  34. package/dist/lib/{evaluation → experiment}/runner.d.ts +2 -0
  35. package/dist/lib/{evaluation → experiment}/runner.js +21 -2
  36. package/dist/lib/{evaluation → experiment}/tui.d.ts +1 -1
  37. package/dist/lib/{evaluation → experiment}/tui.js +3 -3
  38. package/dist/lib/{evaluation → experiment}/types.d.ts +10 -4
  39. package/dist/lib/{evaluation → experiment}/types.js +5 -3
  40. package/dist/lib/flags.d.ts +47 -0
  41. package/dist/lib/flags.js +63 -0
  42. package/dist/lib/schemas.d.ts +63 -2
  43. package/dist/lib/schemas.js +27 -1
  44. package/dist/lib/utils.d.ts +9 -10
  45. package/dist/lib/utils.js +12 -12
  46. package/oclif.manifest.json +423 -64
  47. package/package.json +11 -8
  48. package/static/challenge.ts +12 -13
  49. package/static/experiment_template.ts +114 -0
  50. package/static/project_template/dev.env +5 -5
  51. package/static/project_template/prod.env +4 -4
  52. package/static/project_template/tsconfig.json +1 -1
  53. package/dist/commands/challenge/multi-upload.d.ts +0 -6
  54. package/dist/commands/challenge/multi-upload.js +0 -80
  55. package/dist/commands/evaluation/run.d.ts +0 -13
  56. package/dist/commands/evaluation/run.js +0 -61
  57. package/dist/lib/config.d.ts +0 -12
  58. package/dist/lib/config.js +0 -49
  59. package/dist/lib/evaluation/evaluator.d.ts +0 -88
  60. package/dist/lib/evaluation/evaluator.js +0 -268
  61. package/static/evaluation_template.ts +0 -69
  62. /package/dist/commands/{evaluation → experiment}/list.d.ts +0 -0
@@ -3,8 +3,8 @@ import pc from "picocolors";
3
3
  import { ApiClient } from "../../lib/api-client.js";
4
4
  import { getChallengeSlugArgument } from "../../lib/arguments.js";
5
5
  import { Challenge } from "../../lib/challenge.js";
6
- import { loadConfig } from "../../lib/config.js";
7
- import { loadTemplateRun } from "../../lib/utils.js";
6
+ import { getConfigFlags } from "../../lib/flags.js";
7
+ import { loadTemplateRun, openInBrowser } from "../../lib/utils.js";
8
8
  export default class Run extends Command {
9
9
  static description = "Run a challenge";
10
10
  static examples = [
@@ -12,16 +12,18 @@ export default class Run extends Command {
12
12
  "<%= config.bin %> <%= command.id %> my-challenge --studio",
13
13
  ];
14
14
  static args = {
15
- challenge: getChallengeSlugArgument({ description: "Challenge slug to run" }),
15
+ challengeSlug: getChallengeSlugArgument({ description: "Challenge slug to run" }),
16
16
  };
17
17
  static flags = {
18
18
  studio: Flags.boolean({ char: "s", description: "Run in studio environment", default: false }),
19
+ open: Flags.boolean({ char: "o", description: "Open the run URL in the browser", default: false }),
20
+ ...getConfigFlags("api-key", "api-url", "challenges-path", "web-url", "studio-url", "studio-api-url"),
19
21
  };
20
22
  async run() {
21
23
  const { args, flags } = await this.parse(Run);
22
- const config = loadConfig();
23
- const api = new ApiClient(config);
24
- const challenge = new Challenge(args.challenge, config);
24
+ const apiUrl = flags.studio ? flags["studio-api-url"] : flags["api-url"];
25
+ const studioApi = new ApiClient(apiUrl, flags["api-key"], flags.studio);
26
+ const challenge = new Challenge(args.challengeSlug, flags["challenges-path"]);
25
27
  try {
26
28
  const { participants } = (await loadTemplateRun());
27
29
  const template = {
@@ -29,12 +31,15 @@ export default class Run extends Command {
29
31
  participants,
30
32
  };
31
33
  this.log(pc.blue(`>> Running challenge: ${challenge.shortSlug}${flags.studio ? " (studio)" : ""}...`));
32
- const response = await api.runChallenge(template, flags.studio);
34
+ const response = await studioApi.runChallenge(template);
33
35
  if (response.runIds && response.runIds.length > 0) {
34
- const baseUrl = flags.studio ? config.STUDIO_URL : config.WEB_URL;
36
+ const baseUrl = flags.studio ? flags["studio-url"] : flags["web-url"];
35
37
  const runUrl = `${baseUrl}/runs/${response.runIds[0]}`;
36
38
  this.log(pc.green("\n✓ Challenge started!"));
37
39
  this.log(pc.dim(`Run URL: ${runUrl}`));
40
+ if (flags.open) {
41
+ await openInBrowser(runUrl);
42
+ }
38
43
  }
39
44
  else {
40
45
  this.log(pc.yellow("⚠ Challenge started but no run ID returned"));
@@ -3,10 +3,13 @@ export default class Watch extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
+ "api-key": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ "api-url": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ "challenges-path": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
6
9
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
10
  };
8
11
  static args: {
9
- challenge: import("@oclif/core/interfaces").Arg<string>;
12
+ challengeSlug: import("@oclif/core/interfaces").Arg<string>;
10
13
  };
11
14
  run(): Promise<void>;
12
15
  }
@@ -6,27 +6,27 @@ import pc from "picocolors";
6
6
  import { ApiClient } from "../../lib/api-client.js";
7
7
  import { getChallengeSlugArgument } from "../../lib/arguments.js";
8
8
  import { Challenge, SOURCE_FOLDER } from "../../lib/challenge.js";
9
- import { loadConfig } from "../../lib/config.js";
9
+ import { getConfigFlags } from "../../lib/flags.js";
10
10
  import { clearScreen, debounced } from "../../lib/utils.js";
11
11
  export default class Watch extends Command {
12
12
  static description = "Watch a challenge for changes and auto-rebuild/upload";
13
13
  static examples = ["<%= config.bin %> <%= command.id %> my-challenge"];
14
14
  static flags = {
15
15
  verbose: Flags.boolean({ description: "Enable verbose output", default: false }),
16
+ ...getConfigFlags("api-key", "api-url", "challenges-path"),
16
17
  };
17
18
  static args = {
18
- challenge: getChallengeSlugArgument({ description: "Challenge slug to watch" }),
19
+ challengeSlug: getChallengeSlugArgument({ description: "Challenge slug to watch" }),
19
20
  };
20
21
  async run() {
21
22
  const { args, flags } = await this.parse(Watch);
22
- const config = loadConfig();
23
- const challenge = new Challenge(args.challenge, config);
24
- const api = new ApiClient(config);
23
+ const challenge = new Challenge(args.challengeSlug, flags["challenges-path"]);
24
+ const api = new ApiClient(flags["api-url"], flags["api-key"]);
25
25
  const debounceSeconds = 1;
26
26
  let building = false;
27
27
  // Perform the initial build and upload
28
28
  this.log(pc.blue(`Performing initial build and upload...`));
29
- let { config: lastConfig, datapackHash: lastHash } = await challenge.buildAndUpload(api);
29
+ let { config: lastConfig, datapackHash: lastHash } = await challenge.buildAndUpload(api, false);
30
30
  this.log(pc.green(`✓ Initial build and upload complete`));
31
31
  const executeRebuild = async () => {
32
32
  building = true;
@@ -40,7 +40,7 @@ export default class Watch extends Command {
40
40
  const newConfig = await challenge.loadConfig();
41
41
  if (JSON.stringify(newConfig) !== JSON.stringify(lastConfig)) {
42
42
  task.title = "Uploading configuration";
43
- await api.updateChallenge(challenge, newConfig);
43
+ await api.updateChallenge(challenge.shortSlug, newConfig, "private");
44
44
  lastConfig = newConfig;
45
45
  task.title = "Configuration uploaded";
46
46
  }
@@ -63,7 +63,7 @@ export default class Watch extends Command {
63
63
  const newHash = await challenge.getDatapackHash();
64
64
  if (newHash !== lastHash) {
65
65
  task.title = "Uploading datapack";
66
- await challenge.upload(api);
66
+ await api.uploadChallengeDatapack(challenge.shortSlug, challenge.tarballPath);
67
67
  lastHash = newHash;
68
68
  task.title = "Datapack uploaded";
69
69
  }
@@ -5,5 +5,9 @@ export default class Create extends Command {
5
5
  static args: {
6
6
  name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
7
  };
8
+ static flags: {
9
+ "api-key": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ "api-url": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
8
12
  run(): Promise<void>;
9
13
  }
@@ -5,35 +5,36 @@ import { Args, Command } from "@oclif/core";
5
5
  import enquirer from "enquirer";
6
6
  import pc from "picocolors";
7
7
  import { ApiClient } from "../../lib/api-client.js";
8
- import { loadConfig } from "../../lib/config.js";
8
+ import { getConfigFlags } from "../../lib/flags.js";
9
9
  import { getStaticResourcePath } from "../../lib/utils.js";
10
10
  export default class Create extends Command {
11
- static description = "Create a new evaluation";
12
- static examples = ["<%= config.bin %> <%= command.id %> my-evaluation"];
11
+ static description = "Create a new experiment";
12
+ static examples = ["<%= config.bin %> <%= command.id %> my-experiment"];
13
13
  static args = {
14
14
  name: Args.string({
15
- description: "Name of the evaluation",
15
+ description: "Name of the experiment",
16
16
  required: true,
17
17
  }),
18
18
  };
19
+ static flags = {
20
+ ...getConfigFlags("api-key", "api-url"),
21
+ };
19
22
  async run() {
20
- const { args } = await this.parse(Create);
21
- loadConfig(); // Validate config is available
22
- const evaluationDir = path.resolve(process.cwd(), "evaluations", args.name);
23
- const configPath = path.join(evaluationDir, "config.ts");
24
- // Check if evaluation already exists
23
+ const { args, flags } = await this.parse(Create);
24
+ const experimentDir = path.resolve(process.cwd(), "experiments", args.name);
25
+ const configPath = path.join(experimentDir, "config.ts");
26
+ // Check if experiment already exists
25
27
  try {
26
- await fs.access(evaluationDir);
27
- this.error(pc.red(`Evaluation '${args.name}' already exists at ${evaluationDir}`));
28
+ await fs.access(experimentDir);
29
+ this.error(pc.red(`Experiment '${args.name}' already exists at ${experimentDir}`));
28
30
  }
29
31
  catch {
30
32
  // Directory doesn't exist, which is what we want
31
33
  }
32
- // Create evaluation directory
33
- await fs.mkdir(evaluationDir, { recursive: true });
34
- // Ask for the slug of the challenge to evaluate
35
- const config = loadConfig();
36
- const api = new ApiClient(config);
34
+ // Create experiment directory
35
+ await fs.mkdir(experimentDir, { recursive: true });
36
+ // Ask for the slug of the challenge to run the experiment on
37
+ const api = new ApiClient(flags["api-url"], flags["api-key"]);
37
38
  const [kradleChallenges, cloudChallenges] = await Promise.all([api.listKradleChallenges(), api.listChallenges()]);
38
39
  const choices = [...kradleChallenges, ...cloudChallenges]
39
40
  .map((c) => c.slug)
@@ -45,15 +46,15 @@ export default class Create extends Command {
45
46
  const response = await enquirer.prompt({
46
47
  type: "select",
47
48
  name: "challenge",
48
- message: "Select the challenge to evaluate",
49
+ message: "Select the challenge for this experiment",
49
50
  choices: choices,
50
51
  });
51
52
  // Read template file and fill in the challenge slug, then write to config file
52
- const templatePath = getStaticResourcePath("evaluation_template.ts");
53
+ const templatePath = getStaticResourcePath("experiment_template.ts");
53
54
  const template = await fs.readFile(templatePath, "utf-8");
54
55
  const filledTemplate = template.replace("[INSERT CHALLENGE SLUG HERE]", response.challenge);
55
56
  await fs.writeFile(configPath, filledTemplate);
56
- this.log(pc.green(`✓ Created evaluation '${args.name}'`));
57
+ this.log(pc.green(`✓ Created experiment '${args.name}'`));
57
58
  this.log(pc.dim(` Config: ${configPath}`));
58
59
  // Offer to open in editor on macOS
59
60
  if (process.platform === "darwin") {
@@ -73,7 +74,7 @@ export default class Create extends Command {
73
74
  }
74
75
  this.log("");
75
76
  this.log(pc.blue(">> Next steps:"));
76
- this.log(pc.dim(` 1. Edit ${path.basename(configPath)} to define your evaluation runs`));
77
- this.log(pc.dim(` 2. Run: kradle evaluation run ${args.name}`));
77
+ this.log(pc.dim(` 1. Edit ${path.basename(configPath)} to define your experiment runs`));
78
+ this.log(pc.dim(` 2. Run: kradle experiment run ${args.name}`));
78
79
  }
79
80
  }
@@ -2,29 +2,27 @@ import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { Command } from "@oclif/core";
4
4
  import pc from "picocolors";
5
- import { loadConfig } from "../../lib/config.js";
6
5
  export default class List extends Command {
7
- static description = "List all evaluations";
6
+ static description = "List all experiments";
8
7
  static examples = ["<%= config.bin %> <%= command.id %>"];
9
8
  async run() {
10
- this.parse(List);
11
- loadConfig(); // Validate config is available
12
- const evaluationsDir = path.resolve(process.cwd(), "evaluations");
9
+ await this.parse(List);
10
+ const experimentsDir = path.resolve(process.cwd(), "experiments");
13
11
  try {
14
- const entries = await fs.readdir(evaluationsDir, { withFileTypes: true });
15
- const evaluations = entries.filter((e) => e.isDirectory());
16
- if (evaluations.length === 0) {
17
- this.log(pc.yellow("No evaluations found."));
18
- this.log(pc.dim(` Run 'kradle evaluation init <name>' to create one.`));
12
+ const entries = await fs.readdir(experimentsDir, { withFileTypes: true });
13
+ const experiments = entries.filter((e) => e.isDirectory());
14
+ if (experiments.length === 0) {
15
+ this.log(pc.yellow("No experiments found."));
16
+ this.log(pc.dim(` Run 'kradle experiment create <name>' to create one.`));
19
17
  return;
20
18
  }
21
- this.log(pc.blue(">> Evaluations:"));
19
+ this.log(pc.blue(">> Experiments:"));
22
20
  this.log("");
23
- for (const evaluation of evaluations) {
24
- const evalDir = path.join(evaluationsDir, evaluation.name);
25
- const hasConfig = await this.fileExists(path.join(evalDir, "config.ts"));
26
- const hasManifest = await this.fileExists(path.join(evalDir, "manifest.json"));
27
- const hasProgress = await this.fileExists(path.join(evalDir, "progress.json"));
21
+ for (const experiment of experiments) {
22
+ const expDir = path.join(experimentsDir, experiment.name);
23
+ const hasConfig = await this.fileExists(path.join(expDir, "config.ts"));
24
+ const hasManifest = await this.fileExists(path.join(expDir, "manifest.json"));
25
+ const hasProgress = await this.fileExists(path.join(expDir, "progress.json"));
28
26
  let status = "";
29
27
  if (hasProgress) {
30
28
  status = pc.yellow(" (in progress)");
@@ -35,12 +33,12 @@ export default class List extends Command {
35
33
  else if (hasConfig) {
36
34
  status = pc.dim(" (config only)");
37
35
  }
38
- this.log(` ${pc.bold(evaluation.name)}${status}`);
36
+ this.log(` ${pc.bold(experiment.name)}${status}`);
39
37
  }
40
38
  }
41
39
  catch {
42
- this.log(pc.yellow("No evaluations directory found."));
43
- this.log(pc.dim(` Run 'kradle evaluation init <name>' to create your first evaluation.`));
40
+ this.log(pc.yellow("No experiments directory found."));
41
+ this.log(pc.dim(` Run 'kradle experiment create <name>' to create your first experiment.`));
44
42
  }
45
43
  }
46
44
  async fileExists(filePath) {
@@ -0,0 +1,19 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class Recordings extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ experimentName: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ runId: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
8
+ };
9
+ static flags: {
10
+ "api-key": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ "api-url": import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ version: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ };
15
+ run(): Promise<void>;
16
+ private downloadForExperiment;
17
+ private fetchAndDownloadTargets;
18
+ private downloadRecordings;
19
+ }