genlayer 0.12.1 → 0.12.2-beta.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 (44) hide show
  1. package/.github/workflows/publish-beta.yml +2 -2
  2. package/.github/workflows/publish.yml +2 -2
  3. package/.github/workflows/validate-code.yml +1 -1
  4. package/CHANGELOG.md +6 -0
  5. package/dist/index.js +24736 -108331
  6. package/docker-compose.yml +1 -1
  7. package/esbuild.config.dev.js +18 -0
  8. package/esbuild.config.js +10 -5
  9. package/esbuild.config.prod.js +16 -0
  10. package/eslint.config.js +59 -0
  11. package/package.json +12 -9
  12. package/scripts/postinstall.js +10 -6
  13. package/src/commands/config/getSetReset.ts +25 -18
  14. package/src/commands/contracts/deploy.ts +105 -25
  15. package/src/commands/contracts/index.ts +5 -1
  16. package/src/commands/general/index.ts +10 -4
  17. package/src/commands/general/init.ts +136 -189
  18. package/src/commands/general/start.ts +76 -77
  19. package/src/commands/general/stop.ts +6 -5
  20. package/src/commands/keygen/create.ts +9 -11
  21. package/src/commands/update/index.ts +3 -8
  22. package/src/commands/update/ollama.ts +56 -56
  23. package/src/commands/validators/validators.ts +48 -55
  24. package/src/lib/actions/BaseAction.ts +75 -4
  25. package/src/lib/config/simulator.ts +1 -1
  26. package/src/lib/services/simulator.ts +3 -2
  27. package/tests/actions/create.test.ts +18 -30
  28. package/tests/actions/deploy.test.ts +200 -30
  29. package/tests/actions/getSetReset.test.ts +29 -42
  30. package/tests/actions/init.test.ts +240 -475
  31. package/tests/actions/ollama.test.ts +40 -55
  32. package/tests/actions/start.test.ts +107 -108
  33. package/tests/actions/stop.test.ts +23 -4
  34. package/tests/actions/validators.test.ts +273 -142
  35. package/tests/commands/call.test.ts +4 -1
  36. package/tests/commands/deploy.test.ts +11 -0
  37. package/tests/commands/init.test.ts +11 -12
  38. package/tests/commands/up.test.ts +31 -23
  39. package/tests/commands/update.test.ts +2 -5
  40. package/tests/libs/baseAction.test.ts +175 -0
  41. package/tests/services/simulator.test.ts +15 -0
  42. package/.eslintrc.js +0 -58
  43. package/esbuild.config.dev +0 -16
  44. package/esbuild.config.prod +0 -16
@@ -93,7 +93,7 @@ services:
93
93
  max-file: "3"
94
94
 
95
95
  ollama:
96
- image: ollama/ollama:0.5.7
96
+ image: ollama/ollama:0.5.11
97
97
  ports:
98
98
  - 11434:11434
99
99
  container_name: ollama
@@ -0,0 +1,18 @@
1
+ export default {
2
+ context: {
3
+ tsconfig: "./tsconfig.json",
4
+ entryPoints: ["src/index.ts"],
5
+ bundle: true,
6
+ outfile: "dist/index.js",
7
+ platform: "node",
8
+ target: "es2020",
9
+ format: "esm",
10
+ define: { "import.meta.url": "import.meta.url" },
11
+ banner: {
12
+ js: `const _importMetaUrl = new URL(import.meta.url).pathname;`,
13
+ },
14
+ external: ["commander", "dockerode", "dotenv", "ethers", "inquirer", "update-check", "ssh2", "esbuild"]
15
+
16
+ },
17
+ watch: true,
18
+ };
package/esbuild.config.js CHANGED
@@ -1,13 +1,18 @@
1
- const esbuild = require("esbuild");
1
+ /* eslint-disable no-undef -- Allow process and console in ignored file */
2
+
3
+ import esbuild from "esbuild";
4
+
2
5
  const isProduction = process.env.NODE_ENV === "production";
