Skip to content

Commit ec6f711

Browse files
committed
feat: add npx skill-publish CLI and skills endpoint compatibility
Signed-off-by: Michael Kantor <[email protected]>
1 parent 93aee11 commit ec6f711

File tree

3 files changed

+208
-1
lines changed

3 files changed

+208
-1
lines changed

bin/cli.mjs

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
#!/usr/bin/env node
2+
3+
import { readFileSync } from 'node:fs';
4+
import path from 'node:path';
5+
import { fileURLToPath, pathToFileURL } from 'node:url';
6+
7+
const __filename = fileURLToPath(import.meta.url);
8+
const __dirname = path.dirname(__filename);
9+
const packagePath = path.resolve(__dirname, '..', 'package.json');
10+
const packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));
11+
12+
const HELP = `skill-publish CLI
13+
14+
Usage:
15+
npx skill-publish --api-key <key> --skill-dir <dir> [options]
16+
17+
Required:
18+
--api-key <key> Registry Broker API key (or use RB_API_KEY env var)
19+
--skill-dir <dir> Skill package directory containing SKILL.md + skill.json
20+
21+
Options:
22+
--api-base-url <url> Broker API base URL (default: https://hol.org/registry/api/v1)
23+
--account-id <id> Optional Hedera account ID
24+
--name <name> Optional skill name override
25+
--version <version> Optional skill version override
26+
--annotate <bool> Enable GitHub annotation behavior (default: false for CLI)
27+
--no-annotate Disable GitHub annotations
28+
--github-token <token> GitHub token for annotations
29+
--stamp-repo-commit <bool> Stamp repo/commit fields (default: true)
30+
--no-stamp-repo-commit Disable repo/commit stamping
31+
--poll-timeout-ms <ms> Publish poll timeout (default: 720000)
32+
--poll-interval-ms <ms> Publish poll interval (default: 4000)
33+
--help Show this help
34+
-v Show CLI version
35+
36+
Examples:
37+
RB_API_KEY=rbk_xxx npx skill-publish --skill-dir ./skills/my-skill
38+
npx skill-publish --api-key rbk_xxx --skill-dir ./skills/my-skill --version 1.2.3
39+
`;
40+
41+
const FLAG_ENV_MAP = new Map([
42+
['--api-base-url', 'INPUT_API_BASE_URL'],
43+
['--api-key', 'INPUT_API_KEY'],
44+
['--account-id', 'INPUT_ACCOUNT_ID'],
45+
['--skill-dir', 'INPUT_SKILL_DIR'],
46+
['--name', 'INPUT_NAME'],
47+
['--version', 'INPUT_VERSION'],
48+
['--stamp-repo-commit', 'INPUT_STAMP_REPO_COMMIT'],
49+
['--poll-timeout-ms', 'INPUT_POLL_TIMEOUT_MS'],
50+
['--poll-interval-ms', 'INPUT_POLL_INTERVAL_MS'],
51+
['--annotate', 'INPUT_ANNOTATE'],
52+
['--github-token', 'INPUT_GITHUB_TOKEN'],
53+
]);
54+
55+
const BOOLEAN_FLAGS = new Set(['--annotate', '--stamp-repo-commit']);
56+
57+
function fail(message) {
58+
process.stderr.write(`Error: ${message}\n`);
59+
process.stderr.write('Run `npx skill-publish --help` for usage.\n');
60+
process.exit(1);
61+
}
62+
63+
function parseArgs(args) {
64+
for (let index = 0; index < args.length; index += 1) {
65+
const arg = args[index];
66+
67+
if (arg === '--help' || arg === '-h') {
68+
process.stdout.write(`${HELP}\n`);
69+
process.exit(0);
70+
}
71+
72+
if (arg === '-v') {
73+
process.stdout.write(`${String(packageJson.version)}\n`);
74+
process.exit(0);
75+
}
76+
77+
if (arg === '--version') {
78+
const next = args[index + 1];
79+
if (!next || next.startsWith('--')) {
80+
process.stdout.write(`${String(packageJson.version)}\n`);
81+
process.exit(0);
82+
}
83+
}
84+
85+
if (arg.startsWith('--no-')) {
86+
const positive = `--${arg.slice(5)}`;
87+
if (!BOOLEAN_FLAGS.has(positive)) {
88+
fail(`Unknown flag: ${arg}`);
89+
}
90+
process.env[FLAG_ENV_MAP.get(positive)] = 'false';
91+
continue;
92+
}
93+
94+
let key = arg;
95+
let value = '';
96+
if (arg.startsWith('--') && arg.includes('=')) {
97+
const splitIndex = arg.indexOf('=');
98+
key = arg.slice(0, splitIndex);
99+
value = arg.slice(splitIndex + 1);
100+
}
101+
102+
if (!FLAG_ENV_MAP.has(key)) {
103+
fail(`Unknown flag: ${key}`);
104+
}
105+
106+
if (arg.includes('=')) {
107+
process.env[FLAG_ENV_MAP.get(key)] = value;
108+
continue;
109+
}
110+
111+
const next = args[index + 1];
112+
if (next && !next.startsWith('--')) {
113+
process.env[FLAG_ENV_MAP.get(key)] = next;
114+
index += 1;
115+
continue;
116+
}
117+
118+
if (BOOLEAN_FLAGS.has(key)) {
119+
process.env[FLAG_ENV_MAP.get(key)] = 'true';
120+
continue;
121+
}
122+
123+
fail(`Missing value for ${key}`);
124+
}
125+
}
126+
127+
function applyDefaults() {
128+
if (!process.env.INPUT_API_KEY && process.env.RB_API_KEY) {
129+
process.env.INPUT_API_KEY = process.env.RB_API_KEY;
130+
}
131+
if (!process.env.INPUT_SKILL_DIR) {
132+
process.env.INPUT_SKILL_DIR = '.';
133+
}
134+
if (!process.env.INPUT_ANNOTATE) {
135+
process.env.INPUT_ANNOTATE = 'false';
136+
}
137+
if (!process.env.INPUT_STAMP_REPO_COMMIT) {
138+
process.env.INPUT_STAMP_REPO_COMMIT = 'true';
139+
}
140+
if (!process.env.INPUT_POLL_TIMEOUT_MS) {
141+
process.env.INPUT_POLL_TIMEOUT_MS = '720000';
142+
}
143+
if (!process.env.INPUT_POLL_INTERVAL_MS) {
144+
process.env.INPUT_POLL_INTERVAL_MS = '4000';
145+
}
146+
}
147+
148+
async function run() {
149+
parseArgs(process.argv.slice(2));
150+
applyDefaults();
151+
152+
if (!process.env.INPUT_API_KEY) {
153+
fail('Missing API key. Pass --api-key or set RB_API_KEY.');
154+
}
155+
156+
const entrypointUrl = pathToFileURL(
157+
path.resolve(__dirname, '..', 'entrypoint.mjs'),
158+
).href;
159+
await import(entrypointUrl);
160+
}
161+
162+
run().catch((error) => {
163+
const message = error instanceof Error ? error.message : String(error);
164+
process.stderr.write(`Error: ${message}\n`);
165+
process.exit(1);
166+
});

