clawnet 0.0.1

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.
@@ -0,0 +1,23 @@
1
+ import { searchSkills } from "../api.js";
2
+ import { formatError, formatSkillRow } from "../format.js";
3
+
4
+ export async function search(query: string): Promise<void> {
5
+ try {
6
+ const { results } = await searchSkills(query);
7
+
8
+ if (results.length === 0) {
9
+ console.log("No skills found.");
10
+ return;
11
+ }
12
+
13
+ for (const skill of results) {
14
+ console.log(formatSkillRow(skill));
15
+ console.log();
16
+ }
17
+ } catch (err) {
18
+ console.error(
19
+ formatError(err instanceof Error ? err.message : String(err))
20
+ );
21
+ process.exit(1);
22
+ }
23
+ }
@@ -0,0 +1,31 @@
1
+ import { starSkill, unstarSkill } from "../api.js";
2
+ import { requireToken } from "../auth.js";
3
+ import { formatError, formatSuccess } from "../format.js";
4
+
5
+ export async function star(slug: string): Promise<void> {
6
+ const token = await requireToken();
7
+
8
+ try {
9
+ await starSkill(slug, token);
10
+ console.log(formatSuccess(`Starred ${slug}`));
11
+ } catch (err) {
12
+ console.error(
13
+ formatError(err instanceof Error ? err.message : String(err))
14
+ );
15
+ process.exit(1);
16
+ }
17
+ }
18
+
19
+ export async function unstar(slug: string): Promise<void> {
20
+ const token = await requireToken();
21
+
22
+ try {
23
+ await unstarSkill(slug, token);
24
+ console.log(formatSuccess(`Unstarred ${slug}`));
25
+ } catch (err) {
26
+ console.error(
27
+ formatError(err instanceof Error ? err.message : String(err))
28
+ );
29
+ process.exit(1);
30
+ }
31
+ }
@@ -0,0 +1,18 @@
1
+ import { fetchWhoami } from "../api.js";
2
+ import { requireToken } from "../auth.js";
3
+ import { formatError } from "../format.js";
4
+
5
+ export async function whoami(): Promise<void> {
6
+ const token = await requireToken();
7
+
8
+ try {
9
+ const data = await fetchWhoami(token);
10
+ console.log(`BAP ID: ${data.user.bapId}`);
11
+ console.log(`Pubkey: ${data.user.pubkey}`);
12
+ } catch (err) {
13
+ console.error(
14
+ formatError(err instanceof Error ? err.message : String(err))
15
+ );
16
+ process.exit(1);
17
+ }
18
+ }
package/src/config.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { existsSync, mkdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ const HOME = process.env.HOME || ".";
5
+
6
+ export const CONFIG_DIR = join(HOME, ".clawhub");
7
+ export const TOKEN_PATH = join(CONFIG_DIR, "token");
8
+ export const DEVICE_ID_PATH = join(CONFIG_DIR, "device-id");
9
+
10
+ export const DEFAULT_REGISTRY = "https://clawhub.network";
11
+ export const SIGMA_AUTH_URL = "https://auth.sigmaidentity.com";
12
+ export const SIGMA_CLIENT_ID = "clawhub";
13
+
14
+ export function ensureConfigDir(): void {
15
+ if (!existsSync(CONFIG_DIR)) {
16
+ mkdirSync(CONFIG_DIR, { recursive: true });
17
+ }
18
+ }
package/src/crypt.ts ADDED
@@ -0,0 +1,60 @@
1
+ import {
2
+ createCipheriv,
3
+ createDecipheriv,
4
+ pbkdf2Sync,
5
+ randomBytes,
6
+ randomUUID,
7
+ } from "node:crypto";
8
+ import { readFile, writeFile } from "node:fs/promises";
9
+
10
+ import { DEVICE_ID_PATH, ensureConfigDir } from "./config.js";
11
+
12
+ const SALT = "clawhub-cli-salt";
13
+
14
+ async function getOrCreateDeviceId(): Promise<string> {
15
+ try {
16
+ return await readFile(DEVICE_ID_PATH, "utf8");
17
+ } catch (err: unknown) {
18
+ if (
19
+ err instanceof Error &&
20
+ "code" in err &&
21
+ (err as NodeJS.ErrnoException).code === "ENOENT"
22
+ ) {
23
+ ensureConfigDir();
24
+ const deviceId = randomUUID();
25
+ await writeFile(DEVICE_ID_PATH, deviceId, { mode: 0o600 });
26
+ return deviceId;
27
+ }
28
+ throw err;
29
+ }
30
+ }
31
+
32
+ async function deriveKey(): Promise<Buffer> {
33
+ const deviceId = await getOrCreateDeviceId();
34
+ return pbkdf2Sync(deviceId, SALT, 100000, 32, "sha256");
35
+ }
36
+
37
+ export async function encrypt(plaintext: string): Promise<string> {
38
+ const key = await deriveKey();
39
+ const iv = randomBytes(16);
40
+ const cipher = createCipheriv("aes-256-cbc", key, iv);
41
+ let encrypted = cipher.update(plaintext);
42
+ encrypted = Buffer.concat([encrypted, cipher.final()]);
43
+ return `${iv.toString("hex")}:${encrypted.toString("hex")}`;
44
+ }
45
+
46
+ export async function decrypt(ciphertext: string): Promise<string | null> {
47
+ const [ivHex, encryptedHex] = ciphertext.split(":");
48
+ if (!ivHex || !encryptedHex) return null;
49
+
50
+ const key = await deriveKey();
51
+ const iv = Buffer.from(ivHex, "hex");
52
+ try {
53
+ const decipher = createDecipheriv("aes-256-cbc", key, iv);
54
+ let decrypted = decipher.update(Buffer.from(encryptedHex, "hex"));
55
+ decrypted = Buffer.concat([decrypted, decipher.final()]);
56
+ return decrypted.toString();
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
package/src/format.ts ADDED
@@ -0,0 +1,68 @@
1
+ import chalk from "chalk";
2
+
3
+ import type { SearchResult, SkillDetail, SkillSummary } from "./api.js";
4
+
5
+ export function formatSkillRow(skill: SkillSummary | SearchResult): string {
6
+ const version = chalk.dim(`v${skill.latestVersion}`);
7
+ const name = chalk.bold(skill.name);
8
+ const slug = chalk.cyan(skill.slug);
9
+ return `${slug} ${version} ${name}\n ${chalk.dim(skill.description)}`;
10
+ }
11
+
12
+ export function formatSkillDetail(data: SkillDetail): string {
13
+ const { skill, latestVersion, author } = data;
14
+ const lines: string[] = [];
15
+
16
+ lines.push(chalk.bold.cyan(skill.name));
17
+ lines.push(chalk.dim(`${skill.slug}@${skill.latestVersion}`));
18
+ lines.push("");
19
+ lines.push(skill.description);
20
+
21
+ if (skill.homepage) {
22
+ lines.push("");
23
+ lines.push(`Homepage: ${chalk.underline(skill.homepage)}`);
24
+ }
25
+
26
+ if (skill.tags && skill.tags.length > 0) {
27
+ lines.push(
28
+ `Tags: ${skill.tags.map((t) => chalk.yellow(t)).join(", ")}`
29
+ );
30
+ }
31
+
32
+ if (author) {
33
+ lines.push(`Author: ${chalk.dim(author.bapId)}`);
34
+ }
35
+
36
+ lines.push("");
37
+ lines.push(
38
+ `Stars: ${chalk.yellow(String(skill.starCount))} Downloads: ${chalk.green(String(skill.downloadCount))}`
39
+ );
40
+
41
+ if (latestVersion) {
42
+ lines.push("");
43
+ lines.push(chalk.dim("Latest version:"));
44
+ lines.push(` Version: ${latestVersion.version}`);
45
+ lines.push(
46
+ ` Published: ${new Date(latestVersion.publishedAt).toLocaleDateString()}`
47
+ );
48
+ lines.push(` On-chain: ${latestVersion.onChain ? "yes" : "no"}`);
49
+ if (latestVersion.changelog) {
50
+ lines.push(` Changelog: ${latestVersion.changelog}`);
51
+ }
52
+ if (latestVersion.files && latestVersion.files.length > 0) {
53
+ lines.push(
54
+ ` Files: ${latestVersion.files.map((f) => f.path).join(", ")}`
55
+ );
56
+ }
57
+ }
58
+
59
+ return lines.join("\n");
60
+ }
61
+
62
+ export function formatError(message: string): string {
63
+ return `${chalk.red("Error:")} ${message}`;
64
+ }
65
+
66
+ export function formatSuccess(message: string): string {
67
+ return `${chalk.green("Success:")} ${message}`;
68
+ }