clisbot 0.1.45-beta.10 → 0.1.45-beta.12
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 +159 -36
- package/dist/main.js +67 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/brand/x-profile-banner-2026-04-29/images/clisbot-x-banner-v5-frontier-tagline-1500x500.png" alt="clisbot banner" width="100%" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="./README.md">English</a> |
|
|
7
|
+
<a href="./docs/langs/root/README.vi.md">Tiếng Việt</a> |
|
|
8
|
+
<a href="./docs/langs/root/README.zh-CN.md">简体中文</a> |
|
|
9
|
+
<a href="./docs/langs/root/README.ko.md">한국어</a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://www.npmjs.com/package/clisbot"><img src="https://img.shields.io/npm/v/clisbot?label=npm&color=cb3837" alt="npm version" /></a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/clisbot"><img src="https://img.shields.io/npm/dm/clisbot?label=downloads&color=22c55e" alt="npm downloads per month" /></a>
|
|
15
|
+
<a href="./LICENSE"><img src="https://img.shields.io/badge/License-MIT-d4a017" alt="MIT license" /></a>
|
|
16
|
+
<img src="https://img.shields.io/badge/CLI-Codex%20%7C%20Claude%20%7C%20Gemini-111827" alt="supported cli tools" />
|
|
17
|
+
<img src="https://img.shields.io/badge/Channels-Slack%20%7C%20Telegram-0a66c2" alt="supported channels" />
|
|
18
|
+
<img src="https://img.shields.io/badge/Runtime-tmux%20backed-16a34a" alt="tmux backed runtime" />
|
|
19
|
+
<img src="https://img.shields.io/badge/Workflow-AI--native-f59e0b" alt="AI-native workflow" />
|
|
20
|
+
</p>
|
|
21
|
+
|
|
1
22
|
# clisbot - Turn your favorite coding CLI into an agentic personal assistant, workplace assistant, coding partner - on the go
|
|
2
23
|
Want to use OpenClaw but are struggling because:
|
|
3
24
|
|
|
@@ -13,6 +34,40 @@ It is not just a tmux bridge with chat glued on top. `clisbot` treats Slack and
|
|
|
13
34
|
|
|
14
35
|
`clisbot` is also meant to grow into a reusable agent runtime layer that can support many CLI tools, many channels, and many workflow shapes on top of the same durable agent session.
|
|
15
36
|
|
|
37
|
+
## Start Here By Goal
|
|
38
|
+
|
|
39
|
+
### I Want A Personal Coding Bot In Telegram Or Slack
|
|
40
|
+
|
|
41
|
+
- start with the [Quick Start](#quick-start)
|
|
42
|
+
- best fit when you want Codex, Claude, or Gemini available from chat without
|
|
43
|
+
giving up a real workspace
|
|
44
|
+
- current release value: a much more AI-native control path, where the bot can
|
|
45
|
+
increasingly set up `/queue`, loops, schedules, and other recurring work for
|
|
46
|
+
you from normal chat instead of forcing you to memorize command syntax first
|
|
47
|
+
|
|
48
|
+
### I Want A Shared Team Bot
|
|
49
|
+
|
|
50
|
+
- start with [Quick Start](#quick-start), then read [Surface Access Model](#surface-access-model)
|
|
51
|
+
- best fit when you need one bot in a real Slack channel, Telegram group, or
|
|
52
|
+
Telegram topic with explicit route and sender control
|
|
53
|
+
- current release value: safer shared-surface policy, tighter topic or thread
|
|
54
|
+
isolation, per-group sender control, and permission boundaries that let one
|
|
55
|
+
bot live in a team group without opening it to everyone there
|
|
56
|
+
|
|
57
|
+
### I Need Operator Control And Debugging
|
|
58
|
+
|
|
59
|
+
- start with [Common CLI commands](#common-cli-commands)
|
|
60
|
+
- most useful surfaces: `clisbot status`, `clisbot logs`,
|
|
61
|
+
`clisbot watch --latest`, `clisbot inspect --latest`, and `clisbot queues`
|
|
62
|
+
- current release value: more truthful `sessionId`, lighter runner inventory,
|
|
63
|
+
and less confusing restart behavior during updates
|
|
64
|
+
|
|
65
|
+
### I Just Want To Know What Changed Recently
|
|
66
|
+
|
|
67
|
+
- start with [Recent Release Highlights](#recent-release-highlights)
|
|
68
|
+
- then read [v0.1.45 Release Notes](docs/releases/v0.1.45.md) or the
|
|
69
|
+
[v0.1.45 Release Guide](docs/updates/releases/v0.1.45-release-guide.md)
|
|
70
|
+
|
|
16
71
|
## Why I Built This
|
|
17
72
|
|
|
18
73
|
I’m Long Luong (Long), Co-founder & CTO of Vexere, Vietnam’s #1 transportation booking platform, where we also build SaaS and inventory distribution infrastructure for transportation operators. As we scale a 300-person company with a 100-member Engineering, Product, and Design team, I’ve been searching for the most practical way to roll out AI-native workflows across the organization.
|
|
@@ -28,17 +83,20 @@ The challenge is not whether AI is useful. It is how to make it work at enterpri
|
|
|
28
83
|
- Learns from and integrates the two biggest strengths that made OpenClaw popular: memory and native channel integration with deep, channel-specific conversation and presentation capabilities.
|
|
29
84
|
- Not just a tmux bridge. Slack and Telegram are treated as real channel surfaces with routing, thread or topic continuity, pairing, follow-up control, and attachment-aware interaction instead of plain text passthrough so you can work from your laptop or on the go without giving up a real coding workspace.
|
|
30
85
|
- Team-first by design, with `AGENTS`, `USER`, and `MEMORY` context bootstrapping shaped for shared team reality instead of only personal solo-assistant flows.
|
|
31
|
-
-
|
|
86
|
+
- Shared-surface permission control is a first-class feature: a bot can be in a team group but still answer only the specific people you allow there, while sensitive control actions stay behind explicit auth roles and permissions.
|
|
87
|
+
- Useful for coding, operations, teamwork, and general assistant work, with fast chat controls such as `!<command>`, `/bash <command>`, `/queue`, `/loop`, `/streaming`, and `/mention`.
|
|
88
|
+
- New in `v0.1.45`: the AI-native control experience is much better. You can increasingly ask the bot in normal chat to update itself and explain what changed, help with onboarding, add or configure bots and agents, or create recurring schedules and loops for you instead of relying only on slash commands.
|
|
32
89
|
|
|
33
|
-
##
|
|
90
|
+
## Who This Fits Best
|
|
34
91
|
|
|
35
|
-
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
-
|
|
92
|
+
- Anyone who wants a high-agency personal assistant with OpenClaw-style memory,
|
|
93
|
+
workspace context, and a skill-oriented operating model that can do far more
|
|
94
|
+
than a thin chat wrapper.
|
|
95
|
+
- Solo builders who want a real coding assistant in Telegram or Slack, backed
|
|
96
|
+
by Codex, Claude, or Gemini, without rebuilding their workflow around a new
|
|
97
|
+
web product.
|
|
98
|
+
- Team leads who want one shared bot with explicit group or topic safety,
|
|
99
|
+
durable context, and attachment-aware chat workflows.
|
|
42
100
|
|
|
43
101
|
## Surface Access Model
|
|
44
102
|
|
|
@@ -114,23 +172,44 @@ clisbot start \
|
|
|
114
172
|
```
|
|
115
173
|
|
|
116
174
|
If you want to try first without persisting the token yet, just remove `--persist`.
|
|
175
|
+
Day-to-day rescue commands are `clisbot stop`, `clisbot restart`,
|
|
176
|
+
`clisbot status`, and `clisbot logs`.
|
|
117
177
|
|
|
118
178
|
Next steps:
|
|
119
179
|
|
|
120
180
|
- For security, DMs default to pairing.
|
|
181
|
+
- `clisbot` also has a smart autopairing path to reduce first-run friction. If
|
|
182
|
+
you send the bot a DM within the first 30 minutes, you can usually claim the
|
|
183
|
+
owner role immediately and start using it without a separate pairing round.
|
|
184
|
+
- New from `v0.1.45`: the AI-native operator experience is much stronger. You
|
|
185
|
+
can increasingly ask the bot through chat to explain how to use it, update
|
|
186
|
+
itself and summarize what's new, help onboard you, create or add a new bot or
|
|
187
|
+
agent, or set up loops and schedules for recurring work instead of relying
|
|
188
|
+
only on slash commands.
|
|
121
189
|
- Existing configs from any version before `0.1.45` update directly to `0.1.45` automatically on first run. clisbot writes a backup first under `~/.clisbot/backups/`, then rewrites the config to the current shape.
|
|
122
190
|
- Shared Slack channels, Slack groups, Telegram groups, and Telegram topics are a separate gate: normal users need an explicit route such as `group:<id>` or `topic:<chatId>:<topicId>` before the bot will talk there. Legacy Slack `channel:<id>` input still works for compatibility.
|
|
123
191
|
- After a shared surface is admitted, per-surface sender control comes from the bot's default shared rule `groups["*"]` plus any route-local `allowUsers` or `blockUsers`.
|
|
192
|
+
- With that permission model, a bot can be added to a team group but still be
|
|
193
|
+
allowed to answer only some people in that group.
|
|
124
194
|
- If the effective shared policy is `disabled`, the bot stays silent there for everyone, including owner/admin.
|
|
125
195
|
- If the effective shared policy is `allowlist` and a sender is not allowed, the bot denies before the runner:
|
|
126
196
|
- `You are not allowed to use this bot in this group. Ask a bot owner or admin to add you to \`allowUsers\` for this surface.`
|
|
127
|
-
- However, `clisbot` has smart autopairing feature to help you get started frictionless. Just send direct message to your bot (through telegram or slack) within 30 minutes so you can claim owner role automatically, and use the bot right away without pairing. After this 30 minutes window you need to approve pairing following instructions by the bot in direct message.
|
|
128
197
|
- To chat with the bot in a group:
|
|
129
|
-
- telegram: Add bot to group, then use
|
|
130
|
-
|
|
198
|
+
- telegram: Add the bot to the group, then use `/start` there. It will guide
|
|
199
|
+
you toward the route you need to add. You can run that command directly or
|
|
200
|
+
copy it into a DM with the bot and ask it to do the setup for you if you
|
|
201
|
+
are already authorized.
|
|
202
|
+
- slack: similar flow, but Slack-native slash command handling is awkward.
|
|
203
|
+
Use a leading space such as ` /start`, or use the alias `\start`. The same
|
|
204
|
+
workaround applies to other slash commands such as ` /streaming on` or
|
|
205
|
+
`\mention`.
|
|
206
|
+
- group conversations require a mention by default to avoid abuse, but smart
|
|
207
|
+
follow-up stays open for a short window so you do not need to tag the bot
|
|
208
|
+
again on every reply. You can also ask the bot to change that mode for you.
|
|
131
209
|
- If you want stricter mention behavior, use `/mention` for this conversation only, `/mention channel` for the current channel or group default, or `/mention all` for the current bot default.
|
|
132
|
-
- For long running
|
|
133
|
-
|
|
210
|
+
- For long running tasks such as coding, turn streaming on with `/streaming on`
|
|
211
|
+
and check it with `/streaming status`. In Slack, use a leading space such
|
|
212
|
+
as ` /streaming on` or the alias `\streaming on`.
|
|
134
213
|
- If you want to add more owner or app admin, grant that principal explicitly with the platform prefix plus the channel-native user id, for example `clisbot auth add-user app --role owner --user telegram:1276408333` or `clisbot auth add-user app --role admin --user slack:U123ABC456`.
|
|
135
214
|
- `clisbot auth --help` now covers role scopes, permission sets, and add/remove flows for users and permissions.
|
|
136
215
|
- App-level auth and owner-claim semantics in [Authorization And Roles](docs/user-guide/auth-and-roles.md) describe both the current runtime reality and the remaining target-model gaps.
|
|
@@ -154,11 +233,32 @@ What happens next:
|
|
|
154
233
|
|
|
155
234
|
## Recent Release Highlights
|
|
156
235
|
|
|
157
|
-
- `v0.1.45`:
|
|
236
|
+
- `v0.1.45`: a much more AI-native operator experience, where you can
|
|
237
|
+
increasingly talk to the bot to manage itself; plus safer personal and team
|
|
238
|
+
bots in real Slack and Telegram groups, automatic direct updates from older
|
|
239
|
+
installs, durable queue control, clearer session continuity truth, more
|
|
240
|
+
reliable scheduled loops, stronger trust/restart behavior, and stricter
|
|
241
|
+
streaming/session isolation.
|
|
158
242
|
- `v0.1.43`: more durable runtime recovery, clearer routed follow-up controls, more truthful tmux prompt submission checks, better queued-start notifications, and safer Slack thread attachment behavior.
|
|
159
|
-
- `v0.1.39`: the first large release of the current bot-first shape, with native Slack and Telegram rendering, cleaner first-run setup, stronger pairing/auth defaults, better long-running run visibility, and recurring `/loop` automation.
|
|
160
243
|
|
|
161
|
-
|
|
244
|
+
What `v0.1.45` most likely means for you:
|
|
245
|
+
|
|
246
|
+
- The headline is AI-native control: ask the bot in chat to queue work,
|
|
247
|
+
schedule recurring briefs, help update itself, explain release changes, or
|
|
248
|
+
guide setup and routing instead of dropping to the shell for every action.
|
|
249
|
+
- personal user: fewer fragile long-run failures, better `/queue`, better media
|
|
250
|
+
handling on Telegram
|
|
251
|
+
- shared bot owner: clearer route safety, easier direct upgrade from older
|
|
252
|
+
installs, and more interesting team use cases where one bot lives in the
|
|
253
|
+
group but only responds to selected people there
|
|
254
|
+
- operator: better queue visibility, better session continuity truth, and
|
|
255
|
+
restart behavior that is less misleading during updates, plus faster
|
|
256
|
+
`watch` and `inspect` shortcuts when something goes wrong
|
|
257
|
+
|
|
258
|
+
There are many more useful fixes and operator improvements in the full release
|
|
259
|
+
notes, including config update safety, CLI help, setup docs, runner debugging,
|
|
260
|
+
route policy behavior, channel-specific polish, and the broader AI-native
|
|
261
|
+
workflow direction behind this release.
|
|
162
262
|
|
|
163
263
|
Read the full notes here:
|
|
164
264
|
|
|
@@ -195,14 +295,19 @@ Repo-local `bun run start|stop|restart|status|logs|init|pairing` is pinned by `.
|
|
|
195
295
|
|
|
196
296
|
Update note for existing installs:
|
|
197
297
|
|
|
198
|
-
- `v0.1.
|
|
199
|
-
|
|
200
|
-
|
|
298
|
+
- Older installs before `v0.1.45` now update directly on first run with a
|
|
299
|
+
backup written first, so most people can update and restart without a manual
|
|
300
|
+
migration pass.
|
|
301
|
+
- After you are on `v0.1.45`, future upgrades should feel much more AI-native:
|
|
302
|
+
in many cases you can simply ask the bot to update `clisbot` to the latest
|
|
303
|
+
version, and it can follow the update guide, perform the upgrade flow, then
|
|
304
|
+
brief you on what changed.
|
|
305
|
+
- If you still want an agent to inspect your current config before updating,
|
|
306
|
+
ask Codex or Claude in this repo to review it first.
|
|
307
|
+
- The manual package upgrade path is now simpler:
|
|
201
308
|
|
|
202
309
|
```bash
|
|
203
|
-
clisbot
|
|
204
|
-
npm install -g clisbot
|
|
205
|
-
clisbot start
|
|
310
|
+
npm install -g clisbot && clisbot restart
|
|
206
311
|
clisbot --version
|
|
207
312
|
```
|
|
208
313
|
|
|
@@ -264,6 +369,12 @@ If you want the repo-guided setup path:
|
|
|
264
369
|
3. Ask it to help you set up `clisbot`.
|
|
265
370
|
|
|
266
371
|
The docs in this repo are kept current, including the [User Guide](docs/user-guide/README.md), so the agent should have enough context to walk you through setup, configuration, and troubleshooting directly inside the repo.
|
|
372
|
+
If anything goes wrong, the fastest rescue loop is usually `clisbot logs`,
|
|
373
|
+
`clisbot status`, `clisbot restart`, or if needed `clisbot stop --hard`
|
|
374
|
+
followed by `clisbot start`.
|
|
375
|
+
Also open the coding CLI directly inside the bot workspace, usually
|
|
376
|
+
`~/.clisbot/workspaces/default`, and make sure that CLI already works there.
|
|
377
|
+
That is one of the strongest end-to-end checks for bot health.
|
|
267
378
|
|
|
268
379
|
If you prefer to configure everything yourself:
|
|
269
380
|
|
|
@@ -307,26 +418,28 @@ clisbot start \
|
|
|
307
418
|
If the quick start does not work, check these in order:
|
|
308
419
|
|
|
309
420
|
- If setup feels unclear, open Claude Code, Codex, or Gemini CLI in this repo and ask it to help using the local docs.
|
|
421
|
+
- If anything looks wrong, start with `clisbot logs`, `clisbot status`,
|
|
422
|
+
`clisbot restart`, or if needed `clisbot stop --hard` followed by
|
|
423
|
+
`clisbot start`.
|
|
310
424
|
- If config behavior is confusing, inspect [config/clisbot.json.template](config/clisbot.json.template) first, then compare it with [docs/user-guide/README.md](docs/user-guide/README.md).
|
|
311
425
|
- If `clisbot start` says no agents are configured, prefer `clisbot start --cli codex --bot-type personal --telegram-bot-token <your-telegram-bot-token>`.
|
|
312
|
-
- If you want later runs to work with plain `clisbot start`, rerun your successful first-run command with `--persist`.
|
|
313
426
|
- If `clisbot start` prints token refs as `missing`, either pass the token explicitly on the command line or switch to env-backed setup described in [docs/user-guide/bots-and-credentials.md](docs/user-guide/bots-and-credentials.md).
|
|
314
|
-
- If you use custom env names, pass them explicitly with `--slack-app-token`, `--slack-bot-token`, or `--telegram-bot-token`.
|
|
315
427
|
- If `clisbot status` shows `bootstrap=...:missing` or `bootstrap=...:not-bootstrapped`, follow the advanced agent bootstrap steps in [docs/user-guide/README.md](docs/user-guide/README.md).
|
|
316
|
-
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
- If that trust screen is still blocking, inspect the live session name with `clisbot runner list`, then attach directly with `tmux -S ~/.clisbot/state/clisbot.sock attach -t <session-name>`.
|
|
428
|
+
- Trust is usually handled automatically by the bot, but if trust or startup
|
|
429
|
+
behavior still looks wrong, go to the workspace and launch the underlying CLI
|
|
430
|
+
there directly, for example `cd ~/.clisbot/workspaces/default` and start
|
|
431
|
+
`codex`, `claude`, or `gemini` yourself. If the CLI cannot start cleanly in
|
|
432
|
+
that workspace, the bot will not be healthy either.
|
|
324
433
|
- If Gemini startup says it is waiting for manual authorization, authenticate Gemini directly first or provide a headless auth path such as `GEMINI_API_KEY` or Vertex AI credentials; `clisbot` now treats that screen as a startup blocker instead of a healthy ready session.
|
|
325
434
|
- If Codex warns that `bubblewrap` is missing on Linux, install `bubblewrap` in the runtime environment.
|
|
326
435
|
- If the bot does not answer, check `clisbot status` first. Healthy channels should show `connection=active`; if a channel stays `starting`, inspect `clisbot logs`.
|
|
327
|
-
- If a routed message was accepted but no reply arrives, send one test message
|
|
436
|
+
- If a routed message was accepted but no reply arrives, send one test message
|
|
437
|
+
and immediately run `clisbot watch --latest --lines 100` in a terminal. This
|
|
438
|
+
shows the live tmux runner pane and usually reveals missing CLI auth, trust
|
|
439
|
+
prompts, stuck startup, or model/provider errors.
|
|
328
440
|
- If Codex works in your normal terminal but the routed runner shows `Missing environment variable: CODEX_CLIPROXYAPI_KEY`, remember that `clisbot` runs Codex from a detached background process and tmux session. Start or restart `clisbot` from a shell where `echo $CODEX_CLIPROXYAPI_KEY` prints a value, or export the key in the environment used by your service manager. Existing tmux runner sessions keep their old environment, so recycle them after fixing env.
|
|
329
441
|
- If runtime startup still fails, run `clisbot logs` and inspect the recent log tail that `clisbot` now prints automatically on startup failure.
|
|
442
|
+
- If `clisbot restart` warns that stop timed out during an update, run `clisbot status` once. Current releases should continue cleanly when status already shows the worker exited; only treat it as a real bug if restart leaves the runtime down.
|
|
330
443
|
- If a normal restart is not enough, use `clisbot stop --hard` to stop the runtime and kill all tmux runner sessions on the configured clisbot socket, then start again from a shell with the correct environment.
|
|
331
444
|
- If you need the full command list, run `clisbot --help`.
|
|
332
445
|
- If you need step-by-step operator docs, start with [docs/user-guide/README.md](docs/user-guide/README.md).
|
|
@@ -344,8 +457,12 @@ Most users only need a small set of commands at first:
|
|
|
344
457
|
- `clisbot status`: check whether the runtime, channels, and active sessions look healthy.
|
|
345
458
|
- `clisbot logs`: inspect recent runtime logs when startup, routing, or replies look wrong.
|
|
346
459
|
- `clisbot runner list`: list the live tmux-backed runner sessions and see what is active.
|
|
347
|
-
- `clisbot
|
|
348
|
-
|
|
460
|
+
- `clisbot inspect --latest`: capture the current pane state of the newest
|
|
461
|
+
admitted session once.
|
|
462
|
+
- `clisbot watch --latest --lines 100`: jump straight into the newest admitted
|
|
463
|
+
live session with enough context to debug a just-submitted message.
|
|
464
|
+
- `clisbot watch --index 2`: follow the second most recent admitted session
|
|
465
|
+
without needing to copy a tmux session name first.
|
|
349
466
|
- `clisbot queues list`: inspect pending durable queued prompts across the app.
|
|
350
467
|
- `clisbot queues create --channel telegram --target group:-1001234567890 --topic-id 4335 --sender telegram:1276408333 <prompt>`: create one durable same-session queued prompt, capped by `control.queue.maxPendingItemsPerSession` (default `20`).
|
|
351
468
|
|
|
@@ -406,6 +523,10 @@ Detailed slash-command guide:
|
|
|
406
523
|
|
|
407
524
|
## Docs
|
|
408
525
|
|
|
526
|
+
- [Localized Docs Hub](docs/langs/README.md)
|
|
527
|
+
- [Vietnamese Repo README](docs/langs/root/README.vi.md)
|
|
528
|
+
- [Simplified Chinese Repo README](docs/langs/root/README.zh-CN.md)
|
|
529
|
+
- [Korean Repo README](docs/langs/root/README.ko.md)
|
|
409
530
|
- [Overview](docs/overview/README.md)
|
|
410
531
|
- [Architecture](docs/architecture/README.md)
|
|
411
532
|
- [Development Guide](docs/development/README.md)
|
|
@@ -429,6 +550,8 @@ Detailed slash-command guide:
|
|
|
429
550
|
- more communication channels beyond Slack and Telegram
|
|
430
551
|
- simple workflow building blocks such as cron jobs, heartbeat jobs, and loops
|
|
431
552
|
- durable agent sessions, workspaces, follow-up policy, commands, attachments, and operator controls that stay reusable across all those surfaces
|
|
553
|
+
- stability and security stay at the top of the project focus; if you find an
|
|
554
|
+
issue in either area, please report it
|
|
432
555
|
|
|
433
556
|
tmux is still the current stability boundary. One agent maps to one durable runner session in one workspace, and every CLI, channel, or workflow layer should route onto that durable runtime instead of recreating the agent from scratch.
|
|
434
557
|
|
package/dist/main.js
CHANGED
|
@@ -69100,6 +69100,14 @@ class TmuxSubmitUnconfirmedError extends Error {
|
|
|
69100
69100
|
}
|
|
69101
69101
|
}
|
|
69102
69102
|
async function submitTmuxSessionInput(params) {
|
|
69103
|
+
if (params.trustPrompt) {
|
|
69104
|
+
await acceptTmuxTrustPromptIfPresent({
|
|
69105
|
+
tmux: params.tmux,
|
|
69106
|
+
sessionName: params.sessionName,
|
|
69107
|
+
captureLines: params.trustPrompt.captureLines,
|
|
69108
|
+
startupDelayMs: params.trustPrompt.startupDelayMs
|
|
69109
|
+
});
|
|
69110
|
+
}
|
|
69103
69111
|
const prePasteState = await params.tmux.getPaneState(params.sessionName);
|
|
69104
69112
|
const captureLines = estimatePasteCaptureLines(params.text);
|
|
69105
69113
|
const prePasteSnapshot = normalizePaneText(await params.tmux.capturePane(params.sessionName, captureLines));
|
|
@@ -69153,6 +69161,12 @@ async function submitTmuxSessionInput(params) {
|
|
|
69153
69161
|
throw new TmuxSubmitUnconfirmedError;
|
|
69154
69162
|
}
|
|
69155
69163
|
async function captureTmuxSessionIdentity(params) {
|
|
69164
|
+
await acceptTmuxTrustPromptIfPresent({
|
|
69165
|
+
tmux: params.tmux,
|
|
69166
|
+
sessionName: params.sessionName,
|
|
69167
|
+
captureLines: params.captureLines,
|
|
69168
|
+
startupDelayMs: params.timeoutMs
|
|
69169
|
+
});
|
|
69156
69170
|
let statusSubmission = await submitTmuxSessionInput({
|
|
69157
69171
|
tmux: params.tmux,
|
|
69158
69172
|
sessionName: params.sessionName,
|
|
@@ -69176,7 +69190,7 @@ async function captureTmuxSessionIdentity(params) {
|
|
|
69176
69190
|
throw error;
|
|
69177
69191
|
}
|
|
69178
69192
|
if (tmuxPaneHasTrustPrompt(snapshot)) {
|
|
69179
|
-
await
|
|
69193
|
+
await acceptTrustPrompt({
|
|
69180
69194
|
tmux: params.tmux,
|
|
69181
69195
|
sessionName: params.sessionName,
|
|
69182
69196
|
captureLines: params.captureLines
|
|
@@ -69233,7 +69247,7 @@ function extractSessionIdFromCaptureCandidates(candidates, pattern) {
|
|
|
69233
69247
|
}
|
|
69234
69248
|
return null;
|
|
69235
69249
|
}
|
|
69236
|
-
async function
|
|
69250
|
+
async function acceptTmuxTrustPromptIfPresent(params) {
|
|
69237
69251
|
const deadline = Date.now() + Math.max(TRUST_PROMPT_MAX_WAIT_MS, params.startupDelayMs);
|
|
69238
69252
|
while (Date.now() <= deadline) {
|
|
69239
69253
|
let snapshot = "";
|
|
@@ -69256,7 +69270,7 @@ async function dismissTmuxTrustPromptIfPresent(params) {
|
|
|
69256
69270
|
if (!tmuxPaneHasTrustPrompt(snapshot)) {
|
|
69257
69271
|
return;
|
|
69258
69272
|
}
|
|
69259
|
-
await
|
|
69273
|
+
await acceptTrustPrompt({
|
|
69260
69274
|
tmux: params.tmux,
|
|
69261
69275
|
sessionName: params.sessionName,
|
|
69262
69276
|
captureLines: params.captureLines
|
|
@@ -69288,7 +69302,7 @@ async function waitForTmuxSessionBootstrap(params) {
|
|
|
69288
69302
|
if (snapshot) {
|
|
69289
69303
|
lastSnapshot = snapshot;
|
|
69290
69304
|
if (params.trustWorkspace && tmuxPaneHasTrustPrompt(snapshot)) {
|
|
69291
|
-
await
|
|
69305
|
+
await acceptTrustPrompt({
|
|
69292
69306
|
tmux: params.tmux,
|
|
69293
69307
|
sessionName: params.sessionName,
|
|
69294
69308
|
captureLines: params.captureLines
|
|
@@ -69321,7 +69335,7 @@ async function waitForTmuxSessionBootstrap(params) {
|
|
|
69321
69335
|
snapshot: lastSnapshot
|
|
69322
69336
|
};
|
|
69323
69337
|
}
|
|
69324
|
-
async function
|
|
69338
|
+
async function acceptTrustPrompt(params) {
|
|
69325
69339
|
await params.tmux.sendKey(params.sessionName, "Enter");
|
|
69326
69340
|
const deadline = Date.now() + TRUST_PROMPT_MAX_WAIT_MS;
|
|
69327
69341
|
while (Date.now() <= deadline) {
|
|
@@ -69970,7 +69984,7 @@ class RunnerService {
|
|
|
69970
69984
|
try {
|
|
69971
69985
|
const snapshot = await this.captureSessionSnapshot(resolved);
|
|
69972
69986
|
if (tmuxPaneHasTrustPrompt(snapshot)) {
|
|
69973
|
-
await this.
|
|
69987
|
+
await this.acceptVisibleWorkspaceTrustPrompt(resolved);
|
|
69974
69988
|
continue;
|
|
69975
69989
|
}
|
|
69976
69990
|
return;
|
|
@@ -70041,6 +70055,7 @@ class RunnerService {
|
|
|
70041
70055
|
});
|
|
70042
70056
|
try {
|
|
70043
70057
|
await clearRunnerExitRecord(this.loadedConfig.stateDir, resolved.sessionName);
|
|
70058
|
+
await this.acceptWorkspaceTrustPromptIfPresent(resolved);
|
|
70044
70059
|
await this.syncStoredSessionIdForResolvedTarget(resolved);
|
|
70045
70060
|
} catch (error) {
|
|
70046
70061
|
throw await this.mapSessionError(error, resolved.sessionName, "during startup");
|
|
@@ -70124,7 +70139,7 @@ class RunnerService {
|
|
|
70124
70139
|
return resolved;
|
|
70125
70140
|
}
|
|
70126
70141
|
async finalizeSessionStartup(resolved, params) {
|
|
70127
|
-
await this.
|
|
70142
|
+
await this.acceptWorkspaceTrustPromptIfPresent(resolved);
|
|
70128
70143
|
await this.verifySessionReady(resolved);
|
|
70129
70144
|
if (params.storedOrExplicitSessionId) {
|
|
70130
70145
|
await this.persistStoredSessionIdBestEffort(resolved, params.storedOrExplicitSessionId, params.runnerCommand);
|
|
@@ -70158,14 +70173,14 @@ class RunnerService {
|
|
|
70158
70173
|
return;
|
|
70159
70174
|
}
|
|
70160
70175
|
}
|
|
70161
|
-
async
|
|
70176
|
+
async acceptWorkspaceTrustPromptIfPresent(resolved) {
|
|
70162
70177
|
if (!resolved.runner.trustWorkspace) {
|
|
70163
70178
|
return;
|
|
70164
70179
|
}
|
|
70165
|
-
await this.
|
|
70180
|
+
await this.acceptVisibleWorkspaceTrustPrompt(resolved);
|
|
70166
70181
|
}
|
|
70167
|
-
async
|
|
70168
|
-
await
|
|
70182
|
+
async acceptVisibleWorkspaceTrustPrompt(resolved) {
|
|
70183
|
+
await acceptTmuxTrustPromptIfPresent({
|
|
70169
70184
|
tmux: this.tmux,
|
|
70170
70185
|
sessionName: resolved.sessionName,
|
|
70171
70186
|
captureLines: resolved.stream.captureLines,
|
|
@@ -70246,9 +70261,22 @@ class RunnerService {
|
|
|
70246
70261
|
async triggerNewSessionInLiveRunner(resolved) {
|
|
70247
70262
|
const oldSessionId = (await this.sessionMapping.get(resolved.sessionKey))?.sessionId;
|
|
70248
70263
|
const command = this.resolveNewSessionCommand(resolved);
|
|
70249
|
-
await this.
|
|
70264
|
+
await this.acceptWorkspaceTrustPromptIfPresent(resolved);
|
|
70265
|
+
let submitUnconfirmedError = null;
|
|
70266
|
+
try {
|
|
70267
|
+
await this.submitNewSessionCommand(resolved, command);
|
|
70268
|
+
} catch (error) {
|
|
70269
|
+
if (error instanceof TmuxSubmitUnconfirmedError) {
|
|
70270
|
+
submitUnconfirmedError = error;
|
|
70271
|
+
} else {
|
|
70272
|
+
throw error;
|
|
70273
|
+
}
|
|
70274
|
+
}
|
|
70250
70275
|
const sessionId = await this.captureNewSessionIdentityAfterTrigger(resolved, oldSessionId);
|
|
70251
70276
|
if (!sessionId) {
|
|
70277
|
+
if (submitUnconfirmedError) {
|
|
70278
|
+
throw submitUnconfirmedError;
|
|
70279
|
+
}
|
|
70252
70280
|
this.throwNewSessionCaptureFailure(command, oldSessionId);
|
|
70253
70281
|
}
|
|
70254
70282
|
try {
|
|
@@ -70422,6 +70450,7 @@ class RunnerService {
|
|
|
70422
70450
|
if (!await this.tmux.hasSession(resolved.sessionName)) {
|
|
70423
70451
|
throw new Error(`tmux session "${resolved.sessionName}" does not exist`);
|
|
70424
70452
|
}
|
|
70453
|
+
await this.acceptWorkspaceTrustPromptIfPresent(resolved);
|
|
70425
70454
|
await submitTmuxSessionInput({
|
|
70426
70455
|
tmux: this.tmux,
|
|
70427
70456
|
sessionName: resolved.sessionName,
|
|
@@ -70500,6 +70529,14 @@ async function monitorTmuxRun(params) {
|
|
|
70500
70529
|
let firstMeaningfulDeltaLogged = false;
|
|
70501
70530
|
let noOutputThresholdLogged = false;
|
|
70502
70531
|
if (params.prompt) {
|
|
70532
|
+
if (params.trustWorkspace) {
|
|
70533
|
+
await acceptTmuxTrustPromptIfPresent({
|
|
70534
|
+
tmux: params.tmux,
|
|
70535
|
+
sessionName: params.sessionName,
|
|
70536
|
+
captureLines: params.captureLines,
|
|
70537
|
+
startupDelayMs: params.startupDelayMs ?? 0
|
|
70538
|
+
});
|
|
70539
|
+
}
|
|
70503
70540
|
logLatencyDebug("tmux-submit-start", params.timingContext, {
|
|
70504
70541
|
sessionName: params.sessionName,
|
|
70505
70542
|
promptSubmitDelayMs: params.promptSubmitDelayMs
|
|
@@ -71242,6 +71279,8 @@ class SessionService {
|
|
|
71242
71279
|
sessionName: run.resolved.sessionName,
|
|
71243
71280
|
prompt: params.prompt,
|
|
71244
71281
|
promptSubmitDelayMs: run.resolved.runner.promptSubmitDelayMs,
|
|
71282
|
+
trustWorkspace: run.resolved.runner.trustWorkspace,
|
|
71283
|
+
startupDelayMs: run.resolved.runner.startupDelayMs,
|
|
71245
71284
|
captureLines: run.resolved.stream.captureLines,
|
|
71246
71285
|
updateIntervalMs: run.resolved.stream.updateIntervalMs,
|
|
71247
71286
|
idleTimeoutMs: run.resolved.stream.idleTimeoutMs,
|
|
@@ -71620,8 +71659,10 @@ var TELEGRAM_REPLY_COMMAND_BASE = `{{command}} message send \\
|
|
|
71620
71659
|
--render native \\
|
|
71621
71660
|
`;
|
|
71622
71661
|
var SLACK_REPLY_STYLE_HINT = `Put readable hierarchical Markdown in the --message body.
|
|
71662
|
+
For clickable links, use canonical URLs and do not wrap them in backticks.
|
|
71623
71663
|
Keep each paragraph, list, or code block under 2500 chars.`;
|
|
71624
71664
|
var TELEGRAM_REPLY_STYLE_HINT = `Put readable hierarchical Markdown in the --message body.
|
|
71665
|
+
For clickable links, use canonical URLs and do not wrap them in backticks.
|
|
71625
71666
|
Keep the Markdown body under 3000 chars.`;
|
|
71626
71667
|
var ACCOUNT_CLAUSE = " --account {{account_id}} \\\n";
|
|
71627
71668
|
var EMPTY_ACCOUNT_CLAUSE = "";
|
|
@@ -86200,6 +86241,8 @@ async function withStartupTimeout(name, start2) {
|
|
|
86200
86241
|
}
|
|
86201
86242
|
|
|
86202
86243
|
// src/control/runtime-management-cli.ts
|
|
86244
|
+
var RESTART_STOP_STATUS_RECHECK_TIMEOUT_MS = 2000;
|
|
86245
|
+
var RESTART_STOP_STATUS_RECHECK_INTERVAL_MS = 100;
|
|
86203
86246
|
function getOperatorConfigPath() {
|
|
86204
86247
|
return expandHomePath(process.env.CLISBOT_CONFIG_PATH || DEFAULT_CONFIG_PATH);
|
|
86205
86248
|
}
|
|
@@ -86439,6 +86482,7 @@ async function stop(hard = false) {
|
|
|
86439
86482
|
async function restart(dependencies = {
|
|
86440
86483
|
stopDetachedRuntime,
|
|
86441
86484
|
getRuntimeStatus,
|
|
86485
|
+
sleep,
|
|
86442
86486
|
warn: (message) => console.error(message)
|
|
86443
86487
|
}) {
|
|
86444
86488
|
const configPath = getOperatorConfigPath();
|
|
@@ -86448,7 +86492,7 @@ async function restart(dependencies = {
|
|
|
86448
86492
|
hard: false
|
|
86449
86493
|
});
|
|
86450
86494
|
} catch (error) {
|
|
86451
|
-
const status = await
|
|
86495
|
+
const status = await waitForStoppedRuntimeAfterStopError(configPath, dependencies);
|
|
86452
86496
|
if (status.running) {
|
|
86453
86497
|
throw error;
|
|
86454
86498
|
}
|
|
@@ -86456,6 +86500,16 @@ async function restart(dependencies = {
|
|
|
86456
86500
|
dependencies.warn(`warning: clisbot stop reported an error, but status now shows the service is stopped; continuing with start. Stop error: ${message}`);
|
|
86457
86501
|
}
|
|
86458
86502
|
}
|
|
86503
|
+
async function waitForStoppedRuntimeAfterStopError(configPath, dependencies) {
|
|
86504
|
+
const deadline = Date.now() + RESTART_STOP_STATUS_RECHECK_TIMEOUT_MS;
|
|
86505
|
+
while (true) {
|
|
86506
|
+
const status = await dependencies.getRuntimeStatus({ configPath });
|
|
86507
|
+
if (!status.running || Date.now() >= deadline) {
|
|
86508
|
+
return status;
|
|
86509
|
+
}
|
|
86510
|
+
await dependencies.sleep(RESTART_STOP_STATUS_RECHECK_INTERVAL_MS);
|
|
86511
|
+
}
|
|
86512
|
+
}
|
|
86459
86513
|
async function status() {
|
|
86460
86514
|
await printStatusSummary();
|
|
86461
86515
|
}
|