entrypoint.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ const findExistingSkillVersion = async (params) => {
180180
const { apiBaseUrl, apiKey, name, version } = params;
181181
const response = await requestJson({
182182
method: 'GET',
183-
url: buildApiUrl(apiBaseUrl, '/skills/list', {
183+
url: buildApiUrl(apiBaseUrl, '/skills', {
184184
name,
185185
version,
186186
limit: 20,

package.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "skill-publish",
3+
"version": "1.0.0",
4+
"description": "Publish trustless, immutable, on-chain skill releases via the Hashgraph Online Registry Broker.",
5+
"type": "module",
6+
"bin": {
7+
"skill-publish": "bin/cli.mjs"
8+
},
9+
"files": [
10+
"action.yml",
11+
"entrypoint.mjs",
12+
"bin/",
13+
"schemas/",
14+
"examples/",
15+
"apis.json",
16+
"llms.txt",
17+
"README.md",
18+
"CITATION.cff"
19+
],
20+
"keywords": [
21+
"hcs-26",
22+
"skill-registry",
23+
"registry-broker",
24+
"hedera",
25+
"cli",
26+
"github-action"
27+
],
28+
"repository": {
29+
"type": "git",
30+
"url": "git+https://github.com/hashgraph-online/skill-publish.git"
31+
},
32+
"license": "Apache-2.0",
33+
"author": "Hashgraph Online <[email protected]>",
34+
"engines": {
35+
"node": ">=20"
36+
},
37+
"scripts": {
38+
"lint": "node --check entrypoint.mjs && node --check bin/cli.mjs",
39+
"smoke:help": "node bin/cli.mjs --help"
40+
}
41+
}

0 commit comments

Comments
 (0)