@clawstore/clawstore 1.0.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/src/types.ts ADDED
@@ -0,0 +1,167 @@
1
+ // ─── Agent Manifest (agent.json) ────────────────────────────────────
2
+
3
+ export interface SkillDependency {
4
+ id: string;
5
+ version: string;
6
+ required: boolean;
7
+ }
8
+
9
+ export interface AgentManifest {
10
+ id: string;
11
+ slug: string;
12
+ name: string;
13
+ subtitle: string;
14
+ description: string;
15
+ version: string;
16
+ author: string;
17
+ author_id?: string;
18
+ homepage?: string;
19
+ license: string;
20
+ price: number;
21
+ currency: string;
22
+ category: string;
23
+ tags: string[];
24
+ icon?: string;
25
+ screenshots?: string[];
26
+ openclaw_version: string;
27
+ entry_files: string[];
28
+ skills: SkillDependency[];
29
+ checksum?: string;
30
+ published_at?: string;
31
+ updated_at?: string;
32
+ source_agent_id?: string;
33
+ packed_at?: string;
34
+ packaged_by?: string;
35
+ detection_mode?: string;
36
+ }
37
+
38
+ // ─── Installed Agent Record ─────────────────────────────────────────
39
+
40
+ export interface InstalledAgentRecord {
41
+ id: string;
42
+ name: string;
43
+ version: string;
44
+ category: string;
45
+ author: string;
46
+ enabled: boolean;
47
+ installed_at: string;
48
+ updated_at: string;
49
+ source: string;
50
+ install_path: string;
51
+ backup_path: string;
52
+ disable_backup_path?: string;
53
+ files: string[];
54
+ skills: string[];
55
+ }
56
+
57
+ export interface AgentRegistry {
58
+ agents: Record<string, InstalledAgentRecord>;
59
+ }
60
+
61
+ // ─── Installed Skill Record ─────────────────────────────────────────
62
+
63
+ export interface InstalledSkillRecord {
64
+ skill_id: string;
65
+ version: string;
66
+ installed_at: string;
67
+ installed_by: string;
68
+ source: string;
69
+ }
70
+
71
+ export interface SkillRegistry {
72
+ skills: Record<string, InstalledSkillRecord>;
73
+ }
74
+
75
+ // ─── Install State Machine ──────────────────────────────────────────
76
+
77
+ export type InstallState =
78
+ | "idle"
79
+ | "fetching_manifest"
80
+ | "downloading_package"
81
+ | "verifying_package"
82
+ | "checking_compatibility"
83
+ | "installing_skills"
84
+ | "installing_agent"
85
+ | "registering_agent"
86
+ | "rolling_back"
87
+ | "success"
88
+ | "failed";
89
+
90
+ // ─── Pack State Machine ─────────────────────────────────────────────
91
+
92
+ export type PackState =
93
+ | "idle"
94
+ | "loading_agent"
95
+ | "collecting_files"
96
+ | "detecting_skills"
97
+ | "awaiting_confirmation"
98
+ | "generating_agent_json"
99
+ | "packing_zip"
100
+ | "saving_or_uploading"
101
+ | "success"
102
+ | "failed";
103
+
104
+ // ─── Plugin Config ──────────────────────────────────────────────────
105
+
106
+ export interface ClawstoreConfig {
107
+ apiBaseUrl: string;
108
+ autoCheckUpdates: boolean;
109
+ autoInstallSkills: boolean;
110
+ }
111
+
112
+ // ─── Store API Response Types ───────────────────────────────────────
113
+
114
+ export interface StoreAgentSummary {
115
+ id: string;
116
+ name: string;
117
+ subtitle: string;
118
+ author: string;
119
+ price: number;
120
+ currency: string;
121
+ category: string;
122
+ rating: number;
123
+ downloads: number;
124
+ openclaw_version: string;
125
+ }
126
+
127
+ export interface StoreAgentDetail extends StoreAgentSummary {
128
+ description: string;
129
+ tags: string[];
130
+ skills: SkillDependency[];
131
+ version: string;
132
+ screenshots: string[];
133
+ published_at: string;
134
+ updated_at: string;
135
+ }
136
+
137
+ export interface StoreSearchResult {
138
+ total: number;
139
+ agents: StoreAgentSummary[];
140
+ }
141
+
142
+ // ─── Diagnostic Result ──────────────────────────────────────────────
143
+
144
+ export interface DiagnosticCheck {
145
+ label: string;
146
+ path?: string;
147
+ status: "ok" | "warn" | "fail";
148
+ detail?: string;
149
+ }
150
+
151
+ export interface DiagnosticReport {
152
+ checks: DiagnosticCheck[];
153
+ allOk: boolean;
154
+ }
155
+
156
+ // ─── Validation ─────────────────────────────────────────────────────
157
+
158
+ export interface ValidationError {
159
+ field: string;
160
+ message: string;
161
+ }
162
+
163
+ export interface PackageValidationResult {
164
+ valid: boolean;
165
+ errors: ValidationError[];
166
+ manifest: AgentManifest | null;
167
+ }
@@ -0,0 +1,17 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile } from "node:fs/promises";
3
+
4
+ export async function computeSha256(filePath: string): Promise<string> {
5
+ const data = await readFile(filePath);
6
+ const hash = createHash("sha256").update(data).digest("hex");
7
+ return `sha256:${hash.substring(0, 16)}`;
8
+ }
9
+
10
+ export async function computeFullSha256(filePath: string): Promise<string> {
11
+ const data = await readFile(filePath);
12
+ return createHash("sha256").update(data).digest("hex");
13
+ }
14
+
15
+ export function hashBuffer(buf: Buffer): string {
16
+ return createHash("sha256").update(buf).digest("hex");
17
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Formatting helpers for CLI/slash command output.
3
+ * Follows the Clawstore output design spec: result first, details second,
4
+ * short text for next-step guidance.
5
+ */
6
+
7
+ export function stepLine(step: number, total: number, label: string, status: string): string {
8
+ return ` [${step}/${total}] ${label} ... ${status}`;
9
+ }
10
+
11
+ export function successBanner(agentName: string, version: string, extra?: Record<string, string>): string {
12
+ const lines: string[] = [
13
+ "",
14
+ ` Installed: ${agentName}@${version}`,
15
+ ];
16
+ if (extra) {
17
+ for (const [k, v] of Object.entries(extra)) {
18
+ lines.push(` ${k}: ${v}`);
19
+ }
20
+ }
21
+ lines.push(" Status: enabled");
22
+ lines.push("");
23
+ lines.push(" Next: start a new chat in OpenClaw to activate the new persona");
24
+ lines.push("");
25
+ return lines.join("\n");
26
+ }
27
+
28
+ export function errorBanner(action: string, agentId: string, step: string, reason: string): string {
29
+ return [
30
+ "",
31
+ ` ${action} failed: ${agentId}`,
32
+ ` Step: ${step}`,
33
+ ` Reason: ${reason}`,
34
+ " Next:",
35
+ " 1. Run `openclaw clawstore doctor`",
36
+ ` 2. Retry \`openclaw clawstore ${action.toLowerCase()} ${agentId}\``,
37
+ "",
38
+ ].join("\n");
39
+ }
40
+
41
+ export function agentListItem(index: number, id: string, name: string, version: string, category: string, enabled: boolean): string {
42
+ const status = enabled ? "enabled" : "disabled";
43
+ return [
44
+ ` ${index}. ${id}`,
45
+ ` ${name}`,
46
+ ` v${version} · ${category} · ${status}`,
47
+ ].join("\n");
48
+ }
49
+
50
+ export function detailBlock(fields: Record<string, string | undefined>): string {
51
+ const lines: string[] = [""];
52
+ for (const [k, v] of Object.entries(fields)) {
53
+ if (v !== undefined) {
54
+ lines.push(` ${k}: ${v}`);
55
+ }
56
+ }
57
+ lines.push("");
58
+ return lines.join("\n");
59
+ }
60
+
61
+ export function searchResultItem(index: number, id: string, name: string, price: number, currency: string, author: string, rating: number): string {
62
+ const priceStr = price === 0 ? "Free" : `${currency === "USD" ? "$" : currency}${price}`;
63
+ return [
64
+ ` ${index}. ${id}`,
65
+ ` ${name}`,
66
+ ` ${priceStr} · by ${author} · rating ${rating.toFixed(1)}`,
67
+ ].join("\n");
68
+ }
69
+
70
+ export function indentLines(text: string, indent: number = 2): string {
71
+ const pad = " ".repeat(indent);
72
+ return text
73
+ .split("\n")
74
+ .map((l) => (l.trim() ? `${pad}${l}` : l))
75
+ .join("\n");
76
+ }
@@ -0,0 +1,55 @@
1
+ import AdmZip from "adm-zip";
2
+ import { mkdir, readdir, stat } from "node:fs/promises";
3
+ import { join, relative } from "node:path";
4
+ import { CACHE_DIR } from "../constants.js";
5
+
6
+ /**
7
+ * Extract a ZIP to a cache directory, returning the effective package root.
8
+ * If the ZIP contains a single top-level directory, that directory is returned.
9
+ */
10
+ export async function extractZip(zipPath: string): Promise<string> {
11
+ const zip = new AdmZip(zipPath);
12
+ const baseName = zipPath.replace(/\.zip$/i, "").split(/[\\/]/).pop()!;
13
+ const extractDir = join(CACHE_DIR, baseName);
14
+
15
+ await mkdir(extractDir, { recursive: true });
16
+ zip.extractAllTo(extractDir, true);
17
+
18
+ const children = await readdir(extractDir);
19
+ if (children.length === 1) {
20
+ const child = join(extractDir, children[0]);
21
+ const st = await stat(child);
22
+ if (st.isDirectory()) return child;
23
+ }
24
+
25
+ return extractDir;
26
+ }
27
+
28
+ /**
29
+ * Create a ZIP from a source directory, excluding hidden files and __pycache__.
30
+ */
31
+ export async function createZip(sourceDir: string, outputPath: string): Promise<{ path: string; sizeBytes: number }> {
32
+ const zip = new AdmZip();
33
+
34
+ async function walk(dir: string) {
35
+ const entries = await readdir(dir, { withFileTypes: true });
36
+ for (const entry of entries) {
37
+ if (entry.name.startsWith(".") || entry.name === "__pycache__" || entry.name === "node_modules") {
38
+ continue;
39
+ }
40
+ const fullPath = join(dir, entry.name);
41
+ const arcPath = relative(sourceDir, fullPath);
42
+ if (entry.isDirectory()) {
43
+ await walk(fullPath);
44
+ } else {
45
+ zip.addLocalFile(fullPath, relative(sourceDir, dir) || undefined);
46
+ }
47
+ }
48
+ }
49
+
50
+ await walk(sourceDir);
51
+ zip.writeZip(outputPath);
52
+
53
+ const st = await stat(outputPath);
54
+ return { path: outputPath, sizeBytes: st.size };
55
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2022"],
7
+ "outDir": "./dist",
8
+ "rootDir": ".",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "resolveJsonModule": true,
14
+ "declaration": true,
15
+ "declarationMap": true,
16
+ "sourceMap": true,
17
+ "paths": {
18
+ "openclaw/plugin-sdk/*": ["./typings/openclaw-plugin-sdk/*"]
19
+ }
20
+ },
21
+ "include": ["index.ts", "src/**/*.ts", "typings/**/*.ts"],
22
+ "exclude": ["node_modules", "dist"]
23
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * OpenClaw Plugin SDK type stubs.
3
+ *
4
+ * These mirror the real OpenClaw plugin APIs so the plugin compiles
5
+ * without the actual SDK installed. When running inside OpenClaw, the real
6
+ * module resolution takes over.
7
+ */
8
+
9
+ // ─── CLI Registration ────────────────────────────────────────────────
10
+
11
+ /**
12
+ * Minimal Commander.js Command interface.
13
+ * At runtime, `program` is a real Commander Command instance provided by OpenClaw.
14
+ */
15
+ export interface CommanderProgram {
16
+ command(nameAndArgs: string): CommanderProgram;
17
+ description(str: string): CommanderProgram;
18
+ option(flags: string, description?: string, defaultValue?: unknown): CommanderProgram;
19
+ requiredOption(flags: string, description?: string, defaultValue?: unknown): CommanderProgram;
20
+ argument(name: string, description?: string): CommanderProgram;
21
+ action(fn: (...args: any[]) => void | Promise<void>): CommanderProgram;
22
+ addHelpText(position: string, text: string | (() => string)): CommanderProgram;
23
+ }
24
+
25
+ export interface CliRegistrarContext {
26
+ program: CommanderProgram;
27
+ config: Record<string, unknown>;
28
+ workspaceDir: string;
29
+ logger: PluginLogger;
30
+ }
31
+
32
+ export interface CliRegistrationOptions {
33
+ commands: string[];
34
+ }
35
+
36
+ // ─── Slash Command ───────────────────────────────────────────────────
37
+
38
+ export interface SlashCommandContext {
39
+ args: string;
40
+ channel?: string;
41
+ senderId?: string;
42
+ }
43
+
44
+ export interface SlashCommandRegistration {
45
+ name: string;
46
+ description: string;
47
+ acceptsArgs: boolean;
48
+ handler: (ctx: SlashCommandContext) => Promise<{ text: string }>;
49
+ }
50
+
51
+ // ─── Service ─────────────────────────────────────────────────────────
52
+
53
+ export interface ServiceRegistration {
54
+ id: string;
55
+ start: () => Promise<void>;
56
+ stop: () => Promise<void>;
57
+ }
58
+
59
+ // ─── Tool ────────────────────────────────────────────────────────────
60
+
61
+ export interface ToolRegistration {
62
+ name: string;
63
+ label?: string;
64
+ description: string;
65
+ parameters: unknown;
66
+ execute: (
67
+ toolCallId: string,
68
+ params: Record<string, unknown>,
69
+ ) => Promise<{
70
+ content: { type: string; text: string }[];
71
+ details?: unknown;
72
+ }>;
73
+ }
74
+
75
+ // ─── Logger ──────────────────────────────────────────────────────────
76
+
77
+ export interface PluginLogger {
78
+ info: (msg: string) => void;
79
+ warn: (msg: string) => void;
80
+ error: (msg: string) => void;
81
+ debug: (msg: string) => void;
82
+ }
83
+
84
+ // ─── Plugin API ──────────────────────────────────────────────────────
85
+
86
+ export interface OpenClawPluginApi {
87
+ id: string;
88
+ name: string;
89
+ version: string;
90
+ description: string;
91
+ source: string;
92
+ config: Record<string, unknown>;
93
+ pluginConfig: unknown;
94
+ runtime: Record<string, unknown>;
95
+ logger: PluginLogger;
96
+
97
+ registerCli(
98
+ registrar: (ctx: CliRegistrarContext) => void,
99
+ opts: CliRegistrationOptions,
100
+ ): void;
101
+
102
+ registerCommand(registration: SlashCommandRegistration): void;
103
+ registerService(registration: ServiceRegistration): void;
104
+ registerTool(tool: ToolRegistration, opts?: { names?: string[] }): void;
105
+
106
+ registerGatewayMethod(
107
+ method: string,
108
+ handler: (opts: { params: unknown; respond: (ok: boolean, payload?: unknown) => void }) => void,
109
+ ): void;
110
+
111
+ registerHttpRoute(params: unknown): void;
112
+ registerChannel(registration: unknown): void;
113
+ registerProvider(provider: unknown): void;
114
+ registerContextEngine(id: string, factory: unknown): void;
115
+ resolvePath(input: string): string;
116
+ on(hookName: string, handler: unknown, opts?: unknown): void;
117
+ }
118
+
119
+ // ─── Plugin Definition ───────────────────────────────────────────────
120
+
121
+ export interface PluginConfigSchema<T = unknown> {
122
+ parse?: (value: unknown) => T;
123
+ safeParse?: (value: unknown) => { success: boolean; data?: T; error?: { issues: { path: unknown[]; message: string }[] } };
124
+ jsonSchema?: unknown;
125
+ uiHints?: Record<string, unknown>;
126
+ }
127
+
128
+ export interface PluginDefinition {
129
+ id: string;
130
+ name: string;
131
+ description: string;
132
+ configSchema?: PluginConfigSchema;
133
+ register: (api: OpenClawPluginApi) => void;
134
+ }