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.
- package/README.md +59 -14
- package/dist/index.js +234 -0
- 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,
|
|
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
|
-
#
|
|
15
|
-
clawdock
|
|
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
|
-
#
|
|
18
|
-
clawdock
|
|
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
|
-
|
|
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
|
|
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.
|
|
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": [
|
|
27
|
-
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"keywords": [
|
|
30
|
+
"openclaw",
|
|
31
|
+
"agent",
|
|
32
|
+
"registry",
|
|
33
|
+
"clawdock"
|
|
34
|
+
],
|
|
28
35
|
"license": "MIT"
|
|
29
36
|
}
|