clawdock 0.1.0 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +59 -14
  2. package/dist/index.js +234 -0
  3. package/package.json +10 -3
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ClawDock CLI
2
2
 
3
- The command-line tool for [ClawDock](https://clawhub-one.vercel.app) — the agent registry. Push, pull, share, and deploy OpenClaw agent bundles.
3
+ The command-line tool for [ClawDock](https://clawhub-one.vercel.app) — the agent registry. Push, pull, and run OpenClaw agent bundles.
4
4
 
5
5
  ## Install
6
6
 
@@ -11,24 +11,20 @@ npm install -g clawdock
11
11
  ## Quick Start
12
12
 
13
13
  ```bash
14
- # Log in with your access token (from dashboard)
15
- clawdock login
14
+ # 1. Set up runtime credentials (one-time)
15
+ clawdock credentials set --provider github-copilot --api-key ghu_xxx --model github-copilot/claude-sonnet-4
16
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
17
+ # 2. Pull and run any agent
18
+ clawdock run hugo/caramelo
25
19
  ```
26
20
 
27
21
  ## Commands
28
22
 
23
+ ### Registry
24
+
29
25
  | Command | Description |
30
26
  |---------|-------------|
31
- | `clawdock login` | Authenticate with your access token |
27
+ | `clawdock login` | Authenticate with your registry access token |
32
28
  | `clawdock whoami` | Show current user |
33
29
  | `clawdock init` | Create a `manifest.json` in the current directory |
34
30
  | `clawdock push [path]` | Bundle and publish an agent |
@@ -36,12 +32,61 @@ clawdock pull username/agent-name
36
32
  | `clawdock search <query>` | Search the registry |
37
33
  | `clawdock list` | List your published bundles |
38
34
 
35
+ ### Runtime
36
+
37
+ | Command | Description |
38
+ |---------|-------------|
39
+ | `clawdock credentials set` | Configure provider/model/runtime for running agents |
40
+ | `clawdock credentials show` | Show stored runtime credentials |
41
+ | `clawdock credentials clear` | Remove stored credentials |
42
+ | `clawdock run <owner/slug[@version]>` | Pull + launch an agent locally |
43
+
44
+ ### `clawdock run`
45
+
46
+ Pulls an agent bundle and launches it with a local runtime (OpenClaw by default).
47
+
48
+ ```bash
49
+ # Uses stored credentials
50
+ clawdock run hugo/caramelo
51
+
52
+ # Override model
53
+ clawdock run hugo/caramelo --model github-copilot/claude-opus-4
54
+
55
+ # Use a different runtime
56
+ clawdock run hugo/caramelo --runtime nullclaw@latest
57
+
58
+ # Skip pull (use cached workspace)
59
+ clawdock run hugo/caramelo --no-pull
60
+ ```
61
+
62
+ Agents are cached at `~/.clawdock/agents/<owner>/<slug>/workspace/`.
63
+
64
+ ### Supported Runtimes
65
+
66
+ | Runtime | Default | Install |
67
+ |---------|---------|---------|
68
+ | `openclaw@latest` | ✅ | `npm install -g openclaw` (auto-installed if missing) |
69
+ | `nullclaw@latest` | | [github.com/pigeonflow/brain-arch-v2](https://github.com/pigeonflow/brain-arch-v2) |
70
+
39
71
  ## Configuration
40
72
 
41
- Config is stored in `~/.clawdock/config.json`.
73
+ All config lives in `~/.clawdock/config.json`:
74
+
75
+ ```json
76
+ {
77
+ "apiKey": "cd_registry_token",
78
+ "registry": "https://clawhub-one.vercel.app",
79
+ "runtime": {
80
+ "provider": "github-copilot",
81
+ "apiKey": "ghu_xxx",
82
+ "model": "github-copilot/claude-sonnet-4",
83
+ "runtime": "openclaw@latest"
84
+ }
85
+ }
86
+ ```
42
87
 
43
88
  Environment variables:
44
- - `CLAWDOCK_API_KEY` — override stored API key
89
+ - `CLAWDOCK_API_KEY` — override runtime API key
45
90
  - `CLAWDOCK_REGISTRY` — override registry URL
46
91
 
47
92
  ## License
package/dist/index.js CHANGED
@@ -235,4 +235,238 @@ program.command("init").description("Create a manifest.json for your agent").act
235
235
  console.log(`\u2705 Created manifest.json`);
236
236
  console.log(` Edit it, then run: clawdock push`);
237
237
  });
238
+ var credentials = program.command("credentials").description("Manage runtime credentials for clawdock run");
239
+ credentials.command("set").description("Configure provider credentials for running agents").option("--provider <name>", "Provider name (e.g. github-copilot, openai, anthropic)").option("--api-key <key>", "Provider API key").option("--model <model>", "Default model (e.g. github-copilot/claude-sonnet-4)").option("--runtime <runtime>", "Runtime to use (e.g. openclaw@latest, nullclaw@latest)", "openclaw@latest").action(async (opts) => {
240
+ const config = loadConfig();
241
+ config.runtime = config.runtime || {};
242
+ if (opts.provider) config.runtime.provider = opts.provider;
243
+ if (opts.apiKey) config.runtime.apiKey = opts.apiKey;
244
+ if (opts.model) config.runtime.model = opts.model;
245
+ if (opts.runtime) config.runtime.runtime = opts.runtime;
246
+ if (!opts.provider && !opts.apiKey && !opts.model) {
247
+ const ask = (prompt) => new Promise((resolve2) => {
248
+ process.stdout.write(prompt);
249
+ let data = "";
250
+ process.stdin.setEncoding("utf-8");
251
+ process.stdin.on("data", (chunk) => {
252
+ data += chunk;
253
+ if (data.includes("\n")) {
254
+ process.stdin.pause();
255
+ resolve2(data.trim());
256
+ }
257
+ });
258
+ process.stdin.resume();
259
+ });
260
+ config.runtime.provider = await ask(`Provider [${config.runtime.provider || "github-copilot"}]: `) || config.runtime.provider || "github-copilot";
261
+ config.runtime.apiKey = await ask("API Key: ") || config.runtime.apiKey;
262
+ config.runtime.model = await ask(`Model [${config.runtime.model || config.runtime.provider + "/claude-sonnet-4"}]: `) || config.runtime.model;
263
+ config.runtime.runtime = await ask(`Runtime [${config.runtime.runtime || "openclaw@latest"}]: `) || config.runtime.runtime || "openclaw@latest";
264
+ }
265
+ saveConfig(config);
266
+ console.log(`\u2705 Credentials saved`);
267
+ console.log(` Provider: ${config.runtime.provider}`);
268
+ console.log(` Model: ${config.runtime.model || config.runtime.provider + "/claude-sonnet-4"}`);
269
+ console.log(` Runtime: ${config.runtime.runtime || "openclaw@latest"}`);
270
+ console.log(` Config: ${CONFIG_FILE}`);
271
+ });
272
+ credentials.command("show").description("Show current runtime credentials").action(() => {
273
+ const config = loadConfig();
274
+ if (!config.runtime?.provider) {
275
+ console.log("No credentials configured. Run: clawdock credentials set");
276
+ return;
277
+ }
278
+ console.log(`Provider: ${config.runtime.provider}`);
279
+ console.log(`API Key: ${config.runtime.apiKey ? config.runtime.apiKey.slice(0, 8) + "..." : "(not set)"}`);
280
+ console.log(`Model: ${config.runtime.model || "(default)"}`);
281
+ console.log(`Runtime: ${config.runtime.runtime || "openclaw@latest"}`);
282
+ });
283
+ credentials.command("clear").description("Remove stored runtime credentials").action(() => {
284
+ const config = loadConfig();
285
+ delete config.runtime;
286
+ saveConfig(config);
287
+ console.log("\u2705 Credentials cleared");
288
+ });
289
+ var AGENTS_DIR = path.join(CONFIG_DIR, "agents");
290
+ program.command("run <bundle>").description("Pull and run an agent locally").option("--runtime <runtime>", "Runtime override (e.g. openclaw@latest)").option("--model <model>", "Model override").option("--provider <name>", "Provider override").option("--api-key <key>", "API key override").option("--no-pull", "Skip pull, use cached workspace").action(async (bundle, opts) => {
291
+ const { execSync, spawn } = await import("child_process");
292
+ const match = bundle.match(/^([^/]+)\/([^@]+)(?:@(.+))?$/);
293
+ if (!match) {
294
+ console.error("\u274C Invalid bundle format. Use: owner/slug[@version]");
295
+ process.exit(1);
296
+ }
297
+ const [, owner, slug, version] = match;
298
+ const agentName = `${owner}-${slug}`;
299
+ const config = loadConfig();
300
+ const rt = config.runtime || {};
301
+ const provider = opts.provider || rt.provider;
302
+ const apiKey = opts.apiKey || rt.apiKey || process.env.CLAWDOCK_API_KEY;
303
+ const model = opts.model || rt.model;
304
+ const runtimeSpec = opts.runtime || rt.runtime || "openclaw@latest";
305
+ if (!provider || !apiKey) {
306
+ console.error("\u274C No runtime credentials configured.");
307
+ console.error(" Run: clawdock credentials set");
308
+ console.error(" Or pass --provider and --api-key");
309
+ process.exit(1);
310
+ }
311
+ const [runtimeName, runtimeVersion] = runtimeSpec.split("@");
312
+ console.log(`\u{1F50D} Checking runtime: ${runtimeName}...`);
313
+ let runtimeBin = null;
314
+ if (runtimeName === "openclaw") {
315
+ try {
316
+ const which = execSync("which openclaw 2>/dev/null", { encoding: "utf-8" }).trim();
317
+ if (which) {
318
+ runtimeBin = which;
319
+ const ver = execSync("openclaw --version 2>/dev/null", { encoding: "utf-8" }).trim();
320
+ console.log(` Found openclaw ${ver}`);
321
+ }
322
+ } catch {
323
+ }
324
+ if (!runtimeBin) {
325
+ console.log(" OpenClaw not found. Installing via npm...");
326
+ try {
327
+ execSync("npm install -g openclaw", { stdio: "inherit" });
328
+ runtimeBin = execSync("which openclaw", { encoding: "utf-8" }).trim();
329
+ console.log(" \u2705 OpenClaw installed");
330
+ } catch {
331
+ console.error("\u274C Failed to install OpenClaw. Install manually:");
332
+ console.error(" npm install -g openclaw");
333
+ process.exit(1);
334
+ }
335
+ }
336
+ } else if (runtimeName === "nullclaw") {
337
+ const candidates = [
338
+ "nullclaw",
339
+ path.join(os.homedir(), ".nullclaw", "bin", "nullclaw"),
340
+ "/usr/local/bin/nullclaw"
341
+ ];
342
+ for (const c of candidates) {
343
+ try {
344
+ execSync(`which ${c} 2>/dev/null || test -x ${c}`, { stdio: "pipe" });
345
+ runtimeBin = c;
346
+ break;
347
+ } catch {
348
+ }
349
+ }
350
+ if (!runtimeBin) {
351
+ console.error(`\u274C ${runtimeName} not found in PATH.`);
352
+ console.error(" Install from: https://github.com/pigeonflow/brain-arch-v2");
353
+ process.exit(1);
354
+ }
355
+ } else {
356
+ console.error(`\u274C Unknown runtime: ${runtimeName}`);
357
+ console.error(" Supported: openclaw, nullclaw");
358
+ process.exit(1);
359
+ }
360
+ const workspaceDir = path.join(AGENTS_DIR, owner, slug, "workspace");
361
+ if (opts.pull !== false) {
362
+ const ver = version || "latest";
363
+ console.log(`\u{1F4E5} Pulling ${owner}/${slug}@${ver}...`);
364
+ try {
365
+ const { url, version: resolvedVersion } = await apiRequest(
366
+ "GET",
367
+ `/v1/bundles/${owner}/${slug}/${ver}/download`
368
+ );
369
+ console.log(` Version: ${resolvedVersion}`);
370
+ const res = await fetch(url);
371
+ if (!res.ok) throw new Error(`Download failed: ${res.statusText}`);
372
+ const tarball = Buffer.from(await res.arrayBuffer());
373
+ fs.mkdirSync(workspaceDir, { recursive: true });
374
+ const tmpFile = path.join(os.tmpdir(), `clawdock-run-${Date.now()}.tar.gz`);
375
+ fs.writeFileSync(tmpFile, tarball);
376
+ await tar.extract({ file: tmpFile, cwd: workspaceDir, strip: 1 });
377
+ fs.unlinkSync(tmpFile);
378
+ console.log(` Extracted to ${workspaceDir}`);
379
+ } catch (err) {
380
+ console.error(`\u274C Pull failed: ${err.message}`);
381
+ process.exit(1);
382
+ }
383
+ }
384
+ if (!fs.existsSync(workspaceDir)) {
385
+ console.error(`\u274C Workspace not found: ${workspaceDir}`);
386
+ console.error(" Run without --no-pull to download first.");
387
+ process.exit(1);
388
+ }
389
+ const resolvedModel = model || `${provider}/claude-sonnet-4`;
390
+ console.log(`
391
+ \u{1F980} Starting ${owner}/${slug}...`);
392
+ console.log(` Runtime: ${runtimeSpec}`);
393
+ console.log(` Workspace: ${workspaceDir}`);
394
+ console.log(` Model: ${resolvedModel}`);
395
+ console.log();
396
+ if (runtimeName === "openclaw") {
397
+ try {
398
+ const list = execSync("openclaw agents list --json 2>/dev/null", { encoding: "utf-8" });
399
+ const agents = JSON.parse(list);
400
+ if (!agents.find((a) => a.name === agentName)) {
401
+ throw new Error("not found");
402
+ }
403
+ } catch {
404
+ console.log(` Registering agent "${agentName}"...`);
405
+ try {
406
+ execSync(
407
+ `openclaw agents add ${agentName} --workspace ${workspaceDir} --model ${resolvedModel} --non-interactive`,
408
+ { stdio: "inherit" }
409
+ );
410
+ } catch (err) {
411
+ console.error(`\u274C Failed to register agent: ${err.message}`);
412
+ process.exit(1);
413
+ }
414
+ }
415
+ const agentDir = path.join(os.homedir(), ".openclaw", "agents", agentName, "agent");
416
+ const authDir = path.join(agentDir);
417
+ fs.mkdirSync(authDir, { recursive: true });
418
+ const authProfilesPath = path.join(authDir, "auth-profiles.json");
419
+ const authProfiles = {};
420
+ authProfiles[provider] = { type: "api-key", apiKey };
421
+ fs.writeFileSync(authProfilesPath, JSON.stringify(authProfiles, null, 2));
422
+ const child = spawn(
423
+ runtimeBin,
424
+ ["agent", "--agent", agentName, "--prompt", "Hello! I just pulled you from ClawDock. Introduce yourself."],
425
+ {
426
+ stdio: "inherit",
427
+ env: { ...process.env }
428
+ }
429
+ );
430
+ child.on("error", (err) => {
431
+ console.error(`\u274C Failed to start: ${err.message}`);
432
+ process.exit(1);
433
+ });
434
+ child.on("exit", (code) => process.exit(code || 0));
435
+ process.on("SIGINT", () => child.kill("SIGINT"));
436
+ process.on("SIGTERM", () => child.kill("SIGTERM"));
437
+ } else if (runtimeName === "nullclaw") {
438
+ const configPath = path.join(workspaceDir, ".nullclaw.json");
439
+ const nullclawConfig = {
440
+ default_temperature: 0.7,
441
+ models: {
442
+ providers: {
443
+ [provider]: { api_key: apiKey }
444
+ }
445
+ },
446
+ agents: {
447
+ defaults: {
448
+ model: { primary: resolvedModel }
449
+ }
450
+ },
451
+ channels: { cli: true },
452
+ memory: {
453
+ profile: "markdown_only",
454
+ backend: "markdown",
455
+ auto_save: true
456
+ }
457
+ };
458
+ fs.writeFileSync(configPath, JSON.stringify(nullclawConfig, null, 2));
459
+ const child = spawn(runtimeBin, ["--config", configPath, "--workspace", workspaceDir], {
460
+ stdio: "inherit",
461
+ cwd: workspaceDir
462
+ });
463
+ child.on("error", (err) => {
464
+ console.error(`\u274C Failed to start NullClaw: ${err.message}`);
465
+ process.exit(1);
466
+ });
467
+ child.on("exit", (code) => process.exit(code || 0));
468
+ process.on("SIGINT", () => child.kill("SIGINT"));
469
+ process.on("SIGTERM", () => child.kill("SIGTERM"));
470
+ }
471
+ });
238
472
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawdock",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "CLI for ClawDock — the agent registry",
5
5
  "bin": {
6
6
  "clawdock": "./dist/index.js"
@@ -23,7 +23,14 @@
23
23
  "@types/node": "^22.0.0",
24
24
  "@types/tar": "^6.1.0"
25
25
  },
26
- "files": ["dist"],
27
- "keywords": ["openclaw", "agent", "registry", "clawdock"],
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "keywords": [
30
+ "openclaw",
31
+ "agent",
32
+ "registry",
33
+ "clawdock"
34
+ ],
28
35
  "license": "MIT"
29
36
  }