3
- const config = require(isProduction ? "./esbuild.config.prod" : "./esbuild.config.dev");
6
+ const config = isProduction
7
+ ? await import("./esbuild.config.prod.js")
8
+ : await import("./esbuild.config.dev.js");
4
9
 
5
10
  const run = async () => {
6
- if (config.watch) {
7
- const context = await esbuild.context(config.context);
11
+ if (config.default.watch) {
12
+ const context = await esbuild.context(config.default.context);
8
13
  await context.watch();
9
14
  } else {
10
- await esbuild.build(config.context);
15
+ await esbuild.build(config.default.context);
11
16
  }
12
17
  };
13
18
 
@@ -0,0 +1,16 @@
1
+ export default {
2
+ context: {
3
+ tsconfig: "./tsconfig.json",
4
+ entryPoints: ["src/index.ts"],
5
+ bundle: true,
6
+ outfile: "dist/index.js",
7
+ platform: "node",
8
+ target: "es2020",
9
+ define: { "import.meta.url": "_importMetaUrl" },
10
+ banner: {
11
+ js: `const _importMetaUrl = new URL(import.meta.url).pathname;`,
12
+ },
13
+ external: ["commander", "dockerode", "dotenv", "ethers", "inquirer", "update-check", "ssh2", "esbuild"]
14
+ },
15
+ watch: false,
16
+ };
@@ -0,0 +1,59 @@
1
+ import js from "@eslint/js";
2
+ import tseslint from "@typescript-eslint/eslint-plugin";
3
+ import tsparser from "@typescript-eslint/parser";
4
+ import prettier from "eslint-config-prettier";
5
+ import importPlugin from "eslint-plugin-import";
6
+
7
+ export default [
8
+ js.configs.recommended,
9
+ prettier,
10
+ {
11
+ languageOptions: {
12
+ parser: tsparser,
13
+ parserOptions: {
14
+ project: ["./tsconfig.json"],
15
+ sourceType: "module",
16
+ },
17
+ },
18
+ ignores: [
19
+ "**/dist/**/*",
20
+ "esbuild.config.js",
21
+ "esbuild.config.dev.js",
22
+ "esbuild.config.prod.js",
23
+ "jest.config.js",
24
+ "eslint.config.js",
25
+ "Config.js",
26
+ "commitLint.config.ts",
27
+ "scripts/postinstall.js"
28
+ ],
29
+ plugins: {
30
+ "@typescript-eslint": tseslint,
31
+ import: importPlugin,
32
+ },
33
+ rules: {
34
+ "import/namespace": "off",
35
+ "@typescript-eslint/no-empty-function": "off",
36
+ "@typescript-eslint/no-empty-interface": "off",
37
+ "no-constant-condition": "off",
38
+ "@typescript-eslint/no-explicit-any": "off",
39
+ "prefer-const": "off",
40
+ "@typescript-eslint/no-non-null-assertion": "off",
41
+ "@typescript-eslint/ban-ts-ignore": "off",
42
+ "@typescript-eslint/no-loss-of-precision": "off",
43
+ "@typescript-eslint/ban-types": "off",
44
+ "@typescript-eslint/ban-ts-comment": "off",
45
+ "@typescript-eslint/no-non-null-asserted-optional-chain": "off",
46
+ "@typescript-eslint/no-var-requires": "off",
47
+ "import/export": "off",
48
+ "no-fallthrough": "off",
49
+ "@typescript-eslint/explicit-module-boundary-types": "off",
50
+ "@typescript-eslint/no-floating-promises": ["error"],
51
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
52
+ },
53
+ settings: {
54
+ "import/resolver": {
55
+ typescript: {},
56
+ },
57
+ },
58
+ },
59
+ ];
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "genlayer",
3
- "version": "0.12.1",
3
+ "version": "0.12.2-beta.0",
4
4
  "description": "GenLayer Command Line Tool",
5
5
  "main": "src/index.ts",
6
+ "type": "module",
6
7
  "bin": {
7
8
  "genlayer": "./dist/index.js"
8
9
  },
@@ -34,7 +35,7 @@
34
35
  },
