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 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`). To develop or use
23
- `npm run login`, clone instead:
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
- npm install
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
- npm run login # prints TG_SESSION=... -> paste it into .env
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 `npm run dev -- <args>`, or build once (`npm run build`) and use the `mira-harness` bin / `npx`.
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 — prints the full settled reply as JSON
54
- npm run dev -- send "STON_USDT_10"
55
-
56
- # self-driving catalog (paced 15s, STOP_MIRA kill switch, observe-only)
57
- npm run dev -- loop --category core
58
- npm run dev -- loop --category skills --max 4
59
-
60
- # also press a safe ✅ Confirm on generation probes (spends Pro credits)
61
- npm run dev -- loop --category generation --confirm
62
-
63
- # run in a group instead of the DM (needs TG_EXPERIMENT_CHAT)
64
- npm run dev -- loop --peer experiment
65
-
66
- # distill the run log into Markdown
67
- npm run dev -- report
68
- npm run dev -- report --out report.md
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
- After `npm run build`, or straight from npm (no clone needed):
86
-
87
- ```bash
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 (30 probes: `core` / `skills` / `generation` / `wallet`) is just a
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", "STON_USDT_10");
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 `npm run build` first):
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
- npm run typecheck # tsc --noEmit
208
- npm test # unit tests for the pure extractors (no network)
209
- npm run build # tsup -> dist/cli.js
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[];