clawdock 0.1.1 → 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 +195 -70
  3. package/package.json +1 -1
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,7 +235,59 @@ 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
- program.command("run <bundle>").description("Pull and run an agent with NullClaw").option("--dir <path>", "Directory for agent workspace", ".").option("--port <port>", "Gateway port", "21789").option("--model <model>", "Model override (e.g. github-copilot/claude-sonnet-4)").option("--provider <name>", "Provider name", "github-copilot").option("--api-key <key>", "Provider API key (or set NULLCLAW_API_KEY)").option("--channel <ch>", "Channel to enable: cli, telegram, http", "cli").option("--nullclaw <path>", "Path to nullclaw binary").option("--no-pull", "Skip pull, use existing workspace").action(async (bundle, opts) => {
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) => {
239
291
  const { execSync, spawn } = await import("child_process");
240
292
  const match = bundle.match(/^([^/]+)\/([^@]+)(?:@(.+))?$/);
241
293
  if (!match) {
@@ -243,10 +295,72 @@ program.command("run <bundle>").description("Pull and run an agent with NullClaw
243
295
  process.exit(1);
244
296
  }
245
297
  const [, owner, slug, version] = match;
246
- const workspaceDir = path.resolve(opts.dir, slug);
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");
247
361
  if (opts.pull !== false) {
248
- console.log(`\u{1F4E5} Pulling ${bundle}...`);
249
362
  const ver = version || "latest";
363
+ console.log(`\u{1F4E5} Pulling ${owner}/${slug}@${ver}...`);
250
364
  try {
251
365
  const { url, version: resolvedVersion } = await apiRequest(
252
366
  "GET",
@@ -269,79 +383,90 @@ program.command("run <bundle>").description("Pull and run an agent with NullClaw
269
383
  }
270
384
  if (!fs.existsSync(workspaceDir)) {
271
385
  console.error(`\u274C Workspace not found: ${workspaceDir}`);
272
- console.error(" Run without --no-pull, or pull first with: clawdock pull");
386
+ console.error(" Run without --no-pull to download first.");
273
387
  process.exit(1);
274
388
  }
275
- let nullclawBin = opts.nullclaw || process.env.NULLCLAW_BIN;
276
- if (!nullclawBin) {
277
- const candidates = [
278
- "nullclaw",
279
- path.join(os.homedir(), ".nullclaw", "bin", "nullclaw"),
280
- "/usr/local/bin/nullclaw"
281
- ];
282
- for (const c of candidates) {
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}"...`);
283
405
  try {
284
- execSync(`which ${c} 2>/dev/null || test -x ${c}`, { stdio: "pipe" });
285
- nullclawBin = c;
286
- break;
287
- } catch {
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);
288
413
  }
289
414
  }
290
- }
291
- if (!nullclawBin) {
292
- console.error("\u274C NullClaw binary not found.");
293
- console.error(" Install: https://github.com/pigeonflow/brain-arch-v2");
294
- console.error(" Or pass --nullclaw /path/to/nullclaw");
295
- process.exit(1);
296
- }
297
- const providerApiKey = opts.apiKey || process.env.NULLCLAW_API_KEY;
298
- const model = opts.model || `${opts.provider}/claude-sonnet-4`;
299
- const configPath = path.join(workspaceDir, ".nullclaw.json");
300
- const manifestPath = path.join(workspaceDir, "manifest.json");
301
- const manifest = fs.existsSync(manifestPath) ? JSON.parse(fs.readFileSync(manifestPath, "utf-8")) : { name: slug };
302
- const config = {
303
- default_temperature: 0.7,
304
- models: {
305
- providers: {
306
- [opts.provider]: providerApiKey ? { api_key: providerApiKey } : {}
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 }
307
428
  }
308
- },
309
- agents: {
310
- defaults: {
311
- model: { primary: model }
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
312
456
  }
313
- },
314
- channels: {
315
- cli: opts.channel === "cli" || opts.channel === "all",
316
- ...opts.channel === "telegram" ? { telegram: true } : {},
317
- ...opts.channel === "http" ? { http: { port: parseInt(opts.port) } } : {}
318
- },
319
- memory: {
320
- profile: "markdown_only",
321
- backend: "markdown",
322
- auto_save: true
323
- }
324
- };
325
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
326
- console.log(`
327
- \u{1F980} Starting ${manifest.name || slug} with NullClaw...`);
328
- console.log(` Workspace: ${workspaceDir}`);
329
- console.log(` Model: ${model}`);
330
- console.log(` Channel: ${opts.channel}`);
331
- console.log();
332
- const child = spawn(nullclawBin, ["--config", configPath, "--workspace", workspaceDir], {
333
- stdio: "inherit",
334
- cwd: workspaceDir,
335
- env: { ...process.env }
336
- });
337
- child.on("error", (err) => {
338
- console.error(`\u274C Failed to start NullClaw: ${err.message}`);
339
- process.exit(1);
340
- });
341
- child.on("exit", (code) => {
342
- process.exit(code || 0);
343
- });
344
- process.on("SIGINT", () => child.kill("SIGINT"));
345
- process.on("SIGTERM", () => child.kill("SIGTERM"));
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
+ }
346
471
  });
347
472
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawdock",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "CLI for ClawDock — the agent registry",
5
5
  "bin": {
6
6
  "clawdock": "./dist/index.js"