@iamoberlin/chorus 1.3.3 → 1.3.4

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/index.ts CHANGED
@@ -38,7 +38,7 @@ import {
38
38
  import * as prayers from "./src/prayers/prayers.js";
39
39
  import * as prayerStore from "./src/prayers/store.js";
40
40
 
41
- const VERSION = "1.3.0"; // Async spawn: unblock gateway event loop during choir execution
41
+ const VERSION = "1.3.4"; // Restore --message flag (required by openclaw agent CLI)
42
42
 
43
43
  const plugin = {
44
44
  id: "chorus",
@@ -2,7 +2,7 @@
2
2
  "id": "chorus",
3
3
  "name": "CHORUS",
4
4
  "description": "CHORUS: Hierarchy Of Recursive Unified Self-improvement",
5
- "version": "1.1.3",
5
+ "version": "1.3.4",
6
6
  "author": "Oberlin",
7
7
  "homepage": "https://chorus.oberlin.ai",
8
8
  "repository": "https://github.com/iamoberlin/chorus",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iamoberlin/chorus",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "description": "CHORUS: Hierarchy Of Recursive Unified Self-improvement — with Prayer Requests social network",
5
5
  "author": "Oberlin <iam@oberlin.ai>",
6
6
  "license": "MIT",
@@ -11,7 +11,7 @@ import { recordExecution, type ChoirExecution } from "./metrics.js";
11
11
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
12
12
  import { join } from "path";
13
13
  import { homedir } from "os";
14
- import { spawnSync } from "child_process";
14
+ import { spawn } from "child_process";
15
15
 
16
16
  // Workspace path for research output
17
17
  const WORKSPACE_PATH = process.env.OPENCLAW_WORKSPACE || join(homedir(), ".openclaw", "workspace");
@@ -274,17 +274,25 @@ CRITICAL: If sending alerts via iMessage, use PLAIN TEXT ONLY (no markdown).
274
274
  }
275
275
 
276
276
  if (!result) {
277
- // CLI fallback - use stdin to avoid arg length limits
277
+ // CLI fallback - use stdin to avoid arg length limits (async to avoid blocking event loop)
278
278
  log.debug(`[purpose-research] Using CLI fallback for "${purpose.name}"`);
279
- result = spawnSync("openclaw", [
280
- "agent",
281
- "--session-id", `chorus:purpose:${purpose.id}`,
282
- "--json",
283
- ], {
284
- input: prompt,
285
- encoding: "utf-8",
286
- timeout: config.researchTimeoutMs,
287
- maxBuffer: 1024 * 1024, // 1MB
279
+ result = await new Promise<any>((resolve) => {
280
+ const child = spawn("openclaw", [
281
+ "agent",
282
+ "--session-id", `chorus:purpose:${purpose.id}`,
283
+ "--message", prompt,
284
+ "--json",
285
+ ], { stdio: ['pipe', 'pipe', 'pipe'] });
286
+
287
+ let stdout = '';
288
+ let stderr = '';
289
+ const maxBuffer = 1024 * 1024;
290
+ child.stdout.on('data', (d: Buffer) => { if (stdout.length < maxBuffer) stdout += d.toString(); });
291
+ child.stderr.on('data', (d: Buffer) => { if (stderr.length < maxBuffer) stderr += d.toString(); });
292
+
293
+ const timer = setTimeout(() => { child.kill('SIGTERM'); }, config.researchTimeoutMs);
294
+ child.on('close', (code) => { clearTimeout(timer); resolve({ status: code, stdout, stderr }); });
295
+ child.on('error', (err) => { clearTimeout(timer); resolve({ status: 1, stdout: '', stderr: String(err) }); });
288
296
  });
289
297
 
290
298
  if (result.status === 0 && result.stdout) {
package/src/scheduler.ts CHANGED
@@ -12,7 +12,7 @@ import { recordExecution, type ChoirExecution } from "./metrics.js";
12
12
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
13
13
  import { join } from "path";
14
14
  import { homedir } from "os";
15
- import { spawnSync } from "child_process";
15
+ import { spawn } from "child_process";
16
16
 
17
17
  interface ChoirContext {
18
18
  choirId: string;
@@ -128,20 +128,28 @@ export function createChoirScheduler(
128
128
  try {
129
129
  const prompt = buildPrompt(choir);
130
130
 
131
- // Use openclaw agent CLI (same as Vision mode)
132
- const result = spawnSync('openclaw', [
133
- 'agent',
134
- '--session-id', `chorus:${choir.id}`,
135
- '--message', prompt,
136
- '--json',
137
- ], {
138
- encoding: 'utf-8',
139
- timeout: 300000, // 5 min
140
- maxBuffer: 1024 * 1024, // 1MB
131
+ // Use openclaw agent CLI (async to avoid blocking event loop)
132
+ const result = await new Promise<{ status: number | null; stdout: string; stderr: string }>((resolve) => {
133
+ const child = spawn('openclaw', [
134
+ 'agent',
135
+ '--session-id', `chorus:${choir.id}`,
136
+ '--message', prompt,
137
+ '--json',
138
+ ], { stdio: ['pipe', 'pipe', 'pipe'] });
139
+
140
+ let stdout = '';
141
+ let stderr = '';
142
+ const maxBuffer = 1024 * 1024;
143
+ child.stdout.on('data', (d: Buffer) => { if (stdout.length < maxBuffer) stdout += d.toString(); });
144
+ child.stderr.on('data', (d: Buffer) => { if (stderr.length < maxBuffer) stderr += d.toString(); });
145
+
146
+ const timer = setTimeout(() => { child.kill('SIGTERM'); }, 300000); // 5 min
147
+ child.on('close', (code) => { clearTimeout(timer); resolve({ status: code, stdout, stderr }); });
148
+ child.on('error', (err) => { clearTimeout(timer); resolve({ status: 1, stdout: '', stderr: String(err) }); });
141
149
  });
142
150
 
143
151
  let output = "(no response)";
144
-
152
+
145
153
  if (result.status === 0 && result.stdout) {
146
154
  // Extract JSON from output (may have plugin logs before it)
147
155
  const stdout = result.stdout;