feat: hooks-transforms module with skill-install transform
This commit is contained in:
parent
2c2c24a310
commit
b49063879c
39
SKILL.md
39
SKILL.md
|
|
@ -1,26 +1,25 @@
|
||||||
---
|
---
|
||||||
name: my-skill
|
name: hooks-transforms
|
||||||
description: "TODO: describe what this skill does and when to use it."
|
description: "OpenClaw hooks transform modules for webhook payload processing"
|
||||||
---
|
---
|
||||||
|
|
||||||
# my-skill
|
|
||||||
|
|
||||||
TODO: one-line description.
|
|
||||||
|
|
||||||
> Auth (CLIENT_KEY) is loaded automatically from `~/.openclaw/.env`.
|
> Auth (CLIENT_KEY) is loaded automatically from `~/.openclaw/.env`.
|
||||||
|
|
||||||
## Run
|
## Transforms
|
||||||
|
|
||||||
```bash
|
### skill-install
|
||||||
bun scripts/run.ts <command> [args] [--dry-run]
|
|
||||||
|
Transform for `skill-update` webhook events. Processes payload containing skill git repos and generates installation instructions.
|
||||||
|
|
||||||
|
**Expected payload:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"skills": [
|
||||||
|
{
|
||||||
|
"repo_url": "http://...",
|
||||||
|
"skill_slug": "my-skill",
|
||||||
|
"git_ref": "v1.0.0",
|
||||||
|
"repo_subpath": "optional/subpath"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
| Command | Description |
|
|
||||||
|---------|-------------|
|
|
||||||
| `run <arg>` | TODO: describe |
|
|
||||||
|
|
||||||
## Output
|
|
||||||
|
|
||||||
Returns JSON: `{ "status": "success" | "failed", "data": ... }`
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "hooks-transforms",
|
||||||
|
"dependencies": {
|
||||||
|
"@clawd/auth-runtime": "git+http://192.168.0.108:3030/agent-skills/auth-runtime.git",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@clawd/auth-runtime": ["@clawd/auth-runtime@git+http://192.168.0.108:3030/agent-skills/auth-runtime.git#70cf86889eecbe9c4649bb072cd971c3a560e889", {}, "70cf86889eecbe9c4649bb072cd971c3a560e889"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "my-skill",
|
"name": "hooks-transforms",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,13 @@
|
||||||
#!/usr/bin/env bun
|
#!/usr/bin/env bun
|
||||||
import type { Command } from '../src/index.ts';
|
import { run } from "../src/index.ts";
|
||||||
import { run } from '../src/index.ts';
|
|
||||||
|
|
||||||
function printUsage(): void {
|
const args = process.argv.slice(2);
|
||||||
console.error(`Usage:
|
const command = args[0] as "skill-install";
|
||||||
bun scripts/run.ts [--api-base=<url>] <command> [args...] [--dry-run]
|
const commandArgs = args.slice(1);
|
||||||
|
|
||||||
Commands:
|
/*
|
||||||
run <arg>
|
Usage:
|
||||||
|
bun run scripts/run.ts skill-install '{"skills":[{"repo_url":"http://...","skill_slug":"my-skill"}]}'
|
||||||
|
*/
|
||||||
|
|
||||||
Config: ~/.openclaw/.env (CLIENT_KEY, API_BASE)
|
await run(command, commandArgs, false);
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main(): Promise<void> {
|
|
||||||
const positionals: string[] = [];
|
|
||||||
let dryRun = false;
|
|
||||||
|
|
||||||
for (const arg of process.argv.slice(2)) {
|
|
||||||
if (arg === '--dry-run') {
|
|
||||||
dryRun = true;
|
|
||||||
} else if (arg.startsWith('--api-base=')) {
|
|
||||||
process.env.API_BASE = arg.slice('--api-base='.length).trim();
|
|
||||||
} else if (arg === '-h' || arg === '--help') {
|
|
||||||
printUsage(); process.exit(0);
|
|
||||||
} else {
|
|
||||||
positionals.push(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (positionals.length < 1) { printUsage(); process.exit(1); }
|
|
||||||
|
|
||||||
const result = await run(positionals[0] as Command, positionals.slice(1), dryRun);
|
|
||||||
console.log(JSON.stringify(result, null, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
main().catch((err) => {
|
|
||||||
console.error(JSON.stringify({ status: 'failed', error: err instanceof Error ? err.message : String(err) }, null, 2));
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
|
|
|
||||||
94
src/index.ts
94
src/index.ts
|
|
@ -1,42 +1,72 @@
|
||||||
import {
|
import { createEnvConfig, requestApiWithAutoRefresh } from '@clawd/auth-runtime';
|
||||||
createEnvConfig,
|
|
||||||
requestApiWithAutoRefresh,
|
|
||||||
type ApiResponse,
|
|
||||||
} from '@clawd/auth-runtime';
|
|
||||||
|
|
||||||
export type Command = 'run'; // TODO: add your commands
|
export type Command = 'skill-install';
|
||||||
|
|
||||||
export interface RunResult {
|
interface SkillEntry {
|
||||||
status: 'success' | 'failed';
|
skill_id?: string;
|
||||||
command: Command;
|
skill_slug?: string;
|
||||||
dryRun: boolean;
|
skill_name?: string;
|
||||||
data?: unknown;
|
repo_url?: string;
|
||||||
error?: string;
|
repo_subpath?: string;
|
||||||
|
git_ref?: string;
|
||||||
|
commit_sha?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function run(
|
export async function run(command: Command, args: string[], dryRun: boolean) {
|
||||||
command: Command,
|
|
||||||
args: string[],
|
|
||||||
dryRun: boolean,
|
|
||||||
): Promise<RunResult> {
|
|
||||||
const config = createEnvConfig();
|
const config = createEnvConfig();
|
||||||
const apiBase = (process.env.API_BASE ?? 'https://api-gw-test.yuanwei-lnc.com').replace(/\/$/, '');
|
|
||||||
|
|
||||||
if (command === 'run') {
|
if (command === 'skill-install') {
|
||||||
const response: ApiResponse = await requestApiWithAutoRefresh(
|
const payload = args[0] ? JSON.parse(args[0]) : {};
|
||||||
'POST',
|
const result = transformSkillInstall(payload);
|
||||||
`${apiBase}/your/endpoint`,
|
console.log(result.message);
|
||||||
dryRun,
|
return result;
|
||||||
config,
|
}
|
||||||
JSON.stringify({ param: args[0] }),
|
}
|
||||||
|
|
||||||
|
function transformSkillInstall(payload: Record<string, unknown>) {
|
||||||
|
const body = (payload.payload ?? payload) as Record<string, unknown>;
|
||||||
|
const skills = body.skills as SkillEntry[] | undefined;
|
||||||
|
|
||||||
|
if (!skills || skills.length === 0) {
|
||||||
|
return {
|
||||||
|
message: "skill-update webhook received but no `skills` array in payload. Nothing to install.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines: string[] = [
|
||||||
|
`Install/update ${skills.length} skill(s) from git. Execute all steps directly — do not ask questions.`,
|
||||||
|
``,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const skill of skills) {
|
||||||
|
const { repo_url, skill_slug, skill_name, git_ref, repo_subpath } = skill;
|
||||||
|
|
||||||
|
if (!repo_url) {
|
||||||
|
lines.push(`⚠️ Skipping skill "${skill_slug ?? "unknown"}" — missing repo_url.`, ``);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dirName = skill_slug ?? repo_url.split("/").pop()?.replace(/\.git$/, "") ?? "unknown-skill";
|
||||||
|
const installPath = `~/clawd/skills/${dirName}`;
|
||||||
|
const refFlag = git_ref ? ` --branch ${git_ref}` : "";
|
||||||
|
const subpath = repo_subpath ? `/${repo_subpath}` : "";
|
||||||
|
const skillDir = repo_subpath ? `${installPath}/${repo_subpath}` : installPath;
|
||||||
|
|
||||||
|
lines.push(
|
||||||
|
`── Skill: ${skill_name ?? dirName} (${git_ref ?? "default branch"})`,
|
||||||
|
` Repo: ${repo_url}`,
|
||||||
|
` Target: ${skillDir}`,
|
||||||
|
``,
|
||||||
|
` Steps:`,
|
||||||
|
` 1. If ${installPath} exists: cd ${installPath} && git fetch --tags && git checkout ${git_ref ?? "HEAD"}`,
|
||||||
|
` Otherwise: git clone ${repo_url} ${installPath} && cd ${installPath} && git checkout ${git_ref ?? "HEAD"}`,
|
||||||
|
` 2. cd ${skillDir}`,
|
||||||
|
` 3. If install.sh exists: bash install.sh`,
|
||||||
|
` Otherwise: bun install`,
|
||||||
|
` 4. Reply with one line: installed/updated + skill name + version.`,
|
||||||
|
``,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status < 200 || response.status >= 300) {
|
|
||||||
return { status: 'failed', command, dryRun, error: `HTTP ${response.status}: ${response.body}` };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { status: 'success', command, dryRun, data: JSON.parse(response.body) };
|
return { message: lines.join("\n") };
|
||||||
}
|
|
||||||
|
|
||||||
return { status: 'failed', command, dryRun, error: `unknown command: ${command}` };
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue