mira-harness 0.1.5 → 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
@@ -27,13 +27,14 @@ is a **userbot** — driving a real Telegram *user account* via MTProto (GramJS)
27
27
  ## Install
28
28
 
29
29
  Published on [npm](https://www.npmjs.com/package/mira-harness) — run the CLI / MCP via
30
- `npx mira-harness <command>` (or `npm i -g mira-harness`). To develop or use
31
- `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):
32
33
 
33
34
  ```bash
34
35
  git clone https://github.com/Masashi-Ono0611/mira-harness.git
35
36
  cd mira-harness
36
- npm install
37
+ bun install
37
38
  ```
38
39
 
39
40
  ## Configuration
@@ -48,35 +49,35 @@ cp .env.example .env
48
49
  Mint the session once (interactive — enter the code Telegram sends you):
49
50
 
50
51
  ```bash
51
- npm run login # prints TG_SESSION=... -> paste it into .env
52
+ bun run login # prints TG_SESSION=... -> paste it into .env
52
53
  ```
53
54
 
54
55
  (Optional) Mira Pro credits: DM `/promo MIRAFAM26` to @mira.
55
56
 
56
57
  ## Usage
57
58
 
58
- 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`.
59
60
 
60
61
  ```bash
61
62
  # one probe — full settled reply as JSON (message via arg or stdin)
62
- npm run dev -- send "STON_USDT_10"
63
- echo "STON_USDT_10" | npm run dev -- send
63
+ bun run dev -- send "What can you do?"
64
+ echo "What can you do?" | bun run dev -- send
64
65
 
65
66
  # self-driving catalog — paced, STOP_MIRA kill switch, observe-only
66
- npm run dev -- loop --category core
67
- npm run dev -- loop --category generation --confirm # also taps a safe ✅ (spends Pro credits)
67
+ bun run dev -- loop --category core
68
+ bun run dev -- loop --category generation --confirm # also taps a safe ✅ (spends Pro credits)
68
69
 
69
70
  # read the results back
70
- npm run dev -- report --out report.md # JSONL run log → Markdown
71
- npm run dev -- stats # totals · latency records · 🏆 fastest · sparkline
71
+ bun run dev -- report --out report.md # JSONL run log → Markdown
72
+ bun run dev -- stats # totals · latency records · 🏆 fastest · sparkline
72
73
 
73
74
  # no-send commands: preflight, dry-run, live-tail
74
- npm run dev -- doctor
75
- npm run dev -- loop --list
76
- npm run dev -- watch # watch @mira while you poke it by hand
75
+ bun run dev -- doctor
76
+ bun run dev -- loop --list
77
+ bun run dev -- watch # watch @mira while you poke it by hand
77
78
  ```
78
79
 
79
- Once built (`npm run build`) or installed (`npm i -g mira-harness`), the same commands run as
80
+ Once built (`bun run build`) or installed (`npm i -g mira-harness`), the same commands run as
80
81
  `mira-harness <command>` — or straight from npm with no clone: `npx mira-harness doctor`.
81
82
  Every command and flag is in [Commands](#commands); custom catalogs in [Custom catalog](#custom-catalog).
82
83
 
@@ -126,7 +127,7 @@ per-feature flags — `MIRA_NO_BANNER=1` (mascot + tip), `MIRA_NO_NOTIFY=1` (com
126
127
 
127
128
  ### Custom catalog
128
129
 
129
- 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
130
131
  default. Point `--catalog <file.json>` (CLI) or `catalogFile` (MCP) at your own probe set
131
132
  to probe any bot — each entry needs `id` + `send` (`category` / `hypothesis` / `slow` /
132
133
  `confirm` / `note` optional). See [`examples/catalog.sample.json`](examples/catalog.sample.json):
@@ -144,7 +145,7 @@ The CLI is a thin frontend over an exported core:
144
145
  import { connect, sendAndCollect } from "mira-harness";
145
146
 
146
147
  const client = await connect(process.env.TG_SESSION!);
147
- const result = await sendAndCollect(client, "mira", "STON_USDT_10");
148
+ const result = await sendAndCollect(client, "mira", "What can you do?");
148
149
  console.log(result.messages[0]?.buttons); // captured buttons (incl. web_app/startapp)
149
150
  await client.disconnect();
150
151
  ```
@@ -165,7 +166,7 @@ Claude/agent can probe @mira directly via tools.
165
166
  | `mira_report` | `inFile?`, `category?` | run log → Markdown |
166
167
  | `mira_doctor` | — | env / session / connectivity check |
167
168
 
168
- Register it (local build — run `npm run build` first):
169
+ Register it (local build — run `bun run build` first):
169
170
 
170
171
  ```json
171
172
  {
@@ -210,10 +211,17 @@ directory. The bin ships in the published `mira-harness` package, so:
210
211
 
211
212
  ## Develop
212
213
 
214
+ Development uses [bun](https://bun.sh) (≥ 1.3); the published package stays Node-compatible.
215
+
213
216
  ```bash
214
- npm run typecheck # tsc --noEmit
215
- npm test # unit tests for the pure extractors (no network)
216
- 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)
217
225
  ```
218
226
 
219
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[];