clawdock 0.1.0 → 0.1.1

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 (2) hide show
  1. package/dist/index.js +109 -0
  2. package/package.json +10 -3
package/dist/index.js CHANGED
@@ -235,4 +235,113 @@ 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) => {
239
+ const { execSync, spawn } = await import("child_process");
240
+ const match = bundle.match(/^([^/]+)\/([^@]+)(?:@(.+))?$/);
241
+ if (!match) {
242
+ console.error("\u274C Invalid bundle format. Use: owner/slug[@version]");
243
+ process.exit(1);
244
+ }
245
+ const [, owner, slug, version] = match;
246
+ const workspaceDir = path.resolve(opts.dir, slug);
247
+ if (opts.pull !== false) {
248
+ console.log(`\u{1F4E5} Pulling ${bundle}...`);
249
+ const ver = version || "latest";
250
+ try {
251
+ const { url, version: resolvedVersion } = await apiRequest(
252
+ "GET",
253
+ `/v1/bundles/${owner}/${slug}/${ver}/download`
254
+ );
255
+ console.log(` Version: ${resolvedVersion}`);
256
+ const res = await fetch(url);
257
+ if (!res.ok) throw new Error(`Download failed: ${res.statusText}`);
258
+ const tarball = Buffer.from(await res.arrayBuffer());
259
+ fs.mkdirSync(workspaceDir, { recursive: true });
260
+ const tmpFile = path.join(os.tmpdir(), `clawdock-run-${Date.now()}.tar.gz`);
261
+ fs.writeFileSync(tmpFile, tarball);
262
+ await tar.extract({ file: tmpFile, cwd: workspaceDir, strip: 1 });
263
+ fs.unlinkSync(tmpFile);
264
+ console.log(` Extracted to ${workspaceDir}`);
265
+ } catch (err) {
266
+ console.error(`\u274C Pull failed: ${err.message}`);
267
+ process.exit(1);
268
+ }
269
+ }
270
+ if (!fs.existsSync(workspaceDir)) {
271
+ console.error(`\u274C Workspace not found: ${workspaceDir}`);
272
+ console.error(" Run without --no-pull, or pull first with: clawdock pull");
273
+ process.exit(1);
274
+ }
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) {
283
+ try {
284
+ execSync(`which ${c} 2>/dev/null || test -x ${c}`, { stdio: "pipe" });
285
+ nullclawBin = c;
286
+ break;
287
+ } catch {
288
+ }
289
+ }
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 } : {}
307
+ }
308
+ },
309
+ agents: {
310
+ defaults: {
311
+ model: { primary: model }
312
+ }
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"));
346
+ });
238
347
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawdock",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
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
  }