mira-harness 0.1.4 → 0.2.0
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 +63 -48
- package/dist/capture.d.ts +65 -0
- package/dist/catalog.d.ts +27 -0
- package/dist/cli.js +992 -223
- package/dist/client.d.ts +52 -0
- package/dist/commands/report.d.ts +25 -0
- package/dist/env.d.ts +18 -0
- package/dist/index.d.ts +13 -165
- package/dist/index.js +628 -35
- package/dist/log.d.ts +9 -0
- package/dist/mcp.js +796 -191
- package/examples/catalog.sample.json +3 -4
- package/llms.txt +6 -4
- package/package.json +16 -8
- package/dist/chunk-NSGRB4ZK.js +0 -636
- package/dist/chunk-PPYKMDQ4.js +0 -17
package/README.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# mira-harness — a CLI + MCP dev-tool for communicating with [@mira](https://t.me/mira)
|
|
2
2
|
|
|
3
|
+
```text
|
|
4
|
+
* . *
|
|
5
|
+
.-------.
|
|
6
|
+
| o o | hi — I drive @mira so you don't have to copy-paste its replies.
|
|
7
|
+
| ~ | ( I blink ^_^ when doctor passes, and yawn -_- on a timeout. )
|
|
8
|
+
'-------'
|
|
9
|
+
```
|
|
10
|
+
|
|
3
11
|
Drive the **@mira Telegram bot** from a userbot, capture its **full** reply
|
|
4
12
|
(buttons, links, media, edits), and run a self-driving experiment catalog — from the
|
|
5
13
|
**CLI**, as a **library**, or via **MCP**.
|
|
@@ -19,13 +27,14 @@ is a **userbot** — driving a real Telegram *user account* via MTProto (GramJS)
|
|
|
19
27
|
## Install
|
|
20
28
|
|
|
21
29
|
Published on [npm](https://www.npmjs.com/package/mira-harness) — run the CLI / MCP via
|
|
22
|
-
`npx mira-harness <command>` (or `npm i -g mira-harness`)
|
|
23
|
-
`
|
|
30
|
+
`npx mira-harness <command>` (or `npm i -g mira-harness`) — runs under Node, no bun
|
|
31
|
+
needed. To develop (or use `bun run login`), clone instead; development uses
|
|
32
|
+
[bun](https://bun.sh):
|
|
24
33
|
|
|
25
34
|
```bash
|
|
26
35
|
git clone https://github.com/Masashi-Ono0611/mira-harness.git
|
|
27
36
|
cd mira-harness
|
|
28
|
-
|
|
37
|
+
bun install
|
|
29
38
|
```
|
|
30
39
|
|
|
31
40
|
## Configuration
|
|
@@ -40,55 +49,37 @@ cp .env.example .env
|
|
|
40
49
|
Mint the session once (interactive — enter the code Telegram sends you):
|
|
41
50
|
|
|
42
51
|
```bash
|
|
43
|
-
|
|
52
|
+
bun run login # prints TG_SESSION=... -> paste it into .env
|
|
44
53
|
```
|
|
45
54
|
|
|
46
55
|
(Optional) Mira Pro credits: DM `/promo MIRAFAM26` to @mira.
|
|
47
56
|
|
|
48
57
|
## Usage
|
|
49
58
|
|
|
50
|
-
Via `
|
|
59
|
+
Via `bun run dev -- <args>`, or build once (`bun run build`) and use the `mira-harness` bin / `npx`.
|
|
51
60
|
|
|
52
61
|
```bash
|
|
53
|
-
# one probe —
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
# preflight checks (env / session / connectivity) and list the catalog
|
|
71
|
-
npm run dev -- doctor
|
|
72
|
-
npm run dev -- catalog
|
|
73
|
-
npm run dev -- loop --list
|
|
74
|
-
|
|
75
|
-
# message via stdin; tune timing; quiet (no spinner); skip the run log
|
|
76
|
-
echo "STON_USDT_10" | npm run dev -- send
|
|
77
|
-
npm run dev -- send "ping" --settle 3000 --timeout 30000 --quiet --no-log
|
|
78
|
-
|
|
79
|
-
# live-tail @mira while you poke at it by hand; custom catalog; filtered report
|
|
80
|
-
npm run dev -- watch
|
|
81
|
-
npm run dev -- loop --catalog ./examples/catalog.sample.json
|
|
82
|
-
npm run dev -- report --category core
|
|
62
|
+
# one probe — full settled reply as JSON (message via arg or stdin)
|
|
63
|
+
bun run dev -- send "What can you do?"
|
|
64
|
+
echo "What can you do?" | bun run dev -- send
|
|
65
|
+
|
|
66
|
+
# self-driving catalog — paced, STOP_MIRA kill switch, observe-only
|
|
67
|
+
bun run dev -- loop --category core
|
|
68
|
+
bun run dev -- loop --category generation --confirm # also taps a safe ✅ (spends Pro credits)
|
|
69
|
+
|
|
70
|
+
# read the results back
|
|
71
|
+
bun run dev -- report --out report.md # JSONL run log → Markdown
|
|
72
|
+
bun run dev -- stats # totals · latency records · 🏆 fastest · sparkline
|
|
73
|
+
|
|
74
|
+
# no-send commands: preflight, dry-run, live-tail
|
|
75
|
+
bun run dev -- doctor
|
|
76
|
+
bun run dev -- loop --list
|
|
77
|
+
bun run dev -- watch # watch @mira while you poke it by hand
|
|
83
78
|
```
|
|
84
79
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
npx mira-harness doctor
|
|
89
|
-
npx mira-harness send "STON_USDT_10"
|
|
90
|
-
npx mira-harness loop --category core
|
|
91
|
-
```
|
|
80
|
+
Once built (`bun run build`) or installed (`npm i -g mira-harness`), the same commands run as
|
|
81
|
+
`mira-harness <command>` — or straight from npm with no clone: `npx mira-harness doctor`.
|
|
82
|
+
Every command and flag is in [Commands](#commands); custom catalogs in [Custom catalog](#custom-catalog).
|
|
92
83
|
|
|
93
84
|
## Capture fidelity (the point)
|
|
94
85
|
|
|
@@ -114,12 +105,29 @@ a "typing…" fallback for a slow bot — replies run 5–62s) and capture, per
|
|
|
114
105
|
| `catalog` | List the catalog (no sends). `--category --catalog --json` |
|
|
115
106
|
| `watch` | Live-tail @mira's messages (observe-only). `--peer` |
|
|
116
107
|
| `report` | Distill the run log into Markdown. `--in --out --category` |
|
|
108
|
+
| `stats` | At-a-glance dashboard: totals, latency records, sparkline. `--in --category --json` |
|
|
117
109
|
|
|
118
110
|
Run `mira-harness --help` (or `<command> --help`) for full options.
|
|
119
111
|
|
|
112
|
+
## Terminal flair
|
|
113
|
+
|
|
114
|
+
Because waiting on a bot shouldn't be boring:
|
|
115
|
+
|
|
116
|
+
- **Mascot** greets you on startup, **blinks `^_^`** when `doctor` passes and **`x_x`** when it fails.
|
|
117
|
+
- **Rotating tips** under the banner surface a hidden flag each day.
|
|
118
|
+
- **Playful spinner** — @mira can take 5–62s, so the wait cycles through verbs ("Summoning…",
|
|
119
|
+
"Consulting the chain…", and a reassuring "Still pondering…" past 30s) next to the elapsed time.
|
|
120
|
+
- **Tab-title progress** during `loop` (`mira loop 3/6 · core`) so you can background it.
|
|
121
|
+
- **Completion ping** — a terminal notification (OSC 9) when a `loop` finishes.
|
|
122
|
+
|
|
123
|
+
It never gets in the way: **all decoration goes to stderr** (stdout stays machine-clean for
|
|
124
|
+
JSON / Markdown) and only on an interactive TTY. Silence it with `--quiet`, `NO_COLOR`, or
|
|
125
|
+
per-feature flags — `MIRA_NO_BANNER=1` (mascot + tip), `MIRA_NO_NOTIFY=1` (completion ping),
|
|
126
|
+
`MIRA_NO_TITLE=1` (tab title).
|
|
127
|
+
|
|
120
128
|
### Custom catalog
|
|
121
129
|
|
|
122
|
-
The built-in catalog (
|
|
130
|
+
The built-in catalog (27 probes: `core` / `skills` / `generation` / `wallet`) is just a
|
|
123
131
|
default. Point `--catalog <file.json>` (CLI) or `catalogFile` (MCP) at your own probe set
|
|
124
132
|
to probe any bot — each entry needs `id` + `send` (`category` / `hypothesis` / `slow` /
|
|
125
133
|
`confirm` / `note` optional). See [`examples/catalog.sample.json`](examples/catalog.sample.json):
|
|
@@ -137,7 +145,7 @@ The CLI is a thin frontend over an exported core:
|
|
|
137
145
|
import { connect, sendAndCollect } from "mira-harness";
|
|
138
146
|
|
|
139
147
|
const client = await connect(process.env.TG_SESSION!);
|
|
140
|
-
const result = await sendAndCollect(client, "mira", "
|
|
148
|
+
const result = await sendAndCollect(client, "mira", "What can you do?");
|
|
141
149
|
console.log(result.messages[0]?.buttons); // captured buttons (incl. web_app/startapp)
|
|
142
150
|
await client.disconnect();
|
|
143
151
|
```
|
|
@@ -158,7 +166,7 @@ Claude/agent can probe @mira directly via tools.
|
|
|
158
166
|
| `mira_report` | `inFile?`, `category?` | run log → Markdown |
|
|
159
167
|
| `mira_doctor` | — | env / session / connectivity check |
|
|
160
168
|
|
|
161
|
-
Register it (local build — run `
|
|
169
|
+
Register it (local build — run `bun run build` first):
|
|
162
170
|
|
|
163
171
|
```json
|
|
164
172
|
{
|
|
@@ -203,10 +211,17 @@ directory. The bin ships in the published `mira-harness` package, so:
|
|
|
203
211
|
|
|
204
212
|
## Develop
|
|
205
213
|
|
|
214
|
+
Development uses [bun](https://bun.sh) (≥ 1.3); the published package stays Node-compatible.
|
|
215
|
+
|
|
206
216
|
```bash
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
217
|
+
bun install
|
|
218
|
+
bun run dev -- doctor # run the CLI straight from source
|
|
219
|
+
bun run lint # biome check (lint + format) · `bun run format` to auto-fix
|
|
220
|
+
bun run typecheck # tsc --noEmit
|
|
221
|
+
bun test # bun:test unit tests (no network)
|
|
222
|
+
bun run build # bun build -> dist (+ tsc declarations)
|
|
223
|
+
bun run check:publish # publint + attw — validate the package & type resolution
|
|
224
|
+
bun run slop # advisory AI-slop scan (never fails)
|
|
210
225
|
```
|
|
211
226
|
|
|
212
227
|
## License
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure extraction: GramJS `Api.Message` -> a flat, JSON-serializable record of
|
|
3
|
+
* everything @mira put in a reply. NO network here, so this is unit-testable.
|
|
4
|
+
*
|
|
5
|
+
* Why this matters: the *interesting* Mira behaviors live OUTSIDE the plain text
|
|
6
|
+
* — Mini App "Launch" cards (web_app / startapp deep links), deep-research source
|
|
7
|
+
* links (text_url entities), generated media. The old harness grabbed only the
|
|
8
|
+
* first message's `.message` string and dropped all of that. These extractors
|
|
9
|
+
* pull it back so a probe captures Mira's full surface.
|
|
10
|
+
*/
|
|
11
|
+
import type { Api } from "telegram";
|
|
12
|
+
/** An inline-keyboard button, flattened to the bits we care about. */
|
|
13
|
+
export interface CapturedButton {
|
|
14
|
+
text: string;
|
|
15
|
+
/** KeyboardButtonUrl / KeyboardButtonUrlAuth */
|
|
16
|
+
url?: string;
|
|
17
|
+
/** KeyboardButtonWebView / KeyboardButtonSimpleWebView — the Mini App "Launch" target. */
|
|
18
|
+
webAppUrl?: string;
|
|
19
|
+
/** KeyboardButtonCallback payload, base64 (rarely human-readable). */
|
|
20
|
+
callbackData?: string;
|
|
21
|
+
}
|
|
22
|
+
/** A hyperlink found in the message text (entities). */
|
|
23
|
+
export interface CapturedLink {
|
|
24
|
+
/** The visible text the link is attached to (text_url) — omitted for bare urls. */
|
|
25
|
+
text?: string;
|
|
26
|
+
url: string;
|
|
27
|
+
}
|
|
28
|
+
export interface CapturedMedia {
|
|
29
|
+
kind: "photo" | "video" | "audio" | "document" | "webpage" | "other";
|
|
30
|
+
/** webpage url, when the media is a link preview. */
|
|
31
|
+
url?: string;
|
|
32
|
+
mime?: string;
|
|
33
|
+
fileName?: string;
|
|
34
|
+
}
|
|
35
|
+
/** One message (or its final edited state) from @mira. */
|
|
36
|
+
export interface CapturedMessage {
|
|
37
|
+
id: number;
|
|
38
|
+
text: string;
|
|
39
|
+
/** How many edits we observed (0 = sent once, never edited). */
|
|
40
|
+
editCount: number;
|
|
41
|
+
buttons: CapturedButton[];
|
|
42
|
+
links: CapturedLink[];
|
|
43
|
+
media?: CapturedMedia;
|
|
44
|
+
}
|
|
45
|
+
/** The full result of one probe = one send + everything @mira sent back. */
|
|
46
|
+
export interface ProbeResult {
|
|
47
|
+
peer: string;
|
|
48
|
+
sent: string;
|
|
49
|
+
messages: CapturedMessage[];
|
|
50
|
+
/** ms from send to the first reply message (null if nothing arrived). */
|
|
51
|
+
firstReplyMs: number | null;
|
|
52
|
+
/** ms from send until the reply settled (or the cap was hit). */
|
|
53
|
+
totalMs: number;
|
|
54
|
+
/** true if we gave up waiting for a first reply. */
|
|
55
|
+
timedOut: boolean;
|
|
56
|
+
ts: string;
|
|
57
|
+
}
|
|
58
|
+
export declare function extractLinks(text: string, entities?: Api.TypeMessageEntity[]): CapturedLink[];
|
|
59
|
+
export declare function extractButtons(markup?: Api.TypeReplyMarkup): CapturedButton[];
|
|
60
|
+
export declare function extractMedia(media?: Api.TypeMessageMedia): CapturedMedia | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Flatten one message into a CapturedMessage. `editCount` is supplied by the
|
|
63
|
+
* collector (it tracks how many edits it saw for this id), defaulting to 0.
|
|
64
|
+
*/
|
|
65
|
+
export declare function extractMessage(msg: Api.Message, editCount?: number): CapturedMessage;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Built-in categories. Custom catalogs may use any category string. */
|
|
2
|
+
export type ProbeCategory = "core" | "skills" | "generation" | "wallet";
|
|
3
|
+
export interface Probe {
|
|
4
|
+
id: string;
|
|
5
|
+
/** Built-in probes use a ProbeCategory; custom catalogs may use any string. */
|
|
6
|
+
category: string;
|
|
7
|
+
hypothesis: string;
|
|
8
|
+
send: string;
|
|
9
|
+
/** Generation / deep research is slow — widen collector windows. */
|
|
10
|
+
slow?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Safe to press a "✅ Confirm" callback button to complete this probe (e.g. to
|
|
13
|
+
* actually run a credit-gated generation). ONLY set on generation probes — never
|
|
14
|
+
* on wallet/OAuth. Still requires the runner's `--confirm` CLI opt-in to fire.
|
|
15
|
+
*/
|
|
16
|
+
confirm?: boolean;
|
|
17
|
+
note?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const CATEGORIES: ProbeCategory[];
|
|
20
|
+
/**
|
|
21
|
+
* Load a custom probe catalog from a JSON file (an array of probe objects).
|
|
22
|
+
* Throws a clear error on a missing file or an entry that fails validation.
|
|
23
|
+
*/
|
|
24
|
+
export declare function loadCatalog(file: string): Probe[];
|
|
25
|
+
export declare const CATALOG: Probe[];
|
|
26
|
+
/** Filter probes by category. `source` defaults to the built-in CATALOG. */
|
|
27
|
+
export declare function probesFor(category?: string, source?: Probe[]): Probe[];
|