genlayer 0.28.1 → 0.30.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/.github/workflows/cli-docs.yml +121 -0
- package/.github/workflows/publish.yml +2 -0
- package/CHANGELOG.md +12 -0
- package/README.md +11 -11
- package/dist/index.js +7 -5
- package/package.json +3 -2
- package/scripts/generate-cli-docs.mjs +246 -0
- package/src/commands/{validators → localnet}/index.ts +9 -3
- package/src/commands/{validators → localnet}/validators.ts +2 -0
- package/src/index.ts +1 -1
- package/tests/actions/validators.test.ts +1 -1
- package/tests/commands/{validator.test.ts → localnet.test.ts} +15 -11
- package/tests/index.test.ts +1 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
name: Generate CLI Docs and PR to genlayer-docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
release:
|
|
6
|
+
types: [published]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
generate-and-sync:
|
|
10
|
+
# Skip for pre-releases (tags containing '-') unless manually dispatched
|
|
11
|
+
if: github.event_name != 'release' || !contains(github.event.release.tag_name, '-')
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout CLI repo
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Setup Node.js
|
|
20
|
+
uses: actions/setup-node@v4
|
|
21
|
+
with:
|
|
22
|
+
node-version: '20'
|
|
23
|
+
cache: 'npm'
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: npm ci
|
|
27
|
+
|
|
28
|
+
- name: Build
|
|
29
|
+
run: npm run build
|
|
30
|
+
|
|
31
|
+
- name: Determine version for docs
|
|
32
|
+
id: version
|
|
33
|
+
run: |
|
|
34
|
+
if [ "${{ github.event_name }}" = "release" ]; then
|
|
35
|
+
echo "value=${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
|
|
36
|
+
else
|
|
37
|
+
# Prefer package.json version when not a release event
|
|
38
|
+
echo "value=$(node -p \"require('./package.json').version\")" >> $GITHUB_OUTPUT
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
- name: Generate CLI docs (MDX)
|
|
42
|
+
env:
|
|
43
|
+
DOCS_CLEAN: 'true'
|
|
44
|
+
DOCS_VERSION: ${{ steps.version.outputs.value }}
|
|
45
|
+
run: node scripts/generate-cli-docs.mjs | cat
|
|
46
|
+
|
|
47
|
+
- name: Set up Git (for committing to CLI repo)
|
|
48
|
+
run: |
|
|
49
|
+
git config user.name "github-actions[bot]"
|
|
50
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
51
|
+
|
|
52
|
+
- name: Commit and push docs back to CLI repo (non-beta releases)
|
|
53
|
+
if: github.event_name == 'release' && !contains(github.event.release.tag_name, '-')
|
|
54
|
+
run: |
|
|
55
|
+
set -euo pipefail
|
|
56
|
+
if [ -n "$(git status --porcelain docs/api-references || true)" ]; then
|
|
57
|
+
git add docs/api-references
|
|
58
|
+
VERSION=${{ steps.version.outputs.value }}
|
|
59
|
+
git commit -m "docs(cli): update API reference for ${VERSION}"
|
|
60
|
+
git push
|
|
61
|
+
else
|
|
62
|
+
echo "No docs changes to commit"
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
- name: Checkout docs repo
|
|
66
|
+
uses: actions/checkout@v4
|
|
67
|
+
with:
|
|
68
|
+
repository: genlayerlabs/genlayer-docs
|
|
69
|
+
token: ${{ secrets.DOCS_REPO_TOKEN || secrets.GITHUB_TOKEN }}
|
|
70
|
+
path: docs-repo
|
|
71
|
+
fetch-depth: 0
|
|
72
|
+
|
|
73
|
+
- name: Prepare branch
|
|
74
|
+
working-directory: docs-repo
|
|
75
|
+
run: |
|
|
76
|
+
set -euo pipefail
|
|
77
|
+
git config user.name "github-actions[bot]"
|
|
78
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
79
|
+
BRANCH="docs/cli/${{ github.repository }}-${{ github.ref_name }}-${{ github.run_id }}"
|
|
80
|
+
git switch -c "$BRANCH" || git switch "$BRANCH"
|
|
81
|
+
echo "BRANCH=$BRANCH" >> $GITHUB_ENV
|
|
82
|
+
|
|
83
|
+
- name: Sync CLI docs into docs repo
|
|
84
|
+
run: |
|
|
85
|
+
set -euo pipefail
|
|
86
|
+
mkdir -p docs-repo/pages/api-references/genlayer-cli
|
|
87
|
+
rsync -a --delete "${{ github.workspace }}/docs/api-references/" docs-repo/pages/api-references/genlayer-cli/
|
|
88
|
+
echo "Synced files:" && ls -la docs-repo/pages/api-references/genlayer-cli | cat
|
|
89
|
+
|
|
90
|
+
- name: Commit changes
|
|
91
|
+
working-directory: docs-repo
|
|
92
|
+
run: |
|
|
93
|
+
set -euo pipefail
|
|
94
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
95
|
+
git add pages/api-references/genlayer-cli
|
|
96
|
+
git commit -m "docs(cli): sync API reference ${VERSION:-${{ env.VERSION }}}"
|
|
97
|
+
git push --set-upstream origin "$BRANCH"
|
|
98
|
+
echo "HAS_CHANGES=true" >> $GITHUB_ENV
|
|
99
|
+
else
|
|
100
|
+
echo "No changes to commit"
|
|
101
|
+
echo "HAS_CHANGES=false" >> $GITHUB_ENV
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
- name: Create PR in docs repo
|
|
105
|
+
if: env.HAS_CHANGES == 'true'
|
|
106
|
+
uses: peter-evans/create-pull-request@v6
|
|
107
|
+
with:
|
|
108
|
+
token: ${{ secrets.DOCS_REPO_TOKEN || secrets.GITHUB_TOKEN }}
|
|
109
|
+
path: docs-repo
|
|
110
|
+
commit-message: "docs(cli): sync API reference ${{ steps.version.outputs.value }}"
|
|
111
|
+
branch: ${{ env.BRANCH }}
|
|
112
|
+
title: "docs(cli): sync CLI API reference ${{ steps.version.outputs.value }}"
|
|
113
|
+
body: |
|
|
114
|
+
This PR updates the GenlayerCLI API Reference generated automatically from `${{ github.repository }}`.
|
|
115
|
+
|
|
116
|
+
- Version: `${{ steps.version.outputs.value }}`
|
|
117
|
+
- Source commit: `${{ github.sha }}`
|
|
118
|
+
- Trigger: `${{ github.event_name }}`
|
|
119
|
+
labels: documentation, cli
|
|
120
|
+
|
|
121
|
+
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.30.0 (2025-09-03)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* genlayercli api reference auto generated ([#247](https://github.com/yeagerai/genlayer-cli/issues/247)) ([b08c342](https://github.com/yeagerai/genlayer-cli/commit/b08c34218b9f02a1d8d7f1c0b532ab55cc4ca5af))
|
|
8
|
+
|
|
9
|
+
## 0.29.0 (2025-09-03)
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* rename studio validators commands ([#249](https://github.com/yeagerai/genlayer-cli/issues/249)) ([fb875e5](https://github.com/yeagerai/genlayer-cli/commit/fb875e5569d21346507eab07b8eacb88b395a15d))
|
|
14
|
+
|
|
3
15
|
## 0.28.1 (2025-09-03)
|
|
4
16
|
|
|
5
17
|
## 0.28.0 (2025-09-03)
|
package/README.md
CHANGED
|
@@ -238,13 +238,13 @@ EXAMPLES:
|
|
|
238
238
|
genlayer update ollama --model deepseek-r1 --remove
|
|
239
239
|
```
|
|
240
240
|
|
|
241
|
-
#### Validator Management
|
|
241
|
+
#### Localnet Validator Management
|
|
242
242
|
|
|
243
|
-
Manage validator operations.
|
|
243
|
+
Manage localnet validator operations.
|
|
244
244
|
|
|
245
245
|
```bash
|
|
246
246
|
USAGE:
|
|
247
|
-
genlayer validators <command> [options]
|
|
247
|
+
genlayer localnet validators <command> [options]
|
|
248
248
|
|
|
249
249
|
COMMANDS:
|
|
250
250
|
get [--address <validatorAddress>] Retrieve details of a specific validator or all validators
|
|
@@ -272,16 +272,16 @@ OPTIONS (create):
|
|
|
272
272
|
--model <model> Specify the model for the validator
|
|
273
273
|
|
|
274
274
|
EXAMPLES:
|
|
275
|
-
genlayer validators get
|
|
276
|
-
genlayer validators get --address 0x123456789abcdef
|
|
275
|
+
genlayer localnet validators get
|
|
276
|
+
genlayer localnet validators get --address 0x123456789abcdef
|
|
277
277
|
|
|
278
|
-
genlayer validators count
|
|
279
|
-
genlayer validators delete --address 0x123456789abcdef
|
|
280
|
-
genlayer validators update 0x123456789abcdef --stake 100 --provider openai --model gpt-4
|
|
278
|
+
genlayer localnet validators count
|
|
279
|
+
genlayer localnet validators delete --address 0x123456789abcdef
|
|
280
|
+
genlayer localnet validators update 0x123456789abcdef --stake 100 --provider openai --model gpt-4
|
|
281
281
|
|
|
282
|
-
genlayer validators create
|
|
283
|
-
genlayer validators create --stake 50 --provider openai --model gpt-4
|
|
284
|
-
genlayer validators create-random --count 3 --providers openai --models gpt-4 gpt-4o
|
|
282
|
+
genlayer localnet validators create
|
|
283
|
+
genlayer localnet validators create --stake 50 --provider openai --model gpt-4
|
|
284
|
+
genlayer localnet validators create-random --count 3 --providers openai --models gpt-4 gpt-4o
|
|
285
285
|
```
|
|
286
286
|
|
|
287
287
|
### Running the CLI from the repository
|
package/dist/index.js
CHANGED
|
@@ -17856,7 +17856,7 @@ var require_semver2 = __commonJS({
|
|
|
17856
17856
|
import { program } from "commander";
|
|
17857
17857
|
|
|
17858
17858
|
// package.json
|
|
17859
|
-
var version = "0.
|
|
17859
|
+
var version = "0.30.0";
|
|
17860
17860
|
var package_default = {
|
|
17861
17861
|
name: "genlayer",
|
|
17862
17862
|
version,
|
|
@@ -17874,7 +17874,8 @@ var package_default = {
|
|
|
17874
17874
|
build: "cross-env NODE_ENV=production node esbuild.config.js",
|
|
17875
17875
|
release: "release-it --ci",
|
|
17876
17876
|
"release-beta": "release-it --ci --preRelease=beta",
|
|
17877
|
-
postinstall: "node ./scripts/postinstall.js"
|
|
17877
|
+
postinstall: "node ./scripts/postinstall.js",
|
|
17878
|
+
"docs:cli": "node scripts/generate-cli-docs.mjs"
|
|
17878
17879
|
},
|
|
17879
17880
|
repository: {
|
|
17880
17881
|
type: "git",
|
|
@@ -42888,7 +42889,7 @@ function initializeConfigCommands(program2) {
|
|
|
42888
42889
|
return program2;
|
|
42889
42890
|
}
|
|
42890
42891
|
|
|
42891
|
-
// src/commands/
|
|
42892
|
+
// src/commands/localnet/validators.ts
|
|
42892
42893
|
import inquirer4 from "inquirer";
|
|
42893
42894
|
var ValidatorsAction = class extends BaseAction {
|
|
42894
42895
|
async getValidator(options) {
|
|
@@ -43082,10 +43083,11 @@ var ValidatorsAction = class extends BaseAction {
|
|
|
43082
43083
|
}
|
|
43083
43084
|
};
|
|
43084
43085
|
|
|
43085
|
-
// src/commands/
|
|
43086
|
+
// src/commands/localnet/index.ts
|
|
43086
43087
|
function initializeValidatorCommands(program2) {
|
|
43087
43088
|
const validatorsAction = new ValidatorsAction();
|
|
43088
|
-
const
|
|
43089
|
+
const localnetCommand = program2.command("localnet").description("Manage localnet operations");
|
|
43090
|
+
const validatorsCommand = localnetCommand.command("validators").description("Manage localnet validators operations");
|
|
43089
43091
|
validatorsCommand.command("get").description("Retrieve details of a specific validator or all validators").option("--address <validatorAddress>", "The address of the validator to retrieve (omit to retrieve all validators)").action(async (options) => {
|
|
43090
43092
|
await validatorsAction.getValidator({ address: options.address });
|
|
43091
43093
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genlayer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "GenLayer Command Line Tool",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"build": "cross-env NODE_ENV=production node esbuild.config.js",
|
|
16
16
|
"release": "release-it --ci",
|
|
17
17
|
"release-beta": "release-it --ci --preRelease=beta",
|
|
18
|
-
"postinstall": "node ./scripts/postinstall.js"
|
|
18
|
+
"postinstall": "node ./scripts/postinstall.js",
|
|
19
|
+
"docs:cli": "node scripts/generate-cli-docs.mjs"
|
|
19
20
|
},
|
|
20
21
|
"repository": {
|
|
21
22
|
"type": "git",
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
|
|
7
|
+
function escapeMdx(text) {
|
|
8
|
+
if (!text) return '';
|
|
9
|
+
return String(text).replace(/</g, '<').replace(/>/g, '>');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function formatArg(arg) {
|
|
13
|
+
const base = arg.variadic ? `${arg.name}...` : arg.name;
|
|
14
|
+
return arg.required ? `<${base}>` : `[${base}]`;
|
|
15
|
+
}
|
|
16
|
+
function toSlug(text) {
|
|
17
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function makeCommandFilepath(commandPath) {
|
|
21
|
+
const parts = commandPath.split(' ');
|
|
22
|
+
if (parts.length === 1) return { relDir: '', filename: `${toSlug(parts[0])}.mdx` };
|
|
23
|
+
return { relDir: parts.slice(0, -1).map(toSlug).join('/'), filename: `${toSlug(parts[parts.length - 1])}.mdx` };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function renderOptionsTable(options) {
|
|
27
|
+
if (!options.length) return 'No options.\n';
|
|
28
|
+
const rows = options
|
|
29
|
+
.map((o) => `| ${escapeMdx(o.short ?? '')} | ${escapeMdx(o.long ?? '')} | ${escapeMdx(o.description ?? '')} | ${o.required ? 'Yes' : 'No'} | ${o.defaultValue === undefined ? '' : `\`${escapeMdx(String(o.defaultValue))}\``} |`)
|
|
30
|
+
.join('\n');
|
|
31
|
+
return ['| Short | Long | Description | Required | Default |', '| --- | --- | --- | :---: | --- |', rows].join('\n');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function renderArgsList(args) {
|
|
35
|
+
if (!args.length) return 'No positional arguments.\n';
|
|
36
|
+
return args.map((a) => `- \`${formatArg(a)}\``).join('\n');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function generatePageForCommand(help, programName, commandPath) {
|
|
40
|
+
const { description, usage, args, options, subcommands } = help;
|
|
41
|
+
const title = commandPath || programName;
|
|
42
|
+
const header = `---\ntitle: ${escapeMdx(title)}\n---`;
|
|
43
|
+
const parts = [header];
|
|
44
|
+
if (description) parts.push('', description);
|
|
45
|
+
if (usage) parts.push('', '### Usage', '', `\`${usage}\``);
|
|
46
|
+
if (args && args.length) parts.push('', '### Arguments', '', renderArgsList(args));
|
|
47
|
+
parts.push('', '### Options', '', renderOptionsTable(options || []));
|
|
48
|
+
if (subcommands && subcommands.length) {
|
|
49
|
+
parts.push('', '### Subcommands', '');
|
|
50
|
+
for (const sc of subcommands) {
|
|
51
|
+
parts.push(`- \`${programName} ${sc.name}\` — ${escapeMdx(sc.description ?? '')}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const body = parts.join('\n').trim() + '\n';
|
|
55
|
+
const { relDir, filename } = makeCommandFilepath(commandPath || programName);
|
|
56
|
+
return { relDir, filename, content: body };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function generateIndexPage(rootHelp, programName, pkgVersion, pkgDescription) {
|
|
60
|
+
const title = `${programName} Commands`;
|
|
61
|
+
const header = `---\ntitle: ${escapeMdx(title)}\n---`;
|
|
62
|
+
const intro = rootHelp.description || pkgDescription || '';
|
|
63
|
+
const lines = [header, '', intro, `Version: \`${pkgVersion}\``, '', '### Command List', ''];
|
|
64
|
+
for (const sc of rootHelp.subcommands || []) {
|
|
65
|
+
lines.push(`- \`${programName} ${sc.name}\` — ${escapeMdx(sc.description ?? '')}`);
|
|
66
|
+
}
|
|
67
|
+
lines.push('', '---', '', 'This reference is auto-generated. Do not edit manually.');
|
|
68
|
+
return { relDir: '', filename: 'index.mdx', content: lines.join('\n') + '\n' };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function runHelp(args) {
|
|
72
|
+
const res = spawnSync(process.execPath, ['dist/index.js', ...args, '--help'], {
|
|
73
|
+
encoding: 'utf8',
|
|
74
|
+
timeout: 30000,
|
|
75
|
+
});
|
|
76
|
+
if ((res.status !== 0 || res.error) && res.stdout.trim() === '') {
|
|
77
|
+
const reason = res.error?.message || res.stderr || `exitCode=${res.status}`;
|
|
78
|
+
throw new Error(`Failed to run help for: ${args.join(' ')} (${reason})`);
|
|
79
|
+
}
|
|
80
|
+
return res.stdout;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function parseHelp(text, programName, commandPath) {
|
|
84
|
+
const lines = text.split(/\r?\n/);
|
|
85
|
+
let description = '';
|
|
86
|
+
let usage = '';
|
|
87
|
+
const options = [];
|
|
88
|
+
const subcommands = [];
|
|
89
|
+
const args = [];
|
|
90
|
+
|
|
91
|
+
// Accumulate description between the first blank after Usage and before Options/Commands
|
|
92
|
+
let inOptions = false;
|
|
93
|
+
let inCommands = false;
|
|
94
|
+
|
|
95
|
+
// Usage
|
|
96
|
+
const usageIdx = lines.findIndex((l) => l.startsWith('Usage:'));
|
|
97
|
+
if (usageIdx !== -1) {
|
|
98
|
+
const usageLine = lines[usageIdx];
|
|
99
|
+
// Replace program binary name (index) by programName
|
|
100
|
+
const afterColon = usageLine.replace(/^Usage:\s*/, '');
|
|
101
|
+
const replaced = afterColon.replace(/^\S+/, programName);
|
|
102
|
+
usage = `$ ${replaced}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Description
|
|
106
|
+
for (let i = usageIdx + 1; i < lines.length; i += 1) {
|
|
107
|
+
const l = lines[i];
|
|
108
|
+
if (l.trim() === '') continue;
|
|
109
|
+
if (l.startsWith('Options:') || l.startsWith('Commands:')) break;
|
|
110
|
+
description += (description ? '\n' : '') + l.trim();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Parse sections
|
|
114
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
115
|
+
const l = lines[i];
|
|
116
|
+
if (l.startsWith('Options:')) { inOptions = true; inCommands = false; continue; }
|
|
117
|
+
if (l.startsWith('Commands:')) { inCommands = true; inOptions = false; continue; }
|
|
118
|
+
if (/^\s*$/.test(l)) continue;
|
|
119
|
+
|
|
120
|
+
if (inOptions) {
|
|
121
|
+
// e.g., " -V, --version output the version number"
|
|
122
|
+
const m = l.match(/^\s*(-\w)?,?\s*(--[\w-]+)?\s{2,}(.+)$/);
|
|
123
|
+
if (m) {
|
|
124
|
+
const short = m[1] || '';
|
|
125
|
+
const long = m[2] || '';
|
|
126
|
+
const desc = m[3] || '';
|
|
127
|
+
options.push({ short, long, description: desc, required: false });
|
|
128
|
+
}
|
|
129
|
+
} else if (inCommands) {
|
|
130
|
+
// e.g., " deploy [options] Deploy intelligent contracts"
|
|
131
|
+
const m = l.match(/^\s*([\w-]+)(?:\s<[^>]+>|\s\[[^\]]+\])*\s{2,}(.+)$/);
|
|
132
|
+
if (m) {
|
|
133
|
+
const cmdToken = m[1];
|
|
134
|
+
const desc = m[2] || '';
|
|
135
|
+
const name = cmdToken.trim();
|
|
136
|
+
if (name !== 'help') {
|
|
137
|
+
subcommands.push({ name, description: desc });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Derive args from usage (after commandPath)
|
|
144
|
+
if (usage) {
|
|
145
|
+
const usageCmd = usage.replace(/^`?\$\s+/, '').replace(/`?$/, '');
|
|
146
|
+
const tokens = usageCmd.split(/\s+/);
|
|
147
|
+
// find starting index of commandPath tokens
|
|
148
|
+
const cmdTokens = (commandPath ? `${programName} ${commandPath}` : programName).split(' ');
|
|
149
|
+
const start = tokens.findIndex((t, idx) => tokens.slice(idx, idx + cmdTokens.length).join(' ') === cmdTokens.join(' '));
|
|
150
|
+
const after = start >= 0 ? tokens.slice(start + cmdTokens.length) : [];
|
|
151
|
+
for (const t of after) {
|
|
152
|
+
if (t === '[options]') continue;
|
|
153
|
+
const m = t.match(/^<(.*)>$/) || t.match(/^\[(.*)\]$/);
|
|
154
|
+
if (m) {
|
|
155
|
+
const variadic = m[1].endsWith('...');
|
|
156
|
+
const name = variadic ? m[1].slice(0, -3) : m[1];
|
|
157
|
+
const required = t.startsWith('<');
|
|
158
|
+
args.push({ name, variadic, required });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return { description, usage, options, subcommands, args };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function ensureDir(dir) {
|
|
167
|
+
await fs.mkdir(dir, { recursive: true });
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function writePages(root, pages) {
|
|
171
|
+
for (const page of pages) {
|
|
172
|
+
const dir = path.join(root, page.relDir);
|
|
173
|
+
await ensureDir(dir);
|
|
174
|
+
const fullpath = path.join(dir, page.filename);
|
|
175
|
+
await fs.writeFile(fullpath, page.content, 'utf8');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function rmrf(dir) {
|
|
180
|
+
try {
|
|
181
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
182
|
+
} catch {}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function readPackageInfo() {
|
|
186
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
187
|
+
const pkgPath = path.join(here, '..', 'package.json');
|
|
188
|
+
const raw = await fs.readFile(pkgPath, 'utf8');
|
|
189
|
+
const json = JSON.parse(raw);
|
|
190
|
+
return { version: json.version, description: json.description };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function main() {
|
|
194
|
+
const { version: pkgVersion, description: pkgDescription } = await readPackageInfo();
|
|
195
|
+
const programName = 'genlayer';
|
|
196
|
+
|
|
197
|
+
// Ensure CLI is built before attempting to read help output
|
|
198
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
199
|
+
const cliPath = path.join(here, '..', 'dist', 'index.js');
|
|
200
|
+
try {
|
|
201
|
+
await fs.access(cliPath);
|
|
202
|
+
} catch {
|
|
203
|
+
throw new Error('CLI not built. Please run "npm run build" first.');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const rootHelpText = runHelp([]);
|
|
207
|
+
const rootHelp = parseHelp(rootHelpText, programName, '');
|
|
208
|
+
|
|
209
|
+
const outputDirFromEnv = process.env.DOCS_OUTPUT_DIR;
|
|
210
|
+
const clean = (process.env.DOCS_CLEAN || '').toLowerCase() === 'true';
|
|
211
|
+
|
|
212
|
+
const outputs = [];
|
|
213
|
+
// filter out auto 'help' just in case
|
|
214
|
+
rootHelp.subcommands = (rootHelp.subcommands || []).filter((c) => c.name !== 'help');
|
|
215
|
+
outputs.push(generateIndexPage(rootHelp, programName, pkgVersion, pkgDescription));
|
|
216
|
+
|
|
217
|
+
// BFS through subcommands by invoking help for each
|
|
218
|
+
const queue = [...(rootHelp.subcommands || [])].map((c) => ({ path: c.name }));
|
|
219
|
+
while (queue.length) {
|
|
220
|
+
const { path: cmdPath } = queue.shift();
|
|
221
|
+
const helpText = runHelp(cmdPath.split(' '));
|
|
222
|
+
const help = parseHelp(helpText, programName, cmdPath);
|
|
223
|
+
outputs.push(generatePageForCommand(help, programName, cmdPath));
|
|
224
|
+
for (const sc of help.subcommands || []) {
|
|
225
|
+
queue.push({ path: `${cmdPath} ${sc.name}` });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const defaultOut = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'docs', 'api-references');
|
|
230
|
+
const rootOut = outputDirFromEnv ? outputDirFromEnv : defaultOut;
|
|
231
|
+
if (clean) await rmrf(rootOut);
|
|
232
|
+
await writePages(rootOut, outputs);
|
|
233
|
+
|
|
234
|
+
const meta = {};
|
|
235
|
+
for (const c of (rootHelp.subcommands || []).map((c) => toSlug(c.name))) meta[c] = c;
|
|
236
|
+
await fs.writeFile(path.join(rootOut, '_meta.json'), JSON.stringify(meta, null, 2), 'utf8');
|
|
237
|
+
|
|
238
|
+
console.log(`Generated ${outputs.length} pages at ${rootOut}`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
main().catch((err) => {
|
|
242
|
+
console.error(err);
|
|
243
|
+
process.exit(1);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
|
|
@@ -4,9 +4,13 @@ import { ValidatorsAction } from "./validators";
|
|
|
4
4
|
export function initializeValidatorCommands(program: Command) {
|
|
5
5
|
const validatorsAction = new ValidatorsAction();
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const localnetCommand = program
|
|
8
|
+
.command("localnet")
|
|
9
|
+
.description("Manage localnet operations");
|
|
10
|
+
|
|
11
|
+
const validatorsCommand = localnetCommand
|
|
8
12
|
.command("validators")
|
|
9
|
-
.description("Manage
|
|
13
|
+
.description("Manage localnet validators operations");
|
|
10
14
|
|
|
11
15
|
validatorsCommand
|
|
12
16
|
.command("get")
|
|
@@ -77,7 +81,7 @@ export function initializeValidatorCommands(program: Command) {
|
|
|
77
81
|
.option("--stake <stake>", "Stake amount for the validator (default: 1)", "1")
|
|
78
82
|
.option(
|
|
79
83
|
"--config <config>",
|
|
80
|
-
|
|
84
|
+
`Optional JSON configuration for the validator (e.g., '{"max_tokens": 500, "temperature": 0.75}')`
|
|
81
85
|
)
|
|
82
86
|
.option("--provider <provider>", "Specify the provider for the validator")
|
|
83
87
|
.option("--model <model>", "Specify the model for the validator")
|
|
@@ -92,3 +96,5 @@ export function initializeValidatorCommands(program: Command) {
|
|
|
92
96
|
|
|
93
97
|
return program;
|
|
94
98
|
}
|
|
99
|
+
|
|
100
|
+
|
package/src/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {initializeGeneralCommands} from "../src/commands/general";
|
|
|
6
6
|
import {initializeKeygenCommands} from "../src/commands/keygen";
|
|
7
7
|
import {initializeContractsCommands} from "../src/commands/contracts";
|
|
8
8
|
import {initializeConfigCommands} from "../src/commands/config";
|
|
9
|
-
import {initializeValidatorCommands} from "../src/commands/
|
|
9
|
+
import {initializeValidatorCommands} from "../src/commands/localnet";
|
|
10
10
|
import {initializeUpdateCommands} from "../src/commands/update";
|
|
11
11
|
import {initializeScaffoldCommands} from "../src/commands/scaffold";
|
|
12
12
|
import {initializeNetworkCommands} from "../src/commands/network";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, test, vi, beforeEach, afterEach, expect } from "vitest";
|
|
2
|
-
import { ValidatorsAction } from "../../src/commands/
|
|
2
|
+
import { ValidatorsAction } from "../../src/commands/localnet/validators";
|
|
3
3
|
import { rpcClient } from "../../src/lib/clients/jsonRpcClient";
|
|
4
4
|
import inquirer from "inquirer";
|
|
5
5
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { vi, describe, beforeEach, afterEach, test, expect } from "vitest";
|
|
3
|
-
import { initializeValidatorCommands } from "../../src/commands/
|
|
4
|
-
import { ValidatorsAction } from "../../src/commands/
|
|
3
|
+
import { initializeValidatorCommands } from "../../src/commands/localnet";
|
|
4
|
+
import { ValidatorsAction } from "../../src/commands/localnet/validators";
|
|
5
5
|
|
|
6
|
-
vi.mock("../../src/commands/
|
|
6
|
+
vi.mock("../../src/commands/localnet/validators");
|
|
7
7
|
|
|
8
|
-
describe("
|
|
8
|
+
describe("localnet validator command", () => {
|
|
9
9
|
let program: Command;
|
|
10
10
|
|
|
11
11
|
beforeEach(() => {
|
|
@@ -18,7 +18,7 @@ describe("validators command", () => {
|
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
test("ValidatorsAction.getValidator is called with address option", async () => {
|
|
21
|
-
program.parse(["node", "test", "validators", "get", "--address", "mocked_address"]);
|
|
21
|
+
program.parse(["node", "test", "localnet", "validators", "get", "--address", "mocked_address"]);
|
|
22
22
|
expect(ValidatorsAction).toHaveBeenCalledTimes(1);
|
|
23
23
|
expect(ValidatorsAction.prototype.getValidator).toHaveBeenCalledWith({
|
|
24
24
|
address: "mocked_address",
|
|
@@ -26,13 +26,13 @@ describe("validators command", () => {
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
test("ValidatorsAction.getValidator is called without address option", async () => {
|
|
29
|
-
program.parse(["node", "test", "validators", "get"]);
|
|
29
|
+
program.parse(["node", "test", "localnet", "validators", "get"]);
|
|
30
30
|
expect(ValidatorsAction).toHaveBeenCalledTimes(1);
|
|
31
31
|
expect(ValidatorsAction.prototype.getValidator).toHaveBeenCalledWith({});
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
test("ValidatorsAction.deleteValidator is called with address option", async () => {
|
|
35
|
-
program.parse(["node", "test", "validators", "delete", "--address", "mocked_address"]);
|
|
35
|
+
program.parse(["node", "test", "localnet", "validators", "delete", "--address", "mocked_address"]);
|
|
36
36
|
expect(ValidatorsAction).toHaveBeenCalledTimes(1);
|
|
37
37
|
expect(ValidatorsAction.prototype.deleteValidator).toHaveBeenCalledWith({
|
|
38
38
|
address: "mocked_address",
|
|
@@ -40,13 +40,13 @@ describe("validators command", () => {
|
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
test("ValidatorsAction.deleteValidator is called without address option", async () => {
|
|
43
|
-
program.parse(["node", "test", "validators", "delete"]);
|
|
43
|
+
program.parse(["node", "test", "localnet", "validators", "delete"]);
|
|
44
44
|
expect(ValidatorsAction).toHaveBeenCalledTimes(1);
|
|
45
45
|
expect(ValidatorsAction.prototype.deleteValidator).toHaveBeenCalledWith({});
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
test("ValidatorsAction.countValidators is called", async () => {
|
|
49
|
-
program.parse(["node", "test", "validators", "count"]);
|
|
49
|
+
program.parse(["node", "test", "localnet", "validators", "count"]);
|
|
50
50
|
expect(ValidatorsAction).toHaveBeenCalledTimes(1);
|
|
51
51
|
expect(ValidatorsAction.prototype.countValidators).toHaveBeenCalled();
|
|
52
52
|
});
|
|
@@ -55,6 +55,7 @@ describe("validators command", () => {
|
|
|
55
55
|
program.parse([
|
|
56
56
|
"node",
|
|
57
57
|
"test",
|
|
58
|
+
"localnet",
|
|
58
59
|
"validators",
|
|
59
60
|
"update",
|
|
60
61
|
"mocked_address",
|
|
@@ -81,6 +82,7 @@ describe("validators command", () => {
|
|
|
81
82
|
program.parse([
|
|
82
83
|
"node",
|
|
83
84
|
"test",
|
|
85
|
+
"localnet",
|
|
84
86
|
"validators",
|
|
85
87
|
"create-random",
|
|
86
88
|
"--count",
|
|
@@ -98,7 +100,7 @@ describe("validators command", () => {
|
|
|
98
100
|
});
|
|
99
101
|
|
|
100
102
|
test("ValidatorsAction.createValidator is called with default stake", async () => {
|
|
101
|
-
program.parse(["node", "test", "validators", "create"]);
|
|
103
|
+
program.parse(["node", "test", "localnet", "validators", "create"]);
|
|
102
104
|
expect(ValidatorsAction).toHaveBeenCalledTimes(1);
|
|
103
105
|
expect(ValidatorsAction.prototype.createValidator).toHaveBeenCalledWith({
|
|
104
106
|
stake: "1",
|
|
@@ -110,6 +112,7 @@ describe("validators command", () => {
|
|
|
110
112
|
program.parse([
|
|
111
113
|
"node",
|
|
112
114
|
"test",
|
|
115
|
+
"localnet",
|
|
113
116
|
"validators",
|
|
114
117
|
"create",
|
|
115
118
|
"--stake",
|
|
@@ -123,5 +126,6 @@ describe("validators command", () => {
|
|
|
123
126
|
config: '{"temperature":0.8}',
|
|
124
127
|
});
|
|
125
128
|
});
|
|
126
|
-
|
|
127
129
|
});
|
|
130
|
+
|
|
131
|
+
|
package/tests/index.test.ts
CHANGED