crawd 0.8.5 → 0.8.7

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/dist/cli.js CHANGED
@@ -84,7 +84,9 @@ var ConfigSchema = z.object({
84
84
  /** Seconds of inactivity before going idle */
85
85
  idleAfter: z.number().default(180),
86
86
  /** Seconds of inactivity before going to sleep (must be > idleAfter) */
87
- sleepAfter: z.number().default(360)
87
+ sleepAfter: z.number().default(360),
88
+ /** Seconds for chat batch throttle window (leading-edge) */
89
+ chatBatchWindow: z.number().default(20)
88
90
  }).default({}),
89
91
  /** Stream configuration */
90
92
  stream: z.object({
@@ -512,6 +514,9 @@ crawd config set vibe.idleAfter 180
512
514
  # Seconds of inactivity before going to sleep (default: 360)
513
515
  crawd config set vibe.sleepAfter 360
514
516
 
517
+ # Chat batch throttle window in seconds (default: 20)
518
+ crawd config set vibe.chatBatchWindow 20
519
+
515
520
  # Disable vibing entirely
516
521
  crawd config set vibe.enabled false
517
522
  \`\`\`
@@ -691,6 +696,7 @@ function buildEnv(config) {
691
696
  env.VIBE_INTERVAL_MS = String(config.vibe.interval * 1e3);
692
697
  env.IDLE_AFTER_MS = String(config.vibe.idleAfter * 1e3);
693
698
  env.SLEEP_AFTER_IDLE_MS = String((config.vibe.sleepAfter - config.vibe.idleAfter) * 1e3);
699
+ env.CHAT_BATCH_WINDOW_MS = String(config.vibe.chatBatchWindow * 1e3);
694
700
  env.YOUTUBE_ENABLED = String(config.chat.youtube.enabled);
695
701
  if (config.chat.youtube.videoId) {
696
702
  env.YOUTUBE_VIDEO_ID = config.chat.youtube.videoId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crawd",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
4
4
  "description": "CLI for crawd.bot - AI agent livestreaming platform",
5
5
  "type": "module",
6
6
  "types": "./dist/types.d.ts",
@@ -23,14 +23,6 @@
23
23
  "bin": {
24
24
  "crawd": "./dist/cli.js"
25
25
  },
26
- "scripts": {
27
- "dev": "tsx src/cli.ts",
28
- "build": "tsup src/cli.ts src/types.ts src/client.ts --format esm --dts --clean",
29
- "build:backend": "tsup src/backend/index.ts --format esm --out-dir dist/backend --clean",
30
- "build:plugin": "tsup src/plugin.ts --format esm --out-dir dist --dts",
31
- "build:all": "pnpm build && pnpm build:backend",
32
- "typecheck": "tsc --noEmit"
33
- },
34
26
  "keywords": [
35
27
  "ai",
36
28
  "agent",
@@ -82,5 +74,13 @@
82
74
  "openclaw": {
83
75
  "optional": true
84
76
  }
77
+ },
78
+ "scripts": {
79
+ "dev": "tsx src/cli.ts",
80
+ "build": "tsup src/cli.ts src/types.ts src/client.ts --format esm --dts --clean",
81
+ "build:backend": "tsup src/backend/index.ts --format esm --out-dir dist/backend --clean",
82
+ "build:plugin": "tsup src/plugin.ts --format esm --out-dir dist --dts",
83
+ "build:all": "pnpm build && pnpm build:backend",
84
+ "typecheck": "tsc --noEmit"
85
85
  }
86
- }
86
+ }
@@ -2,7 +2,6 @@ import { randomUUID } from 'crypto'
2
2
  import WebSocket from 'ws'
3
3
  import type { ChatMessage } from '../lib/chat/types'
4
4
 
5
- const DEFAULT_BATCH_WINDOW_MS = 20_000
6
5
  const SESSION_KEY = process.env.CRAWD_CHANNEL_ID || 'agent:main:crawd:live'
7
6
 
8
7
  /** Coordinator configuration */
@@ -15,8 +14,8 @@ export type CoordinatorConfig = {
15
14
  idleAfterMs: number
16
15
  /** Go sleep after this much inactivity while idle (ms). Default: 60000 (1 min) */
17
16
  sleepAfterIdleMs: number
18
- /** Chat message batching window (ms). Default: 20000 (20 sec) */
19
- chatBatchWindowMs: number
17
+ /** Chat batch throttle window (ms). Default: 20000 (20 sec) */
18
+ batchWindowMs: number
20
19
  /** The autonomous "vibe" prompt sent periodically */
21
20
  vibePrompt: string
22
21
  }
@@ -26,7 +25,7 @@ export const DEFAULT_CONFIG: CoordinatorConfig = {
26
25
  vibeIntervalMs: 30_000,
27
26
  idleAfterMs: 180_000,
28
27
  sleepAfterIdleMs: 180_000,
29
- chatBatchWindowMs: DEFAULT_BATCH_WINDOW_MS,
28
+ batchWindowMs: 20_000,
30
29
  vibePrompt: `[CRAWD:VIBE] You are on a livestream. Make sure the crawd skill is loaded. Do one thing on the internet or ask the chat something. Respond with LIVESTREAM_REPLIED after using a tool, or NO_REPLY if you have nothing to say.`,
31
30
  }
32
31
 
@@ -557,9 +556,9 @@ export class Coordinator {
557
556
  this.config = { ...this.config, ...config }
558
557
  this.logger.log('[Coordinator] Config updated:', {
559
558
  vibeIntervalMs: this.config.vibeIntervalMs,
560
- chatBatchWindowMs: this.config.chatBatchWindowMs,
561
559
  idleAfterMs: this.config.idleAfterMs,
562
560
  sleepAfterIdleMs: this.config.sleepAfterIdleMs,
561
+ batchWindowMs: this.config.batchWindowMs,
563
562
  })
564
563
  }
565
564
 
@@ -592,6 +591,7 @@ export class Coordinator {
592
591
  vibeIntervalMs: this.config.vibeIntervalMs,
593
592
  idleAfterMs: this.config.idleAfterMs,
594
593
  sleepAfterIdleMs: this.config.sleepAfterIdleMs,
594
+ batchWindowMs: this.config.batchWindowMs,
595
595
  })
596
596
  }
597
597
 
@@ -840,7 +840,7 @@ export class Coordinator {
840
840
  // Leading edge: if no timer running, flush immediately and start cooldown
841
841
  if (!this.timer) {
842
842
  this.flush()
843
- this.timer = this.clock.setTimeout(() => this.onCooldownEnd(), this.config.chatBatchWindowMs)
843
+ this.timer = this.clock.setTimeout(() => this.onCooldownEnd(), this.config.batchWindowMs)
844
844
  }
845
845
  // Otherwise, message is buffered and will be flushed when cooldown ends
846
846
  }
@@ -851,7 +851,7 @@ export class Coordinator {
851
851
  // If messages accumulated during cooldown, flush them and restart cooldown
852
852
  if (this.buffer.length > 0) {
853
853
  this.flush()
854
- this.timer = this.clock.setTimeout(() => this.onCooldownEnd(), this.config.chatBatchWindowMs)
854
+ this.timer = this.clock.setTimeout(() => this.onCooldownEnd(), this.config.batchWindowMs)
855
855
  }
856
856
  }
857
857
 
@@ -46,6 +46,7 @@ export type CrawdConfig = {
46
46
  intervalMs: number
47
47
  idleAfterMs: number
48
48
  sleepAfterIdleMs: number
49
+ batchWindowMs: number
49
50
  prompt?: string
50
51
  }
51
52
  chat: {
@@ -391,6 +392,7 @@ export class CrawdBackend {
391
392
  vibeIntervalMs: this.config.vibe.intervalMs,
392
393
  idleAfterMs: this.config.vibe.idleAfterMs,
393
394
  sleepAfterIdleMs: this.config.vibe.sleepAfterIdleMs,
395
+ batchWindowMs: this.config.vibe.batchWindowMs,
394
396
  }
395
397
  if (this.config.vibe.prompt) {
396
398
  coordConfig.vibePrompt = this.config.vibe.prompt
@@ -432,6 +434,11 @@ export class CrawdBackend {
432
434
  socket.emit('crawd:mcap', { mcap: this.latestMcap })
433
435
  }
434
436
 
437
+ // Sync current coordinator state so the overlay knows the initial animation
438
+ if (this.coordinator) {
439
+ socket.emit('crawd:status', { status: this.coordinator.state })
440
+ }
441
+
435
442
  socket.on('crawd:talk:done', (data: { id?: string }) => {
436
443
  if (data?.id) {
437
444
  this.logger.info(`Talk ack received: ${data.id}`)
@@ -620,6 +627,7 @@ export function configFromEnv(): CrawdConfig {
620
627
  intervalMs: Number(process.env.VIBE_INTERVAL_MS || 30_000),
621
628
  idleAfterMs: Number(process.env.IDLE_AFTER_MS || 180_000),
622
629
  sleepAfterIdleMs: Number(process.env.SLEEP_AFTER_IDLE_MS || 180_000),
630
+ batchWindowMs: Number(process.env.CHAT_BATCH_WINDOW_MS || 20_000),
623
631
  prompt: process.env.VIBE_PROMPT,
624
632
  },
625
633
  chat: {
@@ -110,6 +110,9 @@ crawd config set vibe.idleAfter 180
110
110
  # Seconds of inactivity before going to sleep (default: 360)
111
111
  crawd config set vibe.sleepAfter 360
112
112
 
113
+ # Chat batch throttle window in seconds (default: 20)
114
+ crawd config set vibe.chatBatchWindow 20
115
+
113
116
  # Disable vibing entirely
114
117
  crawd config set vibe.enabled false
115
118
  \`\`\`
@@ -54,6 +54,7 @@ function buildEnv(config: Config): NodeJS.ProcessEnv {
54
54
  env.VIBE_INTERVAL_MS = String(config.vibe.interval * 1000)
55
55
  env.IDLE_AFTER_MS = String(config.vibe.idleAfter * 1000)
56
56
  env.SLEEP_AFTER_IDLE_MS = String((config.vibe.sleepAfter - config.vibe.idleAfter) * 1000)
57
+ env.CHAT_BATCH_WINDOW_MS = String(config.vibe.chatBatchWindow * 1000)
57
58
 
58
59
  env.YOUTUBE_ENABLED = String(config.chat.youtube.enabled)
59
60
  if (config.chat.youtube.videoId) {
@@ -50,6 +50,8 @@ export const ConfigSchema = z.object({
50
50
  idleAfter: z.number().default(180),
51
51
  /** Seconds of inactivity before going to sleep (must be > idleAfter) */
52
52
  sleepAfter: z.number().default(360),
53
+ /** Seconds for chat batch throttle window (leading-edge) */
54
+ chatBatchWindow: z.number().default(20),
53
55
  }).default({}),
54
56
 
55
57
  /** Stream configuration */
package/src/plugin.ts CHANGED
@@ -99,6 +99,7 @@ function parsePluginConfig(raw: Record<string, unknown> | undefined): CrawdConfi
99
99
  intervalMs: typeof vibe.intervalMs === 'number' ? vibe.intervalMs : 10_000,
100
100
  idleAfterMs: typeof vibe.idleAfterMs === 'number' ? vibe.idleAfterMs : 30_000,
101
101
  sleepAfterIdleMs: typeof vibe.sleepAfterIdleMs === 'number' ? vibe.sleepAfterIdleMs : 60_000,
102
+ batchWindowMs: typeof vibe.batchWindowMs === 'number' ? vibe.batchWindowMs : 20_000,
102
103
  prompt: typeof vibe.prompt === 'string' ? vibe.prompt : undefined,
103
104
  },
104
105
  chat: {
@@ -145,6 +146,7 @@ const crawdConfigSchema = {
145
146
  'vibe.intervalMs': { label: 'Vibe Interval (ms)', advanced: true },
146
147
  'vibe.idleAfterMs': { label: 'Idle After (ms)', advanced: true },
147
148
  'vibe.sleepAfterIdleMs': { label: 'Sleep After Idle (ms)', advanced: true },
149
+ 'vibe.batchWindowMs': { label: 'Chat Batch Window (ms)', advanced: true },
148
150
  'vibe.prompt': { label: 'Vibe Prompt', advanced: true },
149
151
  'chat.youtube.enabled': { label: 'YouTube Chat' },
150
152
  'chat.youtube.videoId': { label: 'YouTube Video ID' },
@@ -249,36 +251,6 @@ const plugin: PluginDefinition = {
249
251
  { name: 'livestream_reply' },
250
252
  )
251
253
 
252
- // livestream_config — runtime coordinator tuning
253
- api.registerTool(
254
- {
255
- name: 'livestream_config',
256
- label: 'Livestream Config',
257
- description:
258
- 'Update livestream coordinator settings at runtime. Use when asked to change vibe speed/frequency, chat throttle/batch window, idle timeout, or sleep timeout.',
259
- parameters: Type.Object({
260
- vibeIntervalMs: Type.Optional(Type.Number({ description: 'Milliseconds between autonomous vibe prompts (lower = more frequent vibes)' })),
261
- chatBatchWindowMs: Type.Optional(Type.Number({ description: 'Milliseconds to batch chat messages before sending to agent (lower = faster chat response, higher = more messages per batch)' })),
262
- idleAfterMs: Type.Optional(Type.Number({ description: 'Milliseconds of inactivity before transitioning to idle state' })),
263
- sleepAfterIdleMs: Type.Optional(Type.Number({ description: 'Milliseconds in idle before transitioning to sleep state' })),
264
- vibeEnabled: Type.Optional(Type.Boolean({ description: 'Enable or disable autonomous vibe prompts' })),
265
- }),
266
- async execute(_toolCallId: string, params: unknown) {
267
- const b = await ensureBackend()
268
- if (!b.coordinator) {
269
- return { content: [{ type: 'text', text: 'Coordinator not running' }] }
270
- }
271
- const p = params as Record<string, unknown>
272
- b.coordinator.updateConfig(p)
273
- const { state, config: cfg } = b.coordinator.getState()
274
- return {
275
- content: [{ type: 'text', text: `Config updated. state=${state}, vibeInterval=${cfg.vibeIntervalMs}ms, chatBatch=${cfg.chatBatchWindowMs}ms, idleAfter=${cfg.idleAfterMs}ms, sleepAfter=${cfg.sleepAfterIdleMs}ms, vibes=${cfg.vibeEnabled}` }],
276
- }
277
- },
278
- },
279
- { name: 'livestream_config' },
280
- )
281
-
282
254
  // Service lifecycle
283
255
  api.registerService({
284
256
  id: 'crawd',