clawdock 0.1.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/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # ClawDock CLI
2
+
3
+ The command-line tool for [ClawDock](https://clawhub-one.vercel.app) — the agent registry. Push, pull, share, and deploy OpenClaw agent bundles.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g clawdock
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ # Log in with your access token (from dashboard)
15
+ clawdock login
16
+
17
+ # Initialize a bundle in your agent workspace
18
+ clawdock init
19
+
20
+ # Push your agent
21
+ clawdock push
22
+
23
+ # Pull an agent
24
+ clawdock pull username/agent-name
25
+ ```
26
+
27
+ ## Commands
28
+
29
+ | Command | Description |
30
+ |---------|-------------|
31
+ | `clawdock login` | Authenticate with your access token |
32
+ | `clawdock whoami` | Show current user |
33
+ | `clawdock init` | Create a `manifest.json` in the current directory |
34
+ | `clawdock push [path]` | Bundle and publish an agent |
35
+ | `clawdock pull <owner/slug[@version]>` | Download and extract a bundle |
36
+ | `clawdock search <query>` | Search the registry |
37
+ | `clawdock list` | List your published bundles |
38
+
39
+ ## Configuration
40
+
41
+ Config is stored in `~/.clawdock/config.json`.
42
+
43
+ Environment variables:
44
+ - `CLAWDOCK_API_KEY` — override stored API key
45
+ - `CLAWDOCK_REGISTRY` — override registry URL
46
+
47
+ ## License
48
+
49
+ MIT
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+ import * as fs from "fs";
6
+ import * as path from "path";
7
+ import * as os from "os";
8
+ import * as tar from "tar";
9
+ var CONFIG_DIR = path.join(os.homedir(), ".clawdock");
10
+ var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
11
+ var DEFAULT_REGISTRY = "https://clawhub-one.vercel.app";
12
+ function loadConfig() {
13
+ try {
14
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8"));
15
+ } catch {
16
+ return {};
17
+ }
18
+ }
19
+ function saveConfig(config) {
20
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
21
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
22
+ }
23
+ function getRegistry() {
24
+ return process.env.CLAWDOCK_REGISTRY || loadConfig().registry || DEFAULT_REGISTRY;
25
+ }
26
+ function getApiKey() {
27
+ return process.env.CLAWDOCK_API_KEY || loadConfig().apiKey;
28
+ }
29
+ async function apiRequest(method, endpoint, body, isForm) {
30
+ const registry = getRegistry();
31
+ const apiKey = getApiKey();
32
+ const headers = {};
33
+ if (apiKey) headers["x-api-key"] = apiKey;
34
+ if (!isForm && body) headers["content-type"] = "application/json";
35
+ const res = await fetch(`${registry}/api${endpoint}`, {
36
+ method,
37
+ headers,
38
+ body: isForm ? body : body ? JSON.stringify(body) : void 0
39
+ });
40
+ if (!res.ok) {
41
+ const err = await res.json().catch(() => ({ error: res.statusText }));
42
+ throw new Error(err.error || `HTTP ${res.status}`);
43
+ }
44
+ return res.json();
45
+ }
46
+ var program = new Command();
47
+ program.name("clawdock").description("ClawDock \u2014 the agent registry").version("0.1.0");
48
+ program.command("login").description("Authenticate with ClawDock").option("--api-key <key>", "API key").option("--registry <url>", "Registry URL").action(async (opts) => {
49
+ const config = loadConfig();
50
+ if (opts.apiKey) {
51
+ config.apiKey = opts.apiKey;
52
+ } else {
53
+ process.stdout.write("API Key: ");
54
+ const key = await new Promise((resolve2) => {
55
+ let data = "";
56
+ process.stdin.setEncoding("utf-8");
57
+ process.stdin.on("data", (chunk) => {
58
+ data += chunk;
59
+ if (data.includes("\n")) {
60
+ process.stdin.pause();
61
+ resolve2(data.trim());
62
+ }
63
+ });
64
+ process.stdin.resume();
65
+ });
66
+ config.apiKey = key;
67
+ }
68
+ if (opts.registry) config.registry = opts.registry;
69
+ saveConfig(config);
70
+ console.log(`\u2705 Logged in to ${getRegistry()}`);
71
+ console.log(` Config saved to ${CONFIG_FILE}`);
72
+ });
73
+ program.command("whoami").description("Show current user").action(async () => {
74
+ try {
75
+ const data = await apiRequest("GET", "/v1/auth/me");
76
+ console.log(`Logged in as: ${data.email || data.name || "unknown"}`);
77
+ } catch (err) {
78
+ console.error(`Not logged in or invalid key: ${err.message}`);
79
+ process.exit(1);
80
+ }
81
+ });
82
+ program.command("push [path]").description("Publish an agent bundle").option("--public", "Make bundle public (default)", true).option("--private", "Make bundle private").option("--changelog <text>", "Version changelog").action(async (bundlePath, opts) => {
83
+ const dir = path.resolve(bundlePath || ".");
84
+ const manifestPath = path.join(dir, "manifest.json");
85
+ if (!fs.existsSync(manifestPath)) {
86
+ console.error(`\u274C No manifest.json found in ${dir}`);
87
+ console.error(" Create one with: clawdock init");
88
+ process.exit(1);
89
+ }
90
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
91
+ console.log(`\u{1F4E6} Pushing ${manifest.name}@${manifest.version}...`);
92
+ const tmpFile = path.join(os.tmpdir(), `clawdock-${Date.now()}.tar.gz`);
93
+ const excludePatterns = manifest.exclude || [
94
+ "node_modules",
95
+ "*.log",
96
+ ".git",
97
+ ".env",
98
+ ".env.*",
99
+ "__pycache__",
100
+ ".DS_Store"
101
+ ];
102
+ await tar.create(
103
+ {
104
+ gzip: true,
105
+ file: tmpFile,
106
+ cwd: path.dirname(dir),
107
+ filter: (p) => {
108
+ return !excludePatterns.some((pattern) => {
109
+ if (pattern.startsWith("*.")) {
110
+ return p.endsWith(pattern.slice(1));
111
+ }
112
+ return p.includes(pattern);
113
+ });
114
+ }
115
+ },
116
+ [path.basename(dir)]
117
+ );
118
+ const tarball = fs.readFileSync(tmpFile);
119
+ const sizeMb = (tarball.length / 1024 / 1024).toFixed(1);
120
+ console.log(` Bundle size: ${sizeMb} MB`);
121
+ const formData = new FormData();
122
+ formData.append("tarball", new Blob([tarball]), `${manifest.name}.tar.gz`);
123
+ formData.append("is_public", opts.private ? "false" : "true");
124
+ if (opts.changelog) formData.append("changelog", opts.changelog);
125
+ try {
126
+ const result = await apiRequest("POST", "/v1/bundles/publish", formData, true);
127
+ console.log(`\u2705 Published ${result.owner}/${result.slug}@${result.version}`);
128
+ console.log(` Checksum: ${result.checksum?.slice(0, 12)}...`);
129
+ console.log(` Install: clawdock pull ${result.owner}/${result.slug}`);
130
+ } catch (err) {
131
+ console.error(`\u274C Push failed: ${err.message}`);
132
+ process.exit(1);
133
+ } finally {
134
+ fs.unlinkSync(tmpFile);
135
+ }
136
+ });
137
+ program.command("pull <bundle>").description("Download an agent bundle (owner/slug[@version])").option("--dir <path>", "Extract to directory", ".").action(async (bundle, opts) => {
138
+ const match = bundle.match(/^([^/]+)\/([^@]+)(?:@(.+))?$/);
139
+ if (!match) {
140
+ console.error("\u274C Invalid bundle format. Use: owner/slug[@version]");
141
+ process.exit(1);
142
+ }
143
+ const [, owner, slug, version] = match;
144
+ const ver = version || "latest";
145
+ console.log(`\u{1F4E5} Pulling ${owner}/${slug}@${ver}...`);
146
+ try {
147
+ const { url, version: resolvedVersion } = await apiRequest(
148
+ "GET",
149
+ `/v1/bundles/${owner}/${slug}/${ver}/download`
150
+ );
151
+ console.log(` Version: ${resolvedVersion}`);
152
+ const res = await fetch(url);
153
+ if (!res.ok) throw new Error(`Download failed: ${res.statusText}`);
154
+ const tarball = Buffer.from(await res.arrayBuffer());
155
+ const targetDir = path.resolve(opts.dir, slug);
156
+ fs.mkdirSync(targetDir, { recursive: true });
157
+ const tmpFile = path.join(os.tmpdir(), `clawdock-pull-${Date.now()}.tar.gz`);
158
+ fs.writeFileSync(tmpFile, tarball);
159
+ await tar.extract({
160
+ file: tmpFile,
161
+ cwd: targetDir,
162
+ strip: 1
163
+ // Remove top-level directory
164
+ });
165
+ fs.unlinkSync(tmpFile);
166
+ console.log(`\u2705 Extracted to ${targetDir}`);
167
+ } catch (err) {
168
+ console.error(`\u274C Pull failed: ${err.message}`);
169
+ process.exit(1);
170
+ }
171
+ });
172
+ program.command("search <query>").description("Search for agent bundles").option("--limit <n>", "Max results", "10").action(async (query, opts) => {
173
+ try {
174
+ const { bundles, total } = await apiRequest(
175
+ "GET",
176
+ `/v1/bundles?q=${encodeURIComponent(query)}&limit=${opts.limit}`
177
+ );
178
+ if (!bundles.length) {
179
+ console.log("No bundles found.");
180
+ return;
181
+ }
182
+ console.log(`Found ${total} bundle(s):
183
+ `);
184
+ for (const b of bundles) {
185
+ const stars = b.star_count ? `\u2B50${b.star_count}` : "";
186
+ const downloads = b.download_count ? `\u{1F4E5}${b.download_count}` : "";
187
+ const tags = b.tags?.length ? b.tags.map((t) => `#${t}`).join(" ") : "";
188
+ console.log(` ${b.owner}/${b.slug} ${stars} ${downloads}`);
189
+ if (b.description) console.log(` ${b.description}`);
190
+ if (tags) console.log(` ${tags}`);
191
+ console.log();
192
+ }
193
+ } catch (err) {
194
+ console.error(`\u274C Search failed: ${err.message}`);
195
+ process.exit(1);
196
+ }
197
+ });
198
+ program.command("list").description("List your published bundles").action(async () => {
199
+ try {
200
+ const me = await apiRequest("GET", "/v1/auth/me").catch(() => null);
201
+ if (!me) {
202
+ console.error("Not logged in. Run: clawdock login");
203
+ process.exit(1);
204
+ }
205
+ const { bundles } = await apiRequest("GET", `/v1/bundles/${me.slug || me.name}`);
206
+ if (!bundles?.length) {
207
+ console.log("No bundles published yet. Push your first agent with: clawdock push");
208
+ return;
209
+ }
210
+ console.log("Your bundles:\n");
211
+ for (const b of bundles) {
212
+ const visibility = b.is_public ? "\u{1F30D}" : "\u{1F512}";
213
+ console.log(` ${visibility} ${b.owner}/${b.slug} \u2B50${b.star_count} \u{1F4E5}${b.download_count}`);
214
+ }
215
+ } catch (err) {
216
+ console.error(`\u274C ${err.message}`);
217
+ process.exit(1);
218
+ }
219
+ });
220
+ program.command("init").description("Create a manifest.json for your agent").action(async () => {
221
+ const manifestPath = path.join(process.cwd(), "manifest.json");
222
+ if (fs.existsSync(manifestPath)) {
223
+ console.log("manifest.json already exists.");
224
+ return;
225
+ }
226
+ const dirName = path.basename(process.cwd()).toLowerCase().replace(/[^a-z0-9-]/g, "-");
227
+ const manifest = {
228
+ name: dirName,
229
+ version: "0.1.0",
230
+ description: "",
231
+ author: "",
232
+ tags: []
233
+ };
234
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
235
+ console.log(`\u2705 Created manifest.json`);
236
+ console.log(` Edit it, then run: clawdock push`);
237
+ });
238
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "clawdock",
3
+ "version": "0.1.0",
4
+ "description": "CLI for ClawDock — the agent registry",
5
+ "bin": {
6
+ "clawdock": "./dist/index.js"
7
+ },
8
+ "type": "module",
9
+ "scripts": {
10
+ "build": "tsup src/index.ts --format esm --dts --clean",
11
+ "dev": "tsup src/index.ts --format esm --watch"
12
+ },
13
+ "dependencies": {
14
+ "commander": "^13.0.0",
15
+ "tar": "^7.0.0",
16
+ "glob": "^11.0.0",
17
+ "ora": "^8.0.0",
18
+ "chalk": "^5.0.0"
19
+ },
20
+ "devDependencies": {
21
+ "tsup": "^8.0.0",
22
+ "typescript": "^5.0.0",
23
+ "@types/node": "^22.0.0",
24
+ "@types/tar": "^6.1.0"
25
+ },
26
+ "files": ["dist"],
27
+ "keywords": ["openclaw", "agent", "registry", "clawdock"],
28
+ "license": "MIT"
29
+ }