98 lines
2.8 KiB
TypeScript
98 lines
2.8 KiB
TypeScript
#!/usr/bin/env bun
|
|
import type { Command } from '../src/index.ts';
|
|
import { run } from '../src/index.ts';
|
|
import { createSkillClient } from '../src/auth-cli.ts';
|
|
|
|
const SKILL_NAME = 'my-skill'; // TODO: replace with actual skill name
|
|
|
|
function printUsage(): void {
|
|
console.error(`Usage:
|
|
bun scripts/run.ts [--api-base=<url>] <command> [args...] [--dry-run]
|
|
|
|
Commands:
|
|
run <arg>
|
|
|
|
Config: ~/.openclaw/.env (CLIENT_KEY)
|
|
`);
|
|
}
|
|
|
|
async function reportHook(
|
|
hookUrl: string,
|
|
hookToken: string | undefined,
|
|
payload: object,
|
|
): Promise<void> {
|
|
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
if (hookToken) headers['Authorization'] = `Bearer ${hookToken}`;
|
|
await fetch(hookUrl, { method: 'POST', headers, body: JSON.stringify(payload) });
|
|
}
|
|
|
|
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 command = positionals[0] as Command;
|
|
|
|
// Exchange CLIENT_KEY for session — gives us hookUrl for telemetry
|
|
let hookUrl: string | undefined;
|
|
let hookToken: string | undefined;
|
|
try {
|
|
const client = createSkillClient({ dryRun });
|
|
const session = await client.session();
|
|
hookUrl = session.hookUrl;
|
|
hookToken = session.hookToken;
|
|
} catch {
|
|
// Auth failure is non-fatal for telemetry; skill still runs
|
|
}
|
|
|
|
const startMs = Date.now();
|
|
let result: Awaited<ReturnType<typeof run>>;
|
|
|
|
try {
|
|
result = await run(command, positionals.slice(1), dryRun);
|
|
} catch (err) {
|
|
const error = err instanceof Error ? err.message : String(err);
|
|
const failed = { status: 'failed' as const, command, dryRun, error };
|
|
console.log(JSON.stringify(failed, null, 2));
|
|
|
|
if (hookUrl && !dryRun) {
|
|
reportHook(hookUrl, hookToken, {
|
|
skill: SKILL_NAME, command, status: 'failed',
|
|
durationMs: Date.now() - startMs, error,
|
|
}).catch(() => {});
|
|
}
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(JSON.stringify(result, null, 2));
|
|
|
|
// Fire-and-forget telemetry — never delays output
|
|
if (hookUrl && !dryRun) {
|
|
reportHook(hookUrl, hookToken, {
|
|
skill: SKILL_NAME,
|
|
command,
|
|
status: result.status,
|
|
durationMs: Date.now() - startMs,
|
|
error: (result as any).error,
|
|
}).catch(() => {});
|
|
}
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(JSON.stringify({ status: 'failed', error: err instanceof Error ? err.message : String(err) }, null, 2));
|
|
process.exit(1);
|
|
});
|