@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.
- package/README.md +93 -65
- package/dist/commands/agent/list.d.ts +4 -0
- package/dist/commands/agent/list.js +6 -4
- package/dist/commands/challenge/build.d.ts +9 -1
- package/dist/commands/challenge/build.js +40 -12
- package/dist/commands/challenge/create.d.ts +5 -1
- package/dist/commands/challenge/create.js +17 -18
- package/dist/commands/challenge/delete.d.ts +4 -1
- package/dist/commands/challenge/delete.js +5 -5
- package/dist/commands/challenge/list.d.ts +5 -0
- package/dist/commands/challenge/list.js +11 -10
- package/dist/commands/challenge/run.d.ts +8 -1
- package/dist/commands/challenge/run.js +13 -8
- package/dist/commands/challenge/watch.d.ts +4 -1
- package/dist/commands/challenge/watch.js +8 -8
- package/dist/commands/{evaluation → experiment}/create.d.ts +4 -0
- package/dist/commands/{evaluation → experiment}/create.js +22 -21
- package/dist/commands/{evaluation → experiment}/list.js +17 -19
- package/dist/commands/experiment/recordings.d.ts +19 -0
- package/dist/commands/experiment/recordings.js +416 -0
- package/dist/commands/experiment/run.d.ts +17 -0
- package/dist/commands/experiment/run.js +67 -0
- package/dist/commands/init.js +2 -2
- package/dist/lib/api-client.d.ts +51 -10
- package/dist/lib/api-client.js +108 -39
- package/dist/lib/arguments.d.ts +3 -2
- package/dist/lib/arguments.js +5 -3
- package/dist/lib/challenge.d.ts +13 -18
- package/dist/lib/challenge.js +58 -62
- package/dist/lib/experiment/experimenter.d.ts +92 -0
- package/dist/lib/experiment/experimenter.js +368 -0
- package/dist/lib/{evaluation → experiment}/index.d.ts +1 -1
- package/dist/lib/{evaluation → experiment}/index.js +1 -1
- package/dist/lib/{evaluation → experiment}/runner.d.ts +2 -0
- package/dist/lib/{evaluation → experiment}/runner.js +21 -2
- package/dist/lib/{evaluation → experiment}/tui.d.ts +1 -1
- package/dist/lib/{evaluation → experiment}/tui.js +3 -3
- package/dist/lib/{evaluation → experiment}/types.d.ts +10 -4
- package/dist/lib/{evaluation → experiment}/types.js +5 -3
- package/dist/lib/flags.d.ts +47 -0
- package/dist/lib/flags.js +63 -0
- package/dist/lib/schemas.d.ts +63 -2
- package/dist/lib/schemas.js +27 -1
- package/dist/lib/utils.d.ts +9 -10
- package/dist/lib/utils.js +12 -12
- package/oclif.manifest.json +423 -64
- package/package.json +11 -8
- package/static/challenge.ts +12 -13
- package/static/experiment_template.ts +114 -0
- package/static/project_template/dev.env +5 -5
- package/static/project_template/prod.env +4 -4
- package/static/project_template/tsconfig.json +1 -1
- package/dist/commands/challenge/multi-upload.d.ts +0 -6
- package/dist/commands/challenge/multi-upload.js +0 -80
- package/dist/commands/evaluation/run.d.ts +0 -13
- package/dist/commands/evaluation/run.js +0 -61
- package/dist/lib/config.d.ts +0 -12
- package/dist/lib/config.js +0 -49
- package/dist/lib/evaluation/evaluator.d.ts +0 -88
- package/dist/lib/evaluation/evaluator.js +0 -268
- package/static/evaluation_template.ts +0 -69
- /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 {
|
|
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
|
-
|
|
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
|
|
23
|
-
const
|
|
24
|
-
const challenge = new Challenge(args.
|
|
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
|
|
34
|
+
const response = await studioApi.runChallenge(template);
|
|
33
35
|
if (response.runIds && response.runIds.length > 0) {
|
|
34
|
-
const baseUrl = flags.studio ?
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
|
23
|
-
const
|
|
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.
|
|
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 {
|
|
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
|
|
12
|
-
static examples = ["<%= config.bin %> <%= command.id %> my-
|
|
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
|
|
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
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
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(
|
|
27
|
-
this.error(pc.red(`
|
|
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
|
|
33
|
-
await fs.mkdir(
|
|
34
|
-
// Ask for the slug of the challenge to
|
|
35
|
-
const
|
|
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
|
|
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("
|
|
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
|
|
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
|
|
77
|
-
this.log(pc.dim(` 2. Run: kradle
|
|
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
|
|
6
|
+
static description = "List all experiments";
|
|
8
7
|
static examples = ["<%= config.bin %> <%= command.id %>"];
|
|
9
8
|
async run() {
|
|
10
|
-
this.parse(List);
|
|
11
|
-
|
|
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(
|
|
15
|
-
const
|
|
16
|
-
if (
|
|
17
|
-
this.log(pc.yellow("No
|
|
18
|
-
this.log(pc.dim(` Run 'kradle
|
|
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(">>
|
|
19
|
+
this.log(pc.blue(">> Experiments:"));
|
|
22
20
|
this.log("");
|
|
23
|
-
for (const
|
|
24
|
-
const
|
|
25
|
-
const hasConfig = await this.fileExists(path.join(
|
|
26
|
-
const hasManifest = await this.fileExists(path.join(
|
|
27
|
-
const hasProgress = await this.fileExists(path.join(
|
|
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(
|
|
36
|
+
this.log(` ${pc.bold(experiment.name)}${status}`);
|
|
39
37
|
}
|
|
40
38
|
}
|
|
41
39
|
catch {
|
|
42
|
-
this.log(pc.yellow("No
|
|
43
|
-
this.log(pc.dim(` Run 'kradle
|
|
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
|
+
}
|