35
36
  "homepage": "https://github.com/yeagerai/genlayer-cli#readme",
36
37
  "devDependencies": {
37
- "@release-it/conventional-changelog": "^8.0.1",
38
+ "@release-it/conventional-changelog": "^10.0.0",
38
39
  "@types/dockerode": "^3.3.31",
39
40
  "@types/inquirer": "^9.0.7",
40
41
  "@types/node": "^22.0.0",
@@ -44,26 +45,28 @@
44
45
  "@typescript-eslint/parser": "^8.0.0",
45
46
  "@vitest/coverage-v8": "^2.1.4",
46
47
  "cross-env": "^7.0.3",
47
- "esbuild": "^0.24.0",
48
- "eslint": "^8.57.0",
49
- "eslint-config-prettier": "^9.1.0",
48
+ "esbuild": ">=0.25.0",
49
+ "eslint": "^9.0.0",
50
+ "eslint-config-prettier": "^10.0.0",
50
51
  "eslint-import-resolver-typescript": "^3.6.1",
51
52
  "eslint-plugin-import": "^2.29.1",
52
- "jsdom": "^25.0.1",
53
+ "jsdom": "^26.0.0",
53
54
  "prettier": "^3.2.5",
54
- "release-it": "^17.2.0",
55
+ "release-it": "^18.0.0",
55
56
  "ts-node": "^10.9.2",
56
57
  "typescript": "^5.4.5"
57
58
  },
58
59
  "dependencies": {
59
- "commander": "^12.0.0",
60
+ "chalk": "^5.4.1",
61
+ "commander": "^13.1.0",
60
62
  "dockerode": "^4.0.2",
61
63
  "dotenv": "^16.4.5",
62
64
  "ethers": "^6.13.4",
63
65
  "genlayer-js": "^0.6.0",
64
- "inquirer": "^9.3.7",
66
+ "inquirer": "^12.0.0",
65
67
  "node-fetch": "^3.0.0",
66
68
  "open": "^10.1.0",
69
+ "ora": "^8.2.0",
67
70
  "update-check": "^1.5.4",
68
71
  "uuid": "^11.0.0",
69
72
  "viem": "^2.21.54",
@@ -1,16 +1,20 @@
1
1
  #!/usr/bin/env node
2
+ /* eslint-disable no-undef -- Allow process and console in ignored file */
2
3
 
3
- const fs = require('fs');
4
- const path = require('path');
4
+ import { existsSync, copyFileSync } from 'fs';
5
+ import { resolve, dirname } from 'path';
6
+ import { fileURLToPath } from 'url';
5
7
 
6
- const envExamplePath = path.resolve(__dirname, '../.env.example');
7
- const envPath = path.resolve(__dirname, '../.env');
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+
10
+ const envExamplePath = resolve(__dirname, '../.env.example');
11
+ const envPath = resolve(__dirname, '../.env');
8
12
 
9
13
  try {
10
- if (fs.existsSync(envPath)) {
14
+ if (existsSync(envPath)) {
11
15
  console.log(`⚠️ .env file already exists. Skipping creation.`);
12
16
  } else {
13
- fs.copyFileSync(envExamplePath, envPath);
17
+ copyFileSync(envExamplePath, envPath);
14
18
  console.log(`✅ .env file created successfully from .env.example.`);
15
19
  }
16
20
  } catch (error) {
@@ -1,44 +1,51 @@
1
- import { ConfigFileManager } from "../../lib/config/ConfigFileManager";
1
+ import { BaseAction } from "../../lib/actions/BaseAction";
2
2
 
3
- export class ConfigActions {
4
- private configManager: ConfigFileManager;
3
+ export class ConfigActions extends BaseAction {
5
4
 
6
5
  constructor() {
7
- this.configManager = new ConfigFileManager();
6
+ super();
8
7
  }
9
8
 
10
9
  set(keyValue: string): void {
11
10
  const [key, value] = keyValue.split("=");
11
+ this.startSpinner(`Updating configuration: ${key}`);
12
+
12
13
  if (!key || value === undefined) {
13
- console.error("Invalid format. Use key=value.");
14
- process.exit(1);
14
+ this.failSpinner("Invalid format. Use 'key=value'.");
15
+ return;
15
16
  }
16
- this.configManager.writeConfig(key, value);
17
- console.log(`Configuration updated: ${key}=${value}`);
17
+
18
+ this.writeConfig(key, value);
19
+ this.succeedSpinner(`Configuration successfully updated`);
18
20
  }
19
21
 
20
22
  get(key?: string): void {
23
+ this.startSpinner(key ? `Retrieving value for: ${key}` : "Retrieving all configurations");
24
+
21
25
  if (key) {
22
- const value = this.configManager.getConfigByKey(key);
26
+ const value = this.getConfigByKey(key);
23
27
  if (value === null) {
24
- console.log(`No value set for key: ${key}`);
28
+ this.failSpinner(`No configuration found for '${key}'.`);
25
29
  } else {
26
- console.log(`${key}=${value}`);
30
+ this.succeedSpinner(`Configuration successfully retrieved`, `${key}=${value}`);
27
31
  }
28
32
  } else {
29
- const config = this.configManager.getConfig();
30
- console.log("Current configuration:", JSON.stringify(config, null, 2));
33
+ const config = this.getConfig();
34
+ this.succeedSpinner("All configurations successfully retrieved", JSON.stringify(config, null, 2));
31
35
  }
32
36
  }
33
37
 
34
38
  reset(key: string): void {
35
- const config = this.configManager.getConfig();
36
- if (config[key] === undefined) {
37
- console.log(`Key does not exist in the configuration: ${key}`);
39
+ this.startSpinner(`Resetting configuration: ${key}`);
40
+
41
+ const config = this.getConfig();
42
+ if (!(key in config)) {
43
+ this.failSpinner(`Configuration key '${key}' does not exist.`);
38
44
  return;
39
45
  }
46
+
40
47
  delete config[key];
41
- this.configManager.writeConfig(key, undefined);
42
- console.log(`Configuration key reset: ${key}`);
48
+ this.writeConfig(key, undefined);
49
+ this.succeedSpinner(`Configuration successfully reset`);
43
50
  }
44
51
  }
@@ -1,20 +1,25 @@
1
1
  import fs from "fs";
2
+ import path from "path";
2
3
  import { createClient, createAccount } from "genlayer-js";
3
4
  import { simulator } from "genlayer-js/chains";
4
5
  import type { GenLayerClient } from "genlayer-js/types";
5
6
  import { getPrivateKey } from "../../lib/accounts/getPrivateKey";
7
+ import { BaseAction } from "../../lib/actions/BaseAction";
8
+ import { pathToFileURL } from "url";
9
+ import { TransactionStatus } from "genlayer-js/types";
10
+ import { buildSync } from "esbuild";
6
11
 
7
12
  export interface DeployOptions {
8
13
  contract?: string;
9
- // network: string;
10
14
  args?: any[];
11
- kwargs?: string;
12
15
  }
13
16
 
14
- export class DeployAction {
17
+ export class DeployAction extends BaseAction {
15
18
  private genlayerClient: GenLayerClient<typeof simulator>;
19
+ private readonly deployDir = path.resolve(process.cwd(), "deploy");
16
20
 
17
21
  constructor() {
22
+ super();
18
23
  this.genlayerClient = createClient({
19
24
  chain: simulator,
20
25
  endpoint: process.env.VITE_JSON_RPC_SERVER_URL,
@@ -29,44 +34,119 @@ export class DeployAction {
29
34
  return fs.readFileSync(contractPath, "utf-8");
30
35
  }
31
36
 
32
- async deploy(options: DeployOptions): Promise<void> {
33
-
34
- const argsUsed = options.args && options.args.length > 0;
35
- const kwargsUsed = options.kwargs && options.kwargs.trim() !== "";
37
+ private async executeTsScript(filePath: string): Promise<void> {
38
+ const outFile = filePath.replace(/\.ts$/, ".compiled.js");
39
+ this.startSpinner(`Transpiling TypeScript file: ${filePath}`);
40
+ try {
41
+ buildSync({
42
+ entryPoints: [filePath],
43
+ outfile: outFile,
44
+ bundle: false,
45
+ platform: "node",
46
+ format: "esm",
47
+ target: "es2020",
48
+ sourcemap: false,
49
+ });
50
+ await this.executeJsScript(filePath, outFile);
51
+ } catch (error) {
52
+ this.failSpinner(`Error executing: ${filePath}`, error);
53
+ } finally {
54
+ fs.unlinkSync(outFile);
55
+ }
56
+ }
36
57
 
37
- if (argsUsed && kwargsUsed) {
38
- throw new Error("Invalid usage: Please specify either `args` or `kwargs`, but not both.");
58
+ private async executeJsScript(filePath: string, transpiledFilePath?: string): Promise<void> {
59
+ this.startSpinner(`Executing file: ${filePath}`);
60
+ try {
61
+ const module = await import(pathToFileURL(transpiledFilePath || filePath).href);
62
+ if (!module.default || typeof module.default !== "function") {
63
+ this.failSpinner(`No "default" function found in: ${filePath}`);
64
+ return
65
+ }
66
+ await module.default(this.genlayerClient);
67
+ this.succeedSpinner(`Successfully executed: ${filePath}`);
68
+ } catch (error) {
69
+ this.failSpinner(`Error executing: ${filePath}`, error);
39
70
  }
71
+ }
40
72
 
41
- if (!options.contract) {
42
- console.error("No contract specified for deployment.");
73
+ async deployScripts() {
74
+ this.startSpinner("Searching for deploy scripts...");
75
+ if (!fs.existsSync(this.deployDir)) {
76
+ this.failSpinner("No deploy folder found.");
43
77
  return;
44
78
  }
79
+ const files = fs.readdirSync(this.deployDir)
80
+ .filter(file => file.endsWith(".ts") || file.endsWith(".js"))
81
+ .sort((a, b) => {
82
+ const numA = parseInt(a.split("_")[0]);
83
+ const numB = parseInt(b.split("_")[0]);
45
84
 
46
- const contractCode = this.readContractCode(options.contract);
85
+ if (!isNaN(numA) && !isNaN(numB)) return numA - numB;
86
+ if (!isNaN(numA)) return -1;
87
+ if (!isNaN(numB)) return 1;
88
+ return a.localeCompare(b);
89
+ });
47
90
 
48
- if (!contractCode) {
49
- console.error("Contract code is empty.");
91
+ if (files.length === 0) {
92
+ this.failSpinner("No deploy scripts found.");
50
93
  return;
51
94
  }
52
95
 
53
- const leaderOnly = false;
54
- let deployParams: any = { code: contractCode, args: options.args, leaderOnly };
96
+ this.setSpinnerText(`Found ${files.length} deploy scripts. Executing...`);
55
97
 
56
- console.log("Starting contract deployment...");
57
- console.log("Deployment Parameters:", deployParams);
98
+ for (const file of files) {
99
+ const filePath = path.resolve(this.deployDir, file);
100
+ this.setSpinnerText(`Executing script: ${filePath}`);
101
+ try {
102
+ if (file.endsWith(".ts")) {
103
+ await this.executeTsScript(filePath);
104
+ } else {
105
+ await this.executeJsScript(filePath);
106
+ }
107
+ } catch (error) {
108
+ this.failSpinner(`Error executing script: ${filePath}`, error);
109
+ }
110
+ }
111
+ }
58
112
 
113
+ async deploy(options: DeployOptions): Promise<void> {
59
114
  try {
60
- const hash = await this.genlayerClient.deployContract(deployParams) as any;
115
+ this.startSpinner("Setting up the deployment environment...");
116
+ await this.genlayerClient.initializeConsensusSmartContract();
117
+
118
+ if (!options.contract) {
119
+ this.failSpinner("No contract specified for deployment.");
120
+ return;
121
+ }
122
+ this.setSpinnerText("Reading contract code...");
123
+ const contractCode = this.readContractCode(options.contract);
124
+
125
+ if (!contractCode) {
126
+ this.failSpinner("Contract code is empty.");
127
+ return;
128
+ }
129
+
130
+ const leaderOnly = false;
131
+ const deployParams: any = { code: contractCode, args: options.args, leaderOnly };
132
+
133
+ this.setSpinnerText("Starting contract deployment...");
134
+ this.log("Deployment Parameters:", deployParams);
61
135
 
62
- const result = await this.genlayerClient.waitForTransactionReceipt({hash, retries: 15, interval: 2000})
136
+ const hash = (await this.genlayerClient.deployContract(deployParams)) as any;
137
+ const result = await this.genlayerClient.waitForTransactionReceipt({
138
+ hash,
139
+ retries: 15,
140
+ interval: 2000,
141
+ status: TransactionStatus.ACCEPTED,
142
+ });
63
143
 
64
- console.log("Contract deployed successfully.");
65
- console.log("Transaction Hash:", hash);
66
- console.log("Contract Address:", result.data?.contract_address);
144
+ this.succeedSpinner("Contract deployed successfully.", {
145
+ "Transaction Hash": hash,
146
+ "Contract Address": result.data?.contract_address,
147
+ });
67
148
  } catch (error) {
68
- console.error("Error deploying contract:", error);
69
- throw new Error("Contract deployment failed.");
149
+ this.failSpinner("Error deploying contract", error);
70
150
  }
71
151
  }
72
152
  }
@@ -12,7 +12,11 @@ export function initializeContractsCommands(program: Command) {
12
12
  .option("--args <args...>", "Positional arguments for the contract (space-separated, use quotes for multi-word arguments)", [])
13
13
  .action(async (options: DeployOptions) => {
14
14
  const deployer = new DeployAction();
15
- await deployer.deploy(options);
15
+ if(options.contract){
16
+ await deployer.deploy(options);
17
+ }else {
18
+ await deployer.deployScripts();
19
+ }
16
20
  });
17
21
 
18
22
  program
@@ -1,8 +1,8 @@
1
1
  import { Command } from "commander";
2
2
 
3
3
  import simulatorService from "../../lib/services/simulator";
4
- import { initAction, InitActionOptions } from "./init";
5
- import { startAction, StartActionOptions } from "./start";
4
+ import { InitAction, InitActionOptions } from "./init";
5
+ import { StartAction, StartActionOptions } from "./start";
6
6
  import {localnetCompatibleVersion} from "../../lib/config/simulator";
7
7
  import {StopAction} from "./stop";
8
8
 
@@ -14,7 +14,10 @@ export function initializeGeneralCommands(program: Command) {
14
14
  .option("--headless", "Headless mode", false)
15
15
  .option("--reset-db", "Reset Database", false)
16
16
  .option("--localnet-version <localnetVersion>", "Select a specific localnet version", localnetCompatibleVersion)
17
- .action((options: InitActionOptions) => initAction(options, simulatorService));
17
+ .action(async (options: InitActionOptions) => {
18
+ const initAction = new InitAction();
19
+ await initAction.execute(options)
20
+ });
18
21
 
19
22
  program
20
23
  .command("up")
@@ -23,7 +26,10 @@ export function initializeGeneralCommands(program: Command) {
23
26
  .option("--numValidators <numValidators>", "Number of validators", "5")
24
27
  .option("--headless", "Headless mode", false)
25
28
  .option("--reset-db", "Reset Database", false)
26
- .action((options: StartActionOptions) => startAction(options, simulatorService));
29
+ .action(async (options: StartActionOptions) => {
30
+ const startAction = new StartAction();
31
+ await startAction.execute(options);
32
+ });
27
33
 
28
34
  program
29
35
  .command("stop")