cueclaw 0.1.1 → 0.1.2

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 CHANGED
@@ -38,7 +38,7 @@ Confirm the plan, and CueClaw runs it continuously in the background as a daemon
38
38
  - **Multi-channel** — TUI, WhatsApp, or Telegram. All channels share identical capabilities.
39
39
  - **Triggers** — Poll scripts, cron schedules, or manual. Runs as a system service with auto-restart.
40
40
  - **Parallel DAG execution** — Independent steps run concurrently. Dependencies are respected.
41
- - **Container isolation** — Agents run in Docker with filesystem isolation and mount allowlists.
41
+ - **Container isolation** (optional) — Agents can run in Docker with filesystem isolation and mount allowlists. Defaults to local mode with PreToolUse hooks for permission control.
42
42
 
43
43
  ## Install
44
44
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TriggerLoop
3
- } from "./chunk-W274JWEK.js";
3
+ } from "./chunk-FAT2VKMJ.js";
4
4
  import {
5
5
  getServiceStatus
6
6
  } from "./chunk-PZZ6FBGB.js";
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-KRNAXOQ4.js";
11
11
  import {
12
12
  MessageRouter
13
- } from "./chunk-LSL3FRCU.js";
13
+ } from "./chunk-IB6TU7TP.js";
14
14
  import {
15
15
  askQuestionTool,
16
16
  buildPlannerSystemPrompt,
@@ -1309,7 +1309,7 @@ async function startBotChannels(bridge, config) {
1309
1309
  async function connectBotChannels(config, router, botChannels) {
1310
1310
  if (config.telegram?.enabled && config.telegram.token) {
1311
1311
  try {
1312
- const { TelegramChannel } = await import("./telegram-P6DBJ7WZ.js");
1312
+ const { TelegramChannel } = await import("./telegram-EFPHL4HC.js");
1313
1313
  const tg = new TelegramChannel(
1314
1314
  config.telegram.token,
1315
1315
  config.telegram.allowed_users ?? [],
@@ -9,15 +9,18 @@ import {
9
9
  import { CronExpressionParser } from "cron-parser";
10
10
 
11
11
  // src/trigger.ts
12
- import { execFileSync } from "child_process";
12
+ import { execFile } from "child_process";
13
+ import { promisify } from "util";
14
+ var execFileAsync = promisify(execFile);
13
15
  var CHECK_SCRIPT_TIMEOUT = 3e4;
14
- function evaluatePollTrigger(workflow, trigger, db) {
16
+ async function evaluatePollTrigger(workflow, trigger, db) {
15
17
  let stdout;
16
18
  try {
17
- stdout = execFileSync("sh", ["-c", trigger.check_script], {
19
+ const result = await execFileAsync("sh", ["-c", trigger.check_script], {
18
20
  timeout: CHECK_SCRIPT_TIMEOUT,
19
21
  encoding: "utf-8"
20
- }).trim();
22
+ });
23
+ stdout = result.stdout.trim();
21
24
  } catch (err) {
22
25
  logger.error({ workflowId: workflow.id, err }, "Poll check_script failed");
23
26
  db.prepare(
@@ -125,9 +128,9 @@ var TriggerLoop = class {
125
128
  this.unregisterTrigger(workflow.id);
126
129
  const trigger = workflow.trigger;
127
130
  if (trigger.type === "poll") {
128
- const interval = setInterval(() => {
131
+ const interval = setInterval(async () => {
129
132
  try {
130
- const result = evaluatePollTrigger(workflow, trigger, this.db);
133
+ const result = await evaluatePollTrigger(workflow, trigger, this.db);
131
134
  if (result) {
132
135
  this.executeTrigger(workflow, result.data);
133
136
  }
@@ -47,6 +47,12 @@ var MessageRouter = class {
47
47
  this.cleanupTimer = null;
48
48
  }
49
49
  }
50
+ /** Disconnect all registered channels */
51
+ async disconnectAll() {
52
+ await Promise.allSettled(
53
+ [...this.channels.values()].map((c) => c.disconnect())
54
+ );
55
+ }
50
56
  /** Broadcast a notification to all connected channels (used by MCP handler) */
51
57
  broadcastNotification(message) {
52
58
  for (const channel of this.channels.values()) {
@@ -70,8 +76,14 @@ var MessageRouter = class {
70
76
  }
71
77
  if (text.startsWith("/") || text.startsWith("!")) {
72
78
  await this.handleCommand(channel, chatJid, text);
73
- } else if (this.hasPendingConfirmation(chatJid)) {
74
- await this.handleConfirmation(channel, chatJid, text);
79
+ } else if (this.pendingConfirmations.has(chatJid)) {
80
+ const pending = this.pendingConfirmations.get(chatJid);
81
+ if (Date.now() > pending.expiresAt) {
82
+ this.pendingConfirmations.delete(chatJid);
83
+ await channel.sendMessage(chatJid, "Your pending plan has expired. Send a new description to start over.");
84
+ } else {
85
+ await this.handleConfirmation(channel, chatJid, text);
86
+ }
75
87
  } else {
76
88
  await this.classifyAndRoute(channel, chatJid, text);
77
89
  }
@@ -272,15 +284,6 @@ ${steps}`);
272
284
  }
273
285
  }
274
286
  }
275
- hasPendingConfirmation(chatJid) {
276
- const pending = this.pendingConfirmations.get(chatJid);
277
- if (!pending) return false;
278
- if (Date.now() > pending.expiresAt) {
279
- this.pendingConfirmations.delete(chatJid);
280
- return false;
281
- }
282
- return true;
283
- }
284
287
  isRateLimited(chatJid) {
285
288
  const now = Date.now();
286
289
  const timestamps = this.messageTimestamps.get(chatJid) ?? [];
package/dist/cli.js CHANGED
@@ -26,7 +26,10 @@ import {
26
26
  // src/cli.ts
27
27
  import { Command } from "commander";
28
28
  import { createInterface } from "readline";
29
- var program = new Command().name("cueclaw").description("Orchestrate agent workflows with natural language").version("0.0.1");
29
+ import { createRequire } from "module";
30
+ var require2 = createRequire(import.meta.url);
31
+ var { version: pkgVersion } = require2("../package.json");
32
+ var program = new Command().name("cueclaw").description("Orchestrate agent workflows with natural language").version(pkgVersion);
30
33
  program.command("info").description("Show loaded config, paths, versions").action(() => {
31
34
  try {
32
35
  const config = loadConfig();
@@ -333,9 +336,19 @@ program.command("delete").argument("<workflow-id>", "Workflow ID").description("
333
336
  }
334
337
  });
335
338
  var daemonCmd = program.command("daemon").description("Manage background daemon");
336
- daemonCmd.command("start").option("--detach", "Run in background").description("Start the daemon").action(async () => {
339
+ daemonCmd.command("start").option("--detach", "Run in background").description("Start the daemon").action(async (opts) => {
340
+ if (opts.detach) {
341
+ const { spawn } = await import("child_process");
342
+ const child = spawn(process.execPath, [process.argv[1], "daemon", "start"], {
343
+ detached: true,
344
+ stdio: "ignore"
345
+ });
346
+ child.unref();
347
+ console.log(`Daemon started in background (PID ${child.pid})`);
348
+ return;
349
+ }
337
350
  try {
338
- const { startDaemon } = await import("./daemon-HPD3D7TJ.js");
351
+ const { startDaemon } = await import("./daemon-WOR4GE5C.js");
339
352
  await startDaemon();
340
353
  } catch (err) {
341
354
  logger.error({ err }, "Daemon failed");
@@ -357,6 +370,20 @@ daemonCmd.command("stop").description("Stop the daemon").action(async () => {
357
370
  process.exit(1);
358
371
  }
359
372
  });
373
+ daemonCmd.command("restart").description("Restart the daemon").action(async () => {
374
+ const { getServiceStatus, stopService } = await import("./service-VTUYSAAZ.js");
375
+ const status = getServiceStatus();
376
+ if (status === "running") {
377
+ const result = stopService();
378
+ if (!result.success) {
379
+ console.log(`Failed to stop daemon: ${result.error}`);
380
+ process.exit(1);
381
+ }
382
+ console.log("Daemon stopped.");
383
+ }
384
+ const { startDaemon } = await import("./daemon-WOR4GE5C.js");
385
+ await startDaemon();
386
+ });
360
387
  daemonCmd.command("install").description("Install system service").action(async () => {
361
388
  const { installService } = await import("./service-VTUYSAAZ.js");
362
389
  const result = installService();
@@ -399,7 +426,7 @@ botCmd.command("start").description("Start all configured bot channels").action(
399
426
  try {
400
427
  const config = loadConfig();
401
428
  const db = initDb();
402
- const { MessageRouter } = await import("./router-F5PJC74R.js");
429
+ const { MessageRouter } = await import("./router-ID6RN5AT.js");
403
430
  const router = new MessageRouter(db, config, process.cwd());
404
431
  if (config.whatsapp?.enabled) {
405
432
  const { WhatsAppChannel } = await import("./whatsapp-HFMOFSFI.js");
@@ -413,7 +440,7 @@ botCmd.command("start").description("Start all configured bot channels").action(
413
440
  logger.info("WhatsApp channel started");
414
441
  }
415
442
  if (config.telegram?.enabled) {
416
- const { TelegramChannel } = await import("./telegram-P6DBJ7WZ.js");
443
+ const { TelegramChannel } = await import("./telegram-EFPHL4HC.js");
417
444
  const tg = new TelegramChannel(
418
445
  config.telegram.token,
419
446
  config.telegram.allowed_users ?? [],
@@ -452,7 +479,7 @@ program.command("tui").description("Start interactive TUI").option("--skip-onboa
452
479
  enableTuiLogging();
453
480
  const React = await import("react");
454
481
  const { render } = await import("ink");
455
- const { App } = await import("./app-ZJG6ANUK.js");
482
+ const { App } = await import("./app-LWDIWH7K.js");
456
483
  render(React.createElement(App, { cwd: process.cwd(), skipOnboarding: opts.skipOnboarding }));
457
484
  } catch (err) {
458
485
  logger.error({ err }, "Failed to start TUI");
@@ -466,7 +493,7 @@ program.option("--skip-onboarding", "Skip first-run onboarding wizard").action(a
466
493
  enableTuiLogging();
467
494
  const React = await import("react");
468
495
  const { render } = await import("ink");
469
- const { App } = await import("./app-ZJG6ANUK.js");
496
+ const { App } = await import("./app-LWDIWH7K.js");
470
497
  render(React.createElement(App, { cwd: process.cwd(), skipOnboarding }));
471
498
  } catch (err) {
472
499
  logger.error({ err }, "Failed to start TUI");
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  TriggerLoop
3
- } from "./chunk-W274JWEK.js";
3
+ } from "./chunk-FAT2VKMJ.js";
4
4
  import {
5
5
  MessageRouter
6
- } from "./chunk-LSL3FRCU.js";
6
+ } from "./chunk-IB6TU7TP.js";
7
7
  import "./chunk-WE5J7GMR.js";
8
8
  import "./chunk-DVQFSFIZ.js";
9
9
  import "./chunk-ZCK3IFLC.js";
@@ -42,7 +42,7 @@ async function startDaemon() {
42
42
  }
43
43
  if (config.telegram?.enabled) {
44
44
  try {
45
- const { TelegramChannel } = await import("./telegram-P6DBJ7WZ.js");
45
+ const { TelegramChannel } = await import("./telegram-EFPHL4HC.js");
46
46
  const tg = new TelegramChannel(
47
47
  config.telegram.token,
48
48
  config.telegram.allowed_users ?? [],
@@ -62,15 +62,16 @@ async function startDaemon() {
62
62
  triggerLoop.start();
63
63
  router.start();
64
64
  logger.info("Daemon started");
65
- const shutdown = () => {
65
+ const shutdown = async () => {
66
66
  logger.info("Shutting down daemon...");
67
67
  triggerLoop.stop();
68
68
  router.stop();
69
+ await router.disconnectAll();
69
70
  db.close();
70
71
  process.exit(0);
71
72
  };
72
- process.on("SIGTERM", shutdown);
73
- process.on("SIGINT", shutdown);
73
+ process.on("SIGTERM", () => void shutdown());
74
+ process.on("SIGINT", () => void shutdown());
74
75
  }
75
76
  async function recoverRunningWorkflows(db, router) {
76
77
  const interruptedRuns = db.prepare(
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  MessageRouter
3
- } from "./chunk-LSL3FRCU.js";
3
+ } from "./chunk-IB6TU7TP.js";
4
4
  import "./chunk-WE5J7GMR.js";
5
5
  import "./chunk-DVQFSFIZ.js";
6
6
  import "./chunk-ZCK3IFLC.js";
@@ -78,7 +78,8 @@ var TelegramChannel = class {
78
78
  ownsJid(jid) {
79
79
  return /^-?\d+$/.test(jid);
80
80
  }
81
- async setTyping(jid, _isTyping) {
81
+ async setTyping(jid, isTyping) {
82
+ if (!isTyping) return;
82
83
  try {
83
84
  await this.bot.api.sendChatAction(Number(jid), "typing");
84
85
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cueclaw",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Orchestrate agent workflows with natural language. Natural language in, executable DAG out.",
5
5
  "type": "module",
6
6
  "bin": {