@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.
Files changed (45) hide show
  1. package/README.md +224 -0
  2. package/bin/dev.cmd +3 -0
  3. package/bin/dev.js +14 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +14 -0
  6. package/dist/commands/agent/list.d.ts +6 -0
  7. package/dist/commands/agent/list.js +20 -0
  8. package/dist/commands/challenge/build.d.ts +9 -0
  9. package/dist/commands/challenge/build.js +25 -0
  10. package/dist/commands/challenge/create.d.ts +12 -0
  11. package/dist/commands/challenge/create.js +87 -0
  12. package/dist/commands/challenge/delete.d.ts +12 -0
  13. package/dist/commands/challenge/delete.js +99 -0
  14. package/dist/commands/challenge/list.d.ts +6 -0
  15. package/dist/commands/challenge/list.js +48 -0
  16. package/dist/commands/challenge/multi-upload.d.ts +6 -0
  17. package/dist/commands/challenge/multi-upload.js +80 -0
  18. package/dist/commands/challenge/run.d.ts +12 -0
  19. package/dist/commands/challenge/run.js +47 -0
  20. package/dist/commands/challenge/watch.d.ts +12 -0
  21. package/dist/commands/challenge/watch.js +113 -0
  22. package/dist/commands/init.d.ts +11 -0
  23. package/dist/commands/init.js +161 -0
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.js +1 -0
  26. package/dist/lib/api-client.d.ts +55 -0
  27. package/dist/lib/api-client.js +162 -0
  28. package/dist/lib/arguments.d.ts +7 -0
  29. package/dist/lib/arguments.js +17 -0
  30. package/dist/lib/challenge.d.ts +67 -0
  31. package/dist/lib/challenge.js +203 -0
  32. package/dist/lib/config.d.ts +13 -0
  33. package/dist/lib/config.js +51 -0
  34. package/dist/lib/schemas.d.ts +127 -0
  35. package/dist/lib/schemas.js +55 -0
  36. package/dist/lib/utils.d.ts +89 -0
  37. package/dist/lib/utils.js +170 -0
  38. package/oclif.manifest.json +310 -0
  39. package/package.json +78 -0
  40. package/static/challenge.ts +32 -0
  41. package/static/project_template/dev.env +6 -0
  42. package/static/project_template/package.json +17 -0
  43. package/static/project_template/prod.env +6 -0
  44. package/static/project_template/template-run.json +10 -0
  45. 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>;