cliclaw 1.0.33 → 1.0.35
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 +18 -15
- package/package.json +1 -1
- package/src/agents/codex.ts +10 -10
- package/src/handlers/approvals.ts +8 -8
- package/src/handlers/messages.ts +11 -5
package/index.ts
CHANGED
|
@@ -25,37 +25,40 @@ registerCommands(bot, storage, config)
|
|
|
25
25
|
registerMessageHandler(bot, storage, config)
|
|
26
26
|
|
|
27
27
|
// ── Codex exec approval callbacks ──────────────────────────────────────────
|
|
28
|
+
const APPROVAL_PREFIXES: Record<string, { decision: string; label: string }> = {
|
|
29
|
+
capprove: { decision: 'approved', label: '✅ Aprovado' },
|
|
30
|
+
csession: { decision: 'approved_for_session', label: '🔁 Sessão' },
|
|
31
|
+
cdeny: { decision: 'denied', label: '❌ Negado' },
|
|
32
|
+
cabort: { decision: 'abort', label: '🛑 Abortado' },
|
|
33
|
+
}
|
|
34
|
+
|
|
28
35
|
bot.on('callback_query:data', async (ctx) => {
|
|
29
36
|
const data = ctx.callbackQuery.data
|
|
30
37
|
console.log(`[callback] recebido: ${data.slice(0, 60)}`)
|
|
31
38
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const colonIdx = data.indexOf(':')
|
|
35
|
-
const prefix = data.slice(0, colonIdx)
|
|
39
|
+
const colonIdx = data.indexOf(':')
|
|
40
|
+
const prefix = data.slice(0, colonIdx)
|
|
36
41
|
const approvalId = data.slice(colonIdx + 1)
|
|
37
|
-
const
|
|
42
|
+
const entry = APPROVAL_PREFIXES[prefix]
|
|
43
|
+
if (!entry) return
|
|
38
44
|
|
|
39
|
-
console.log(`[callback] clicou ${
|
|
45
|
+
console.log(`[callback] clicou ${prefix} id=${approvalId.slice(0, 8)}...`)
|
|
40
46
|
|
|
41
|
-
const found = respondApproval(approvalId,
|
|
42
|
-
console.log(`[callback] respondApproval found=${found}`)
|
|
47
|
+
const found = respondApproval(approvalId, entry.decision)
|
|
48
|
+
console.log(`[callback] respondApproval found=${found} decision=${entry.decision}`)
|
|
43
49
|
|
|
44
|
-
const label = approved ? '✅ Aprovado' : '❌ Negado'
|
|
45
50
|
try {
|
|
46
|
-
await ctx.answerCallbackQuery({ text: label })
|
|
51
|
+
await ctx.answerCallbackQuery({ text: entry.label })
|
|
47
52
|
} catch (e: any) {
|
|
48
53
|
console.error(`[callback] answerCallbackQuery falhou: ${e.message}`)
|
|
49
54
|
}
|
|
50
55
|
try {
|
|
51
56
|
if (found) {
|
|
52
|
-
// Get the command text from the original message caption (plain text)
|
|
53
57
|
const origText = ctx.callbackQuery.message?.text ?? ''
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
const escaped = cmdLine.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
58
|
+
const cmdLine = origText.split('\n').slice(1).join('\n').trim()
|
|
59
|
+
const escaped = cmdLine.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
57
60
|
await ctx.editMessageText(
|
|
58
|
-
`🔧 <b>Codex quer executar:</b>\n<code>${escaped}</code>\n\n${label}`,
|
|
61
|
+
`🔧 <b>Codex quer executar:</b>\n<code>${escaped}</code>\n\n${entry.label}`,
|
|
59
62
|
{ parse_mode: 'HTML' }
|
|
60
63
|
)
|
|
61
64
|
}
|
package/package.json
CHANGED
package/src/agents/codex.ts
CHANGED
|
@@ -31,7 +31,7 @@ interface ProtoSession {
|
|
|
31
31
|
lastUsed: number
|
|
32
32
|
pending: Map<string, PendingReq>
|
|
33
33
|
buf: string // incomplete stdout line buffer
|
|
34
|
-
approvalHandler?: (callId: string, commandStr: string) => Promise<
|
|
34
|
+
approvalHandler?: (callId: string, commandStr: string) => Promise<string>
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// Map from cliclaw session.id → ProtoSession
|
|
@@ -126,15 +126,15 @@ function handleProtoEvent(ps: ProtoSession, obj: any) {
|
|
|
126
126
|
|
|
127
127
|
// ── exec approval request ───────────────────────────────────────────────────
|
|
128
128
|
if (msg?.type === 'exec_approval_request') {
|
|
129
|
-
const
|
|
129
|
+
const subId = msg.sub_id ?? msg.call_id ?? msgId
|
|
130
130
|
const commandStr = formatApprovalCommand(msg.command ?? msg.cmd)
|
|
131
|
-
console.log(`[Codex approval] request recebido
|
|
132
|
-
const respond = (
|
|
133
|
-
console.log(`[Codex approval] enviando exec_approval
|
|
131
|
+
console.log(`[Codex approval] request recebido sub_id=${subId} cmd="${commandStr.slice(0, 80)}"`)
|
|
132
|
+
const respond = (decision: string) => {
|
|
133
|
+
console.log(`[Codex approval] enviando exec_approval sub_id=${subId} decision=${decision}`)
|
|
134
134
|
try {
|
|
135
135
|
ps.proc.stdin!.write(JSON.stringify({
|
|
136
136
|
id: randomUUID(),
|
|
137
|
-
op: { type: 'exec_approval',
|
|
137
|
+
op: { type: 'exec_approval', sub_id: subId, decision },
|
|
138
138
|
}) + '\n')
|
|
139
139
|
console.log(`[Codex approval] enviado ok`)
|
|
140
140
|
} catch (e: any) {
|
|
@@ -143,15 +143,15 @@ function handleProtoEvent(ps: ProtoSession, obj: any) {
|
|
|
143
143
|
}
|
|
144
144
|
if (ps.approvalHandler) {
|
|
145
145
|
console.log(`[Codex approval] aguardando usuário via Telegram...`)
|
|
146
|
-
ps.approvalHandler(
|
|
146
|
+
ps.approvalHandler(subId, commandStr)
|
|
147
147
|
.then(respond)
|
|
148
148
|
.catch((e: any) => {
|
|
149
149
|
console.error(`[Codex approval] handler error: ${e?.message} — negando`)
|
|
150
|
-
respond(
|
|
150
|
+
respond('denied')
|
|
151
151
|
})
|
|
152
152
|
} else {
|
|
153
153
|
console.log(`[Codex approval] sem handler — auto-aprovando`)
|
|
154
|
-
respond(
|
|
154
|
+
respond('approved')
|
|
155
155
|
}
|
|
156
156
|
return
|
|
157
157
|
}
|
|
@@ -205,7 +205,7 @@ export async function askCodex(
|
|
|
205
205
|
session: { id: string; codexThreadId?: string },
|
|
206
206
|
userMessage: string,
|
|
207
207
|
_onNewThreadId?: (id: string) => void,
|
|
208
|
-
approvalHandler?: (callId: string, commandStr: string) => Promise<
|
|
208
|
+
approvalHandler?: (callId: string, commandStr: string) => Promise<string>
|
|
209
209
|
): Promise<{ text: string; usage?: TokenUsage }> {
|
|
210
210
|
const ps = getOrStart(session.id)
|
|
211
211
|
// Update handler every call (chat context may have changed)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared state for pending Codex exec approval requests.
|
|
3
3
|
* When codex wants to run a shell command, the bot sends a Telegram message
|
|
4
|
-
* with Approve/Deny buttons. The user's answer is routed back here.
|
|
4
|
+
* with Approve/Deny/Session/Abort buttons. The user's answer is routed back here.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
interface PendingApproval {
|
|
8
|
-
resolve: (
|
|
8
|
+
resolve: (decision: string) => void
|
|
9
9
|
timeout: ReturnType<typeof setTimeout>
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -14,25 +14,25 @@ const pending = new Map<string, PendingApproval>()
|
|
|
14
14
|
/** Store a pending approval that will resolve when the user responds. */
|
|
15
15
|
export function registerApproval(
|
|
16
16
|
id: string,
|
|
17
|
-
resolve: (
|
|
17
|
+
resolve: (decision: string) => void,
|
|
18
18
|
ttlMs = 5 * 60 * 1000 // auto-deny after 5 min
|
|
19
19
|
): void {
|
|
20
20
|
console.log(`[approvals] registrado id=${id.slice(0, 8)}... total pendentes=${pending.size + 1}`)
|
|
21
21
|
const timeout = setTimeout(() => {
|
|
22
22
|
console.log(`[approvals] timeout id=${id.slice(0, 8)}... auto-negando`)
|
|
23
|
-
respondApproval(id,
|
|
23
|
+
respondApproval(id, 'denied')
|
|
24
24
|
}, ttlMs)
|
|
25
25
|
pending.set(id, { resolve, timeout })
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
/** Called by the callback-query handler when the user clicks
|
|
29
|
-
export function respondApproval(id: string,
|
|
28
|
+
/** Called by the callback-query handler when the user clicks a decision button. */
|
|
29
|
+
export function respondApproval(id: string, decision: string): boolean {
|
|
30
30
|
const found = pending.has(id)
|
|
31
|
-
console.log(`[approvals] respondApproval id=${id.slice(0, 8)}...
|
|
31
|
+
console.log(`[approvals] respondApproval id=${id.slice(0, 8)}... decision=${decision} found=${found} total=${pending.size}`)
|
|
32
32
|
const p = pending.get(id)
|
|
33
33
|
if (!p) return false
|
|
34
34
|
clearTimeout(p.timeout)
|
|
35
35
|
pending.delete(id)
|
|
36
|
-
p.resolve(
|
|
36
|
+
p.resolve(decision)
|
|
37
37
|
return true
|
|
38
38
|
}
|
package/src/handlers/messages.ts
CHANGED
|
@@ -113,13 +113,19 @@ async function processMessage(ctx: Context, storage: Storage, config: Config) {
|
|
|
113
113
|
{
|
|
114
114
|
...replyOpts,
|
|
115
115
|
parse_mode: 'HTML',
|
|
116
|
-
reply_markup: { inline_keyboard: [
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
reply_markup: { inline_keyboard: [
|
|
117
|
+
[
|
|
118
|
+
{ text: '✅ Aprovar', callback_data: `capprove:${approvalId}` },
|
|
119
|
+
{ text: '🔁 Sessão', callback_data: `csession:${approvalId}` },
|
|
120
|
+
],
|
|
121
|
+
[
|
|
122
|
+
{ text: '❌ Negar', callback_data: `cdeny:${approvalId}` },
|
|
123
|
+
{ text: '🛑 Abortar', callback_data: `cabort:${approvalId}` },
|
|
124
|
+
],
|
|
125
|
+
] },
|
|
120
126
|
}
|
|
121
127
|
).catch(() => {})
|
|
122
|
-
return new Promise<
|
|
128
|
+
return new Promise<string>((resolve) => registerApproval(approvalId, resolve))
|
|
123
129
|
}
|
|
124
130
|
: undefined
|
|
125
131
|
|