@kradle/cli 0.0.2
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 +224 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +14 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +14 -0
- package/dist/commands/agent/list.d.ts +6 -0
- package/dist/commands/agent/list.js +20 -0
- package/dist/commands/challenge/build.d.ts +9 -0
- package/dist/commands/challenge/build.js +25 -0
- package/dist/commands/challenge/create.d.ts +12 -0
- package/dist/commands/challenge/create.js +87 -0
- package/dist/commands/challenge/delete.d.ts +12 -0
- package/dist/commands/challenge/delete.js +99 -0
- package/dist/commands/challenge/list.d.ts +6 -0
- package/dist/commands/challenge/list.js +48 -0
- package/dist/commands/challenge/multi-upload.d.ts +6 -0
- package/dist/commands/challenge/multi-upload.js +80 -0
- package/dist/commands/challenge/run.d.ts +12 -0
- package/dist/commands/challenge/run.js +47 -0
- package/dist/commands/challenge/watch.d.ts +12 -0
- package/dist/commands/challenge/watch.js +113 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.js +161 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/api-client.d.ts +55 -0
- package/dist/lib/api-client.js +162 -0
- package/dist/lib/arguments.d.ts +7 -0
- package/dist/lib/arguments.js +17 -0
- package/dist/lib/challenge.d.ts +67 -0
- package/dist/lib/challenge.js +203 -0
- package/dist/lib/config.d.ts +13 -0
- package/dist/lib/config.js +51 -0
- package/dist/lib/schemas.d.ts +127 -0
- package/dist/lib/schemas.js +55 -0
- package/dist/lib/utils.d.ts +89 -0
- package/dist/lib/utils.js +170 -0
- package/oclif.manifest.json +310 -0
- package/package.json +78 -0
- package/static/challenge.ts +32 -0
- package/static/project_template/dev.env +6 -0
- package/static/project_template/package.json +17 -0
- package/static/project_template/prod.env +6 -0
- package/static/project_template/template-run.json +10 -0
- package/static/project_template/tsconfig.json +17 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Args } from "@oclif/core";
|
|
2
|
+
const CHALLENGE_SLUG_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
3
|
+
/**
|
|
4
|
+
* Returns a "challenge slug" argument, and validates it to be a valid challenge slug.
|
|
5
|
+
*/
|
|
6
|
+
export function getChallengeSlugArgument({ description }) {
|
|
7
|
+
return Args.string({
|
|
8
|
+
description,
|
|
9
|
+
required: true,
|
|
10
|
+
parse: async (input) => {
|
|
11
|
+
if (!CHALLENGE_SLUG_REGEX.test(input)) {
|
|
12
|
+
throw new Error(`Invalid challenge slug: ${input}. Challenge slugs must be lowercase alphanumeric characters and hyphens, and must not start or end with a hyphen.`);
|
|
13
|
+
}
|
|
14
|
+
return input;
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { ApiClient } from "./api-client.js";
|
|
2
|
+
import type { Config } from "./config.js";
|
|
3
|
+
import { type ChallengeSchemaType } from "./schemas.js";
|
|
4
|
+
export declare class Challenge {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
private readonly config;
|
|
7
|
+
constructor(name: string, config: Config);
|
|
8
|
+
private get kradleChallengesPath();
|
|
9
|
+
/**
|
|
10
|
+
* Get the short slug without username prefix
|
|
11
|
+
* e.g., "florianernst:challenge" -> "challenge"
|
|
12
|
+
*/
|
|
13
|
+
get shortSlug(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Get the path to the challenge directory
|
|
16
|
+
*/
|
|
17
|
+
get challengeDir(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Get the path to the challenge TypeScript entrypoint file
|
|
20
|
+
*/
|
|
21
|
+
get challengePath(): string;
|
|
22
|
+
/**
|
|
23
|
+
* Get the path to the challenge config file
|
|
24
|
+
*/
|
|
25
|
+
get configPath(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Get the path to the datapack directory
|
|
28
|
+
*/
|
|
29
|
+
get datapackPath(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Get the path to the tarball file
|
|
32
|
+
*/
|
|
33
|
+
get tarballPath(): string;
|
|
34
|
+
/**
|
|
35
|
+
* Make sure the challenge exists locally and is valid by ensuring the challenge.ts file and the config.ts file exist.
|
|
36
|
+
*/
|
|
37
|
+
exists(): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Calculate a SHA256 hash of all files in the datapack directory
|
|
40
|
+
*/
|
|
41
|
+
getDatapackHash(): Promise<string>;
|
|
42
|
+
/**
|
|
43
|
+
* Build the challenge datapack
|
|
44
|
+
*/
|
|
45
|
+
build(silent?: boolean): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Load the challenge configuration from config.ts
|
|
48
|
+
*/
|
|
49
|
+
loadConfig(): Promise<ChallengeSchemaType>;
|
|
50
|
+
/**
|
|
51
|
+
* Upload the challenge datapack to Google Cloud Storage
|
|
52
|
+
*/
|
|
53
|
+
upload(apiClient: ApiClient): Promise<void>;
|
|
54
|
+
buildAndUpload(api: ApiClient): Promise<{
|
|
55
|
+
config: ChallengeSchemaType;
|
|
56
|
+
datapackHash: string;
|
|
57
|
+
}>;
|
|
58
|
+
/**
|
|
59
|
+
* Get all local challenges from the challenges directory
|
|
60
|
+
*/
|
|
61
|
+
static getLocalChallenges(): Promise<Record<string, boolean>>;
|
|
62
|
+
/**
|
|
63
|
+
* Create a new local challenge folder with challenge.ts template
|
|
64
|
+
* Note: config.ts is NOT created here - it should be generated later via `challenge config download`
|
|
65
|
+
*/
|
|
66
|
+
static createLocal(slug: string, config: Config): Promise<void>;
|
|
67
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
import * as tar from "tar";
|
|
7
|
+
import { ChallengeSchema } from "./schemas.js";
|
|
8
|
+
import { executeNodeCommand, executeTypescriptFile, getStaticResourcePath, readDirSorted } from "./utils.js";
|
|
9
|
+
export class Challenge {
|
|
10
|
+
name;
|
|
11
|
+
config;
|
|
12
|
+
constructor(name, config) {
|
|
13
|
+
this.name = name;
|
|
14
|
+
this.config = config;
|
|
15
|
+
}
|
|
16
|
+
get kradleChallengesPath() {
|
|
17
|
+
return path.resolve(this.config.KRADLE_CHALLENGES_PATH);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get the short slug without username prefix
|
|
21
|
+
* e.g., "florianernst:challenge" -> "challenge"
|
|
22
|
+
*/
|
|
23
|
+
get shortSlug() {
|
|
24
|
+
return this.name.includes(":") ? this.name.split(":")[1] : this.name;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get the path to the challenge directory
|
|
28
|
+
*/
|
|
29
|
+
get challengeDir() {
|
|
30
|
+
return path.resolve(process.cwd(), "challenges", this.shortSlug);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get the path to the challenge TypeScript entrypoint file
|
|
34
|
+
*/
|
|
35
|
+
get challengePath() {
|
|
36
|
+
return path.join(this.challengeDir, "challenge.ts");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get the path to the challenge config file
|
|
40
|
+
*/
|
|
41
|
+
get configPath() {
|
|
42
|
+
return path.join(this.challengeDir, "config.ts");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get the path to the datapack directory
|
|
46
|
+
*/
|
|
47
|
+
get datapackPath() {
|
|
48
|
+
return path.join(this.kradleChallengesPath, this.shortSlug);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get the path to the tarball file
|
|
52
|
+
*/
|
|
53
|
+
get tarballPath() {
|
|
54
|
+
return path.join(this.kradleChallengesPath, `${this.shortSlug}.tar.gz`);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Make sure the challenge exists locally and is valid by ensuring the challenge.ts file and the config.ts file exist.
|
|
58
|
+
*/
|
|
59
|
+
exists() {
|
|
60
|
+
if (!existsSync(this.challengePath)) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (!existsSync(this.configPath)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Calculate a SHA256 hash of all files in the datapack directory
|
|
70
|
+
*/
|
|
71
|
+
async getDatapackHash() {
|
|
72
|
+
const datapackDir = path.join(this.datapackPath, "datapack");
|
|
73
|
+
if (!existsSync(datapackDir)) {
|
|
74
|
+
throw new Error(`Datapack not found at ${datapackDir}`);
|
|
75
|
+
}
|
|
76
|
+
const files = await readDirSorted(datapackDir);
|
|
77
|
+
const hash = crypto.createHash("sha256");
|
|
78
|
+
for (const file of files) {
|
|
79
|
+
if (file.isFile()) {
|
|
80
|
+
const filePath = path.join(file.parentPath, file.name);
|
|
81
|
+
const content = await fs.readFile(filePath);
|
|
82
|
+
hash.update(content);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return hash.digest("hex");
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Build the challenge datapack
|
|
89
|
+
*/
|
|
90
|
+
async build(silent = false) {
|
|
91
|
+
// Verify that the challenge file exists
|
|
92
|
+
if (!existsSync(this.challengePath)) {
|
|
93
|
+
throw new Error(`Challenge file not found at ${this.challengePath}`);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
await executeTypescriptFile(this.challengePath, this.config, { silent });
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
throw new Error(`Failed to build datapack: ${error instanceof Error ? error.message : error}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Load the challenge configuration from config.ts
|
|
104
|
+
*/
|
|
105
|
+
async loadConfig() {
|
|
106
|
+
if (!existsSync(this.configPath)) {
|
|
107
|
+
throw new Error(`Config file not found at ${this.configPath}`);
|
|
108
|
+
}
|
|
109
|
+
// We spawn a new NodeJS process to execute & log the config file.
|
|
110
|
+
// We can't directly import the file because it would be cached, and import cache can't be invalidated.
|
|
111
|
+
const stdout = await executeNodeCommand([
|
|
112
|
+
"--experimental-transform-types",
|
|
113
|
+
"--no-warnings",
|
|
114
|
+
"-e",
|
|
115
|
+
`console.log(JSON.stringify(require("${this.configPath}").config));`,
|
|
116
|
+
], this.config);
|
|
117
|
+
return ChallengeSchema.parse(JSON.parse(stdout));
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Upload the challenge datapack to Google Cloud Storage
|
|
121
|
+
*/
|
|
122
|
+
async upload(apiClient) {
|
|
123
|
+
// Create tarball
|
|
124
|
+
// We need to use the cwd option to make sure the structure of the tarball uses paths relative to the challenge directory
|
|
125
|
+
await tar.create({
|
|
126
|
+
"no-mtime": true,
|
|
127
|
+
file: this.tarballPath, // Output file name
|
|
128
|
+
gzip: true,
|
|
129
|
+
cwd: this.datapackPath, // Input directory
|
|
130
|
+
strict: true,
|
|
131
|
+
}, ["datapack"]);
|
|
132
|
+
const uploadUrl = await apiClient.getChallengeUploadUrl(this);
|
|
133
|
+
const fileBuffer = await fs.readFile(this.tarballPath);
|
|
134
|
+
const response = await fetch(uploadUrl, {
|
|
135
|
+
method: "PUT",
|
|
136
|
+
headers: {
|
|
137
|
+
"Content-Type": "application/gzip",
|
|
138
|
+
"Content-Length": fileBuffer.length.toString(),
|
|
139
|
+
},
|
|
140
|
+
body: fileBuffer,
|
|
141
|
+
});
|
|
142
|
+
if (!response.ok) {
|
|
143
|
+
throw new Error(`Failed to upload datapack: ${response.statusText}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async buildAndUpload(api) {
|
|
147
|
+
// Ensure challenge exists locally
|
|
148
|
+
if (!this.exists()) {
|
|
149
|
+
throw new Error(`Challenge "${this.name}" does not exist locally. Make sure both the challenge.ts file and the config.ts file exist.`);
|
|
150
|
+
}
|
|
151
|
+
// Ensure challenge exists in the cloud
|
|
152
|
+
if (!(await api.challengeExists(this.name))) {
|
|
153
|
+
console.log(pc.yellow(`Challenge not found in cloud: ${this.name}`));
|
|
154
|
+
console.log(pc.yellow(`Creating challenge: ${this.name}`));
|
|
155
|
+
await api.createChallenge(this.name);
|
|
156
|
+
console.log(pc.green(`✓ Challenge created in cloud`));
|
|
157
|
+
}
|
|
158
|
+
// Load config
|
|
159
|
+
const config = await this.loadConfig();
|
|
160
|
+
// Upload config
|
|
161
|
+
console.log(pc.blue(`>> Uploading config: ${this.shortSlug}`));
|
|
162
|
+
await api.updateChallenge(this, config);
|
|
163
|
+
console.log(pc.green(`✓ Config uploaded`));
|
|
164
|
+
// Build and upload datapack
|
|
165
|
+
console.log(pc.blue(`>> Building datapack: ${this.shortSlug}`));
|
|
166
|
+
await this.build();
|
|
167
|
+
console.log(pc.blue(`>> Uploading datapack: ${this.shortSlug}`));
|
|
168
|
+
await this.upload(api);
|
|
169
|
+
console.log(pc.green(`✓ Build & upload complete for ${this.shortSlug}`));
|
|
170
|
+
const datapackHash = await this.getDatapackHash();
|
|
171
|
+
return { config, datapackHash };
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get all local challenges from the challenges directory
|
|
175
|
+
*/
|
|
176
|
+
static async getLocalChallenges() {
|
|
177
|
+
const challengesDir = path.resolve(process.cwd(), "challenges");
|
|
178
|
+
const entries = await readDirSorted(challengesDir);
|
|
179
|
+
const challenges = {};
|
|
180
|
+
for (const entry of entries) {
|
|
181
|
+
if (entry.isDirectory()) {
|
|
182
|
+
// Check if it has a challenge.ts file
|
|
183
|
+
const challengePath = path.join(challengesDir, entry.name, "challenge.ts");
|
|
184
|
+
if (existsSync(challengePath)) {
|
|
185
|
+
challenges[entry.name] = true;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return challenges;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Create a new local challenge folder with challenge.ts template
|
|
193
|
+
* Note: config.ts is NOT created here - it should be generated later via `challenge config download`
|
|
194
|
+
*/
|
|
195
|
+
static async createLocal(slug, config) {
|
|
196
|
+
const challenge = new Challenge(slug, config);
|
|
197
|
+
const challengeTemplatePath = getStaticResourcePath("challenge.ts");
|
|
198
|
+
// Create challenge directory
|
|
199
|
+
await fs.mkdir(challenge.challengeDir, { recursive: true });
|
|
200
|
+
// Copy challenge.ts template
|
|
201
|
+
await fs.copyFile(challengeTemplatePath, challenge.challengePath);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const ConfigSchema: z.ZodObject<{
|
|
3
|
+
WEB_API_URL: z.ZodString;
|
|
4
|
+
WEB_URL: z.ZodString;
|
|
5
|
+
STUDIO_API_URL: z.ZodString;
|
|
6
|
+
STUDIO_URL: z.ZodString;
|
|
7
|
+
KRADLE_API_KEY: z.ZodString;
|
|
8
|
+
GCS_BUCKET: z.ZodString;
|
|
9
|
+
KRADLE_CHALLENGES_PATH: z.ZodDefault<z.ZodString>;
|
|
10
|
+
NAMESPACE: z.ZodDefault<z.ZodString>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export type Config = z.infer<typeof ConfigSchema>;
|
|
13
|
+
export declare function loadConfig(): Config;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { untildify } from "./utils.js";
|
|
5
|
+
export const ConfigSchema = z.object({
|
|
6
|
+
WEB_API_URL: z.string().url(),
|
|
7
|
+
WEB_URL: z.string().url(),
|
|
8
|
+
STUDIO_API_URL: z.string().url(),
|
|
9
|
+
STUDIO_URL: z.string(),
|
|
10
|
+
KRADLE_API_KEY: z.string(),
|
|
11
|
+
GCS_BUCKET: z.string(),
|
|
12
|
+
/**
|
|
13
|
+
* Absolute path to the challenges directory. Defaults to ~/Documents/kradle-studio/challenges.
|
|
14
|
+
*/
|
|
15
|
+
KRADLE_CHALLENGES_PATH: z.string().default(path.join(os.homedir(), "Documents", "kradle-studio", "challenges")),
|
|
16
|
+
NAMESPACE: z.string().default("kradle"),
|
|
17
|
+
});
|
|
18
|
+
export function loadConfig() {
|
|
19
|
+
try {
|
|
20
|
+
/**
|
|
21
|
+
* First, resolve tildes in the KRADLE_CHALLENGES_PATH
|
|
22
|
+
*/
|
|
23
|
+
let challengesPath;
|
|
24
|
+
if (process.env.KRADLE_CHALLENGES_PATH) {
|
|
25
|
+
challengesPath = untildify(process.env.KRADLE_CHALLENGES_PATH);
|
|
26
|
+
}
|
|
27
|
+
const config = ConfigSchema.parse({
|
|
28
|
+
WEB_API_URL: process.env.WEB_API_URL,
|
|
29
|
+
WEB_URL: process.env.WEB_URL,
|
|
30
|
+
STUDIO_API_URL: process.env.STUDIO_API_URL,
|
|
31
|
+
STUDIO_URL: process.env.STUDIO_URL,
|
|
32
|
+
KRADLE_API_KEY: process.env.KRADLE_API_KEY,
|
|
33
|
+
GCS_BUCKET: process.env.GCS_BUCKET,
|
|
34
|
+
KRADLE_CHALLENGES_PATH: challengesPath,
|
|
35
|
+
NAMESPACE: process.env.NAMESPACE,
|
|
36
|
+
});
|
|
37
|
+
for (const field of Object.keys(config)) {
|
|
38
|
+
if (!config[field]) {
|
|
39
|
+
throw new Error(`Missing required config field: "${field}". Please check your .env file.`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return config;
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error instanceof z.ZodError) {
|
|
46
|
+
const missingFields = error.issues.map((e) => e.path.join(".")).join(", ");
|
|
47
|
+
throw new Error(`Missing or invalid environment variables: ${missingFields}. Please check your .env file.`);
|
|
48
|
+
}
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const ChallengeSchema: z.ZodObject<{
|
|
3
|
+
id: z.ZodOptional<z.ZodString>;
|
|
4
|
+
slug: z.ZodString;
|
|
5
|
+
name: z.ZodString;
|
|
6
|
+
visibility: z.ZodEnum<{
|
|
7
|
+
private: "private";
|
|
8
|
+
public: "public";
|
|
9
|
+
unlisted: "unlisted";
|
|
10
|
+
}>;
|
|
11
|
+
domain: z.ZodString;
|
|
12
|
+
world: z.ZodString;
|
|
13
|
+
challengeConfig: z.ZodObject<{
|
|
14
|
+
cheat: z.ZodBoolean;
|
|
15
|
+
datapack: z.ZodBoolean;
|
|
16
|
+
gameMode: z.ZodEnum<{
|
|
17
|
+
survival: "survival";
|
|
18
|
+
creative: "creative";
|
|
19
|
+
adventure: "adventure";
|
|
20
|
+
spectator: "spectator";
|
|
21
|
+
}>;
|
|
22
|
+
}, z.core.$strip>;
|
|
23
|
+
task: z.ZodString;
|
|
24
|
+
roles: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
25
|
+
description: z.ZodString;
|
|
26
|
+
specificTask: z.ZodString;
|
|
27
|
+
}, z.core.$strip>>;
|
|
28
|
+
objective: z.ZodObject<{
|
|
29
|
+
fieldName: z.ZodString;
|
|
30
|
+
direction: z.ZodEnum<{
|
|
31
|
+
maximize: "maximize";
|
|
32
|
+
minimize: "minimize";
|
|
33
|
+
}>;
|
|
34
|
+
}, z.core.$strip>;
|
|
35
|
+
creationTime: z.ZodOptional<z.ZodString>;
|
|
36
|
+
updateTime: z.ZodOptional<z.ZodString>;
|
|
37
|
+
creator: z.ZodOptional<z.ZodString>;
|
|
38
|
+
}, z.core.$strip>;
|
|
39
|
+
export declare const ChallengesResponseSchema: z.ZodObject<{
|
|
40
|
+
challenges: z.ZodArray<z.ZodObject<{
|
|
41
|
+
id: z.ZodOptional<z.ZodString>;
|
|
42
|
+
slug: z.ZodString;
|
|
43
|
+
name: z.ZodString;
|
|
44
|
+
visibility: z.ZodEnum<{
|
|
45
|
+
private: "private";
|
|
46
|
+
public: "public";
|
|
47
|
+
unlisted: "unlisted";
|
|
48
|
+
}>;
|
|
49
|
+
domain: z.ZodString;
|
|
50
|
+
world: z.ZodString;
|
|
51
|
+
challengeConfig: z.ZodObject<{
|
|
52
|
+
cheat: z.ZodBoolean;
|
|
53
|
+
datapack: z.ZodBoolean;
|
|
54
|
+
gameMode: z.ZodEnum<{
|
|
55
|
+
survival: "survival";
|
|
56
|
+
creative: "creative";
|
|
57
|
+
adventure: "adventure";
|
|
58
|
+
spectator: "spectator";
|
|
59
|
+
}>;
|
|
60
|
+
}, z.core.$strip>;
|
|
61
|
+
task: z.ZodString;
|
|
62
|
+
roles: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
63
|
+
description: z.ZodString;
|
|
64
|
+
specificTask: z.ZodString;
|
|
65
|
+
}, z.core.$strip>>;
|
|
66
|
+
objective: z.ZodObject<{
|
|
67
|
+
fieldName: z.ZodString;
|
|
68
|
+
direction: z.ZodEnum<{
|
|
69
|
+
maximize: "maximize";
|
|
70
|
+
minimize: "minimize";
|
|
71
|
+
}>;
|
|
72
|
+
}, z.core.$strip>;
|
|
73
|
+
creationTime: z.ZodOptional<z.ZodString>;
|
|
74
|
+
updateTime: z.ZodOptional<z.ZodString>;
|
|
75
|
+
creator: z.ZodOptional<z.ZodString>;
|
|
76
|
+
}, z.core.$strip>>;
|
|
77
|
+
nextPageToken: z.ZodOptional<z.ZodString>;
|
|
78
|
+
}, z.core.$strip>;
|
|
79
|
+
export declare const HumanSchema: z.ZodObject<{
|
|
80
|
+
username: z.ZodString;
|
|
81
|
+
}, z.core.$strip>;
|
|
82
|
+
export declare const RunResponseSchema: z.ZodObject<{
|
|
83
|
+
runIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
84
|
+
}, z.core.$strip>;
|
|
85
|
+
export declare const UploadUrlResponseSchema: z.ZodObject<{
|
|
86
|
+
uploadUrl: z.ZodString;
|
|
87
|
+
expiresAt: z.ZodString;
|
|
88
|
+
}, z.core.$strip>;
|
|
89
|
+
export declare const AgentSchema: z.ZodObject<{
|
|
90
|
+
username: z.ZodOptional<z.ZodString>;
|
|
91
|
+
name: z.ZodOptional<z.ZodString>;
|
|
92
|
+
description: z.ZodOptional<z.ZodString>;
|
|
93
|
+
visibility: z.ZodOptional<z.ZodEnum<{
|
|
94
|
+
private: "private";
|
|
95
|
+
public: "public";
|
|
96
|
+
unlisted: "unlisted";
|
|
97
|
+
}>>;
|
|
98
|
+
creationTime: z.ZodOptional<z.ZodString>;
|
|
99
|
+
updateTime: z.ZodOptional<z.ZodString>;
|
|
100
|
+
deletionTime: z.ZodOptional<z.ZodString>;
|
|
101
|
+
creatorId: z.ZodOptional<z.ZodString>;
|
|
102
|
+
originalCreatorId: z.ZodOptional<z.ZodString>;
|
|
103
|
+
}, z.core.$strip>;
|
|
104
|
+
export declare const AgentsResponseSchema: z.ZodObject<{
|
|
105
|
+
agents: z.ZodArray<z.ZodObject<{
|
|
106
|
+
username: z.ZodOptional<z.ZodString>;
|
|
107
|
+
name: z.ZodOptional<z.ZodString>;
|
|
108
|
+
description: z.ZodOptional<z.ZodString>;
|
|
109
|
+
visibility: z.ZodOptional<z.ZodEnum<{
|
|
110
|
+
private: "private";
|
|
111
|
+
public: "public";
|
|
112
|
+
unlisted: "unlisted";
|
|
113
|
+
}>>;
|
|
114
|
+
creationTime: z.ZodOptional<z.ZodString>;
|
|
115
|
+
updateTime: z.ZodOptional<z.ZodString>;
|
|
116
|
+
deletionTime: z.ZodOptional<z.ZodString>;
|
|
117
|
+
creatorId: z.ZodOptional<z.ZodString>;
|
|
118
|
+
originalCreatorId: z.ZodOptional<z.ZodString>;
|
|
119
|
+
}, z.core.$strip>>;
|
|
120
|
+
nextPageToken: z.ZodOptional<z.ZodString>;
|
|
121
|
+
}, z.core.$strip>;
|
|
122
|
+
export type ChallengeSchemaType = z.infer<typeof ChallengeSchema>;
|
|
123
|
+
export type ChallengesResponseType = z.infer<typeof ChallengesResponseSchema>;
|
|
124
|
+
export type HumanSchemaType = z.infer<typeof HumanSchema>;
|
|
125
|
+
export type RunResponseType = z.infer<typeof RunResponseSchema>;
|
|
126
|
+
export type AgentSchemaType = z.infer<typeof AgentSchema>;
|
|
127
|
+
export type AgentsResponseType = z.infer<typeof AgentsResponseSchema>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const ChallengeSchema = z.object({
|
|
3
|
+
id: z.string().optional(),
|
|
4
|
+
slug: z.string(),
|
|
5
|
+
name: z.string(),
|
|
6
|
+
visibility: z.enum(["private", "public", "unlisted"]),
|
|
7
|
+
domain: z.string(),
|
|
8
|
+
world: z.string(),
|
|
9
|
+
challengeConfig: z.object({
|
|
10
|
+
cheat: z.boolean(),
|
|
11
|
+
datapack: z.boolean(),
|
|
12
|
+
gameMode: z.enum(["survival", "creative", "adventure", "spectator"]),
|
|
13
|
+
}),
|
|
14
|
+
task: z.string(),
|
|
15
|
+
roles: z.record(z.string(), z.object({
|
|
16
|
+
description: z.string(),
|
|
17
|
+
specificTask: z.string(),
|
|
18
|
+
})),
|
|
19
|
+
objective: z.object({
|
|
20
|
+
fieldName: z.string(),
|
|
21
|
+
direction: z.enum(["maximize", "minimize"]),
|
|
22
|
+
}),
|
|
23
|
+
creationTime: z.string().optional(),
|
|
24
|
+
updateTime: z.string().optional(),
|
|
25
|
+
creator: z.string().optional(),
|
|
26
|
+
});
|
|
27
|
+
export const ChallengesResponseSchema = z.object({
|
|
28
|
+
challenges: z.array(ChallengeSchema),
|
|
29
|
+
nextPageToken: z.string().optional(),
|
|
30
|
+
});
|
|
31
|
+
export const HumanSchema = z.object({
|
|
32
|
+
username: z.string(),
|
|
33
|
+
});
|
|
34
|
+
export const RunResponseSchema = z.object({
|
|
35
|
+
runIds: z.array(z.string()).optional(),
|
|
36
|
+
});
|
|
37
|
+
export const UploadUrlResponseSchema = z.object({
|
|
38
|
+
uploadUrl: z.string(),
|
|
39
|
+
expiresAt: z.string(),
|
|
40
|
+
});
|
|
41
|
+
export const AgentSchema = z.object({
|
|
42
|
+
username: z.string().optional(),
|
|
43
|
+
name: z.string().optional(),
|
|
44
|
+
description: z.string().optional(),
|
|
45
|
+
visibility: z.enum(["private", "public", "unlisted"]).optional(),
|
|
46
|
+
creationTime: z.string().optional(),
|
|
47
|
+
updateTime: z.string().optional(),
|
|
48
|
+
deletionTime: z.string().optional(),
|
|
49
|
+
creatorId: z.string().optional(),
|
|
50
|
+
originalCreatorId: z.string().optional(),
|
|
51
|
+
});
|
|
52
|
+
export const AgentsResponseSchema = z.object({
|
|
53
|
+
agents: z.array(AgentSchema),
|
|
54
|
+
nextPageToken: z.string().optional(),
|
|
55
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { Dirent } from "node:fs";
|
|
2
|
+
import type { Config } from "./config.js";
|
|
3
|
+
export declare function loadTemplateRun(): Promise<Record<string, unknown>>;
|
|
4
|
+
/**
|
|
5
|
+
* Resolve tildes in a path to the home directory
|
|
6
|
+
*/
|
|
7
|
+
export declare function untildify(filePath: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Returns a debounced function that will only be called after the delay has passed since the last call.
|
|
10
|
+
*
|
|
11
|
+
* @param fn The function to debounce.
|
|
12
|
+
* @param delay The delay in milliseconds.
|
|
13
|
+
* @returns A debounced function.
|
|
14
|
+
*/
|
|
15
|
+
export declare function debounced(fn: () => Promise<unknown>, delay: number): () => void;
|
|
16
|
+
/**
|
|
17
|
+
* Clear the screen.
|
|
18
|
+
*
|
|
19
|
+
* @see https://stackoverflow.com/a/26373971
|
|
20
|
+
*/
|
|
21
|
+
export declare function clearScreen(): void;
|
|
22
|
+
/**
|
|
23
|
+
* Get the path to a static resource (located in the `static` folder).
|
|
24
|
+
*
|
|
25
|
+
* @param name The name of the resource.
|
|
26
|
+
* @returns The absolute path to the resource.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getStaticResourcePath(name: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Reads a directory and returns a sorted list of files, with their types.
|
|
31
|
+
* @param dir The directory to read.
|
|
32
|
+
* @returns A sorted list of files, with their types (Dirent).
|
|
33
|
+
*/
|
|
34
|
+
export declare function readDirSorted(dir: string): Promise<Dirent[]>;
|
|
35
|
+
/**
|
|
36
|
+
* Runs a NodeJS module. The module is executed in the current working directory, and the environment is set to the given config.
|
|
37
|
+
* stdout & stdout are piped to the parent process.
|
|
38
|
+
*
|
|
39
|
+
* Returns a promise that resolves when the module completes.
|
|
40
|
+
*
|
|
41
|
+
* @param file The file to execute.
|
|
42
|
+
* @param config The configuration to use.
|
|
43
|
+
* @param options The options to use.
|
|
44
|
+
* @param options.silent Whether to suppress stdout.
|
|
45
|
+
* @returns A promise that resolves when the command completes.
|
|
46
|
+
*/
|
|
47
|
+
export declare function runNodeModule(file: string, config: Config, options?: {
|
|
48
|
+
silent?: boolean;
|
|
49
|
+
}): Promise<undefined>;
|
|
50
|
+
/**
|
|
51
|
+
* Execute a TypeScript file using NodeJS's built-in TS support.
|
|
52
|
+
*
|
|
53
|
+
* @param filePath The path to the TypeScript file.
|
|
54
|
+
* @param config The configuration to use.
|
|
55
|
+
* @param options The options to use.
|
|
56
|
+
* @param options.silent Whether to suppress stdout.
|
|
57
|
+
* @returns The result of the execution.
|
|
58
|
+
*/
|
|
59
|
+
export declare function executeTypescriptFile(filePath: string, config: Config, options?: {
|
|
60
|
+
silent?: boolean;
|
|
61
|
+
}): Promise<unknown>;
|
|
62
|
+
/**
|
|
63
|
+
* Executes an arbitrary command. The command is spawned in the current working directory, and the environment is set to the given config.
|
|
64
|
+
*
|
|
65
|
+
* stdout will be returned as a string.
|
|
66
|
+
* if stderr is not empty, an error will be thrown.
|
|
67
|
+
*
|
|
68
|
+
* @param args The arguments to pass to the command.
|
|
69
|
+
* @param options The options to use.
|
|
70
|
+
* @param options.env The environment variables to set.
|
|
71
|
+
* @param options.cwd The current working directory.
|
|
72
|
+
* @returns A promise that resolves with the stdout of the command.
|
|
73
|
+
*/
|
|
74
|
+
export declare function executeCommand(command: string, args: string[], options?: {
|
|
75
|
+
env?: Record<string, string>;
|
|
76
|
+
cwd?: string;
|
|
77
|
+
}): Promise<string>;
|
|
78
|
+
/**
|
|
79
|
+
* Executes an arbitrary NodeJS command. The command is spawned in the current working directory, and the environment is set to the given config.
|
|
80
|
+
* It will use the process.execPath of the current process to find the NodeJS executable.
|
|
81
|
+
*
|
|
82
|
+
* stdout will be returned as a string.
|
|
83
|
+
* if stderr is not empty, an error will be thrown.
|
|
84
|
+
*
|
|
85
|
+
* @param args The arguments to pass to the command.
|
|
86
|
+
* @param config The configuration to use.
|
|
87
|
+
* @returns A promise that resolves with the stdout of the command.
|
|
88
|
+
*/
|
|
89
|
+
export declare function executeNodeCommand(args: string[], config: Config): Promise<string>;
|