cliclaw 1.0.16 → 1.0.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cliclaw",
3
- "version": "1.0.16",
3
+ "version": "1.0.17",
4
4
  "description": "Telegram bot bridging AI CLIs (Claude Code, Codex) to Forum Topics",
5
5
  "main": "index.ts",
6
6
  "scripts": {
@@ -1,5 +1,5 @@
1
1
  import { spawn } from 'child_process'
2
- import type { Session } from '../storage'
2
+ import type { Session, Message } from '../storage'
3
3
  import type { TokenUsage } from './claude'
4
4
 
5
5
  const HOME = process.env.HOME || process.env.USERPROFILE || '/root'
@@ -34,7 +34,22 @@ function extractTextFromObj(obj: any): string | null {
34
34
  return null
35
35
  }
36
36
 
37
- function spawnCodex(args: string[]): Promise<{ text: string; threadId: string | null; usage?: TokenUsage }> {
37
+ function buildPrompt(history: Message[], userMessage: string): string {
38
+ // history already contains the current user message as the last item (added before askCodex)
39
+ const prior = history.slice(0, -1).slice(-20) // up to last 20 prior messages
40
+ if (prior.length === 0) return userMessage
41
+ const lines = prior.map(m => {
42
+ let content = m.content
43
+ // Strip usage stats suffix (e.g. "\n`📊 ↑1234 ↓56`")
44
+ const usageIdx = content.lastIndexOf('\n`📊')
45
+ if (usageIdx > 0) content = content.slice(0, usageIdx)
46
+ if (content.length > 600) content = content.slice(0, 600) + '…'
47
+ return `${m.role === 'user' ? 'User' : 'Assistant'}: ${content}`
48
+ })
49
+ return `<conversation_history>\n${lines.join('\n')}\n</conversation_history>\n\nUser: ${userMessage}`
50
+ }
51
+
52
+ function spawnCodex(args: string[], stdinText?: string): Promise<{ text: string; threadId: string | null; usage?: TokenUsage }> {
38
53
  return new Promise((resolve, reject) => {
39
54
  let stdout = ''
40
55
  let stderr = ''
@@ -44,8 +59,12 @@ function spawnCodex(args: string[]): Promise<{ text: string; threadId: string |
44
59
  cwd: process.cwd(),
45
60
  shell: isWin,
46
61
  windowsHide: true,
47
- stdio: ['ignore', 'pipe', 'pipe'], // explicit: ignore stdin so headless PM2 doesn't hang
62
+ // Use pipe for stdin when we have text to send; otherwise ignore to prevent PM2 hang
63
+ stdio: [stdinText != null ? 'pipe' : 'ignore', 'pipe', 'pipe'],
48
64
  })
65
+ if (stdinText != null) {
66
+ proc.stdin!.end(stdinText, 'utf8')
67
+ }
49
68
  proc.stdout.on('data', (d: Buffer) => { stdout += d.toString() })
50
69
  proc.stderr.on('data', (d: Buffer) => {
51
70
  const s = d.toString().trim()
@@ -101,9 +120,11 @@ export async function askCodex(
101
120
  onNewThreadId?: (id: string) => void
102
121
  ): Promise<{ text: string; usage?: TokenUsage }> {
103
122
  try {
104
- // Always start fresh codex exec resume <id> <message> rejects the message as unexpected arg
105
- const args = ['exec', '--dangerously-bypass-approvals-and-sandbox', '--skip-git-repo-check', '--json', userMessage]
106
- const { text, threadId, usage } = await spawnCodex(args)
123
+ // Build full prompt with conversation history, send via stdin (avoids Windows arg escaping issues)
124
+ const prompt = buildPrompt(session.history, userMessage)
125
+ // `-` tells codex exec to read PROMPT from stdin
126
+ const args = ['exec', '--dangerously-bypass-approvals-and-sandbox', '--skip-git-repo-check', '--json', '-']
127
+ const { text, threadId, usage } = await spawnCodex(args, prompt)
107
128
  if (threadId && !session.codexThreadId) onNewThreadId?.(threadId)
108
129
  return { text, usage }
109
130
  } catch (err: any) {