@telorun/debug-ui 0.1.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/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ # SUSTAINABLE USE LICENSE (Fair-code)
2
+
3
+ Copyright (c) 2026 CodeNet Sp. z o.o.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to use, copy, modify, and distribute the Software for any purpose—including commercial purposes—subject to the following conditions:
6
+
7
+ 1. ANTI-COMPETITION RESTRICTION: The Software may not be provided to third parties as a managed service, commercial SaaS (Software-as-a-Service), PaaS (Platform-as-a-Service), BaaS (Backend-as-a-Service), or similar offering where the primary value provided to the user is the functionality of the Software itself, without a separate commercial license from the copyright holder.
8
+
9
+ 2. PERMITTED COMMERCIAL USE: You are free to use the Software to build, host, and monetize your own commercial applications, products, and services, provided such use does not violate Clause 1.
10
+
11
+ 3. ATTRIBUTION: This copyright notice and license must be included in all copies or substantial portions of the Software.
12
+
13
+ 4. CONTRIBUTIONS: Contributions to the Software are welcome and encouraged. By contributing, you agree that your contributions may be incorporated into the Software and distributed under this license.
14
+
15
+ 5. DISCLAIMER: The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the Software or the use or other dealings in the Software.
16
+
17
+ For commercial licensing, managed hosting exemptions, or enterprise inquiries, please contact <contact@codenet.pl>.
package/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # @telorun/debug-ui
2
+
3
+ Browser-safe building blocks for watching a running Telo app's event stream:
4
+ the debug **wire format**, **filter logic**, an **SSE client**, and **React
5
+ components** — plus a standalone single-page app, built both multi-file
6
+ (`app-dist/`) and as one self-contained file (`app-single/index.html`).
7
+
8
+ This package is a **consumer**: it parses events, it never produces them. It has
9
+ no Node-only dependency (no `fs`/`http`, not `@telorun/runner-core`) so it runs
10
+ in any browser and in the editor webview.
11
+
12
+ ## Why it's shaped this way
13
+
14
+ The Telo kernel is polyglot — Node today, Rust/Go later. The thing that *produces*
15
+ debug events (taps the kernel, serializes values, serves them) lives inside each
16
+ runtime and is reimplemented per language. What's shared across runtimes is the
17
+ **wire format**, not code. Every consumer (this package, the editor) codes against
18
+ that contract; the Node producer lives in the CLI.
19
+
20
+ ```
21
+ wire format (this README + wire-schema.json) ← the cross-runtime contract
22
+ produced by → a kernel runtime's debug server (Node: the telo CLI)
23
+ consumed by → the standalone app (served by the producer) + the editor panel
24
+ ```
25
+
26
+ ## Wire format
27
+
28
+ Each event is one JSON object, delivered one-per-line over SSE and JSONL. Schema:
29
+ [`wire-schema.json`](./wire-schema.json).
30
+
31
+ ```json
32
+ { "timestamp": "2026-06-13T07:00:00.000Z", "event": "Server.Listening",
33
+ "payload": { "port": 5599, "mounts": [{ "path": "/v1", "mount": { "kind": "Http.Api", "name": "Api" } }] } }
34
+ ```
35
+
36
+ Producers reduce `payload` values to wire-safe forms before sending:
37
+
38
+ | Value | Encodes as |
39
+ |---|---|
40
+ | byte buffer (`Uint8Array`/`Buffer`, any file kind) | `{ "$blob": "blobs/<id>", "mediaType", "byteLength" }` — a pointer; bytes are offloaded to the producer's blob store, fetched on demand. The key it sits under is preserved. |
41
+ | resolved `!ref` (live resource instance) | `{ "kind", "name" }` |
42
+ | other live / unrepresentable value (controller, stream, client, function, bigint) | `"[Marker]"` (e.g. `"[Stream]"`, `"[Kernel]"`) |
43
+ | reference cycle | `"[Circular]"` |
44
+ | anything else | plain JSON |
45
+
46
+ A non-TS producer (Rust/Go) conforms to the same table.
47
+
48
+ ### Blobs (binary payloads)
49
+
50
+ The bytes never enter the log. A producer offloads each buffer to a
51
+ content-addressed store (so identical buffers dedupe) and emits a `$blob`
52
+ pointer — a path **relative to the producer origin**. The consumer resolves it
53
+ against the debug-server URL and fetches `GET /blobs/<id>` (which sets the real
54
+ `Content-Type`). `mediaType` is sniffed from magic bytes, falling back to
55
+ `application/octet-stream`. The UI renders `image/*` inline and other types as a
56
+ download link; `byteLength` + sibling `width`/`height` (if present) form the
57
+ caption. An evicted blob 404s — the log keeps the pointer + metadata, so you
58
+ still see *what* it was.
59
+
60
+ ## Endpoints (served by a producer's debug server)
61
+
62
+ - `GET /` — the standalone UI (the self-contained `app-single/index.html` bytes).
63
+ - `GET /events` — SSE; on connect, the producer flushes its replay buffer, then
64
+ streams live events.
65
+ - `GET /events.jsonl` — the full event log, for download.
66
+ - `GET /blobs/:id` — a binary payload offloaded from an event (see Blobs below).
67
+ - `GET /json/version` — discovery handshake: `{ protocol, protocolVersion, url,
68
+ events, eventsLog, blobs }`, so a consumer can confirm the wire format before
69
+ connecting.
70
+
71
+ The Telo CLI exposes this server with `telo run <manifest> --inspect[=[host:]port]`
72
+ (loopback `127.0.0.1:9230` by default). It serves the UI **same-origin**, fetching
73
+ the `app-single` bundle on demand and caching it under the `.telo` cache root — the
74
+ CLI bundles no UI bytes (see Scripts). `--debug` is the separate, network-free path:
75
+ it only writes the `.telo.debug.jsonl` event log.
76
+
77
+ ## Surface
78
+
79
+ ```ts
80
+ // Logic — framework-agnostic, browser-safe
81
+ import { connectDebugStream, matchesFilter, distinctSuffixes,
82
+ isWireRef, eventSuffix, type DebugEvent, type EventFilter } from "@telorun/debug-ui";
83
+
84
+ // React components — for the standalone app and the editor panel
85
+ import { DebugWatcher } from "@telorun/debug-ui/components";
86
+ // <DebugWatcher url="http://localhost:9230/events" />
87
+ ```
88
+
89
+ ## Scripts
90
+
91
+ - `pnpm --filter @telorun/debug-ui build` — builds both the multi-file standalone
92
+ app (`app-dist/`) and the self-contained single-file app (`app-single/index.html`).
93
+ The single file is what the CLI fetches on demand (hosted on npm, delivered via
94
+ jsDelivr) and serves; in the monorepo the CLI resolves it directly from this
95
+ package, so a local build is testable without any network.
96
+ - `pnpm --filter @telorun/debug-ui test` — unit tests (filter logic).
@@ -0,0 +1 @@
1
+ .tdbg-root{--tdbg-bg: #0f1115;--tdbg-panel: #161a21;--tdbg-line: #232a34;--tdbg-text: #d7dce3;--tdbg-muted: #7a8493;--tdbg-accent: #4f9cf0;display:flex;flex-direction:column;height:100%;min-height:0;font:13px/1.5 ui-monospace,SF Mono,Menlo,Consolas,monospace;color:var(--tdbg-text);background:var(--tdbg-bg)}.tdbg-bar{position:sticky;top:0;z-index:1;padding:8px 10px;background:var(--tdbg-panel);border-bottom:1px solid var(--tdbg-line)}.tdbg-bar-row{display:flex;align-items:center;gap:8px}.tdbg-search{flex:1}.tdbg-search,.tdbg-kind{padding:5px 8px;color:var(--tdbg-text);background:var(--tdbg-bg);border:1px solid var(--tdbg-line);border-radius:5px;font:inherit}.tdbg-kind{width:140px}.tdbg-btn{padding:5px 10px;color:var(--tdbg-text);background:var(--tdbg-bg);border:1px solid var(--tdbg-line);border-radius:5px;cursor:pointer;font:inherit}.tdbg-btn:hover{border-color:var(--tdbg-accent)}.tdbg-counts{color:var(--tdbg-muted);white-space:nowrap}.tdbg-status{width:9px;height:9px;border-radius:50%;background:var(--tdbg-muted);flex:0 0 auto}.tdbg-status-open{background:#3fb950}.tdbg-status-connecting{background:#d29922}.tdbg-status-closed{background:#f85149}.tdbg-facets{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}.tdbg-facet{padding:2px 8px;color:var(--tdbg-muted);background:var(--tdbg-bg);border:1px solid var(--tdbg-line);border-radius:999px;cursor:pointer;font:inherit}.tdbg-facet-on{color:#fff;border-color:var(--tdbg-accent);background:#4f9cf02e}.tdbg-table{flex:1;min-height:0;overflow:auto}.tdbg-empty{padding:24px;color:var(--tdbg-muted);text-align:center}.tdbg-row{border-bottom:1px solid var(--tdbg-line)}.tdbg-rowhead{display:flex;align-items:baseline;gap:8px;width:100%;padding:4px 10px;background:none;border:none;color:inherit;cursor:pointer;text-align:left;font:inherit}.tdbg-rowhead:hover{background:var(--tdbg-panel)}.tdbg-caret{width:12px;color:var(--tdbg-muted)}.tdbg-time{color:var(--tdbg-muted);white-space:nowrap}.tdbg-badge{padding:1px 6px;border-radius:4px;background:var(--tdbg-line);color:var(--tdbg-text);white-space:nowrap}.tdbg-suffix-failed,.tdbg-suffix-rejected,.tdbg-suffix-cancelled{background:#f8514933;color:#ff8079}.tdbg-suffix-invoked,.tdbg-suffix-listening,.tdbg-suffix-started,.tdbg-suffix-initialized{background:#3fb9502e;color:#5fd673}.tdbg-name{white-space:nowrap}.tdbg-preview{color:var(--tdbg-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tdbg-payload-wrap{background:var(--tdbg-panel)}.tdbg-payload{margin:0;padding:8px 10px 10px 30px;background:var(--tdbg-panel);overflow:auto;white-space:pre-wrap;word-break:break-word}.tdbg-media{display:flex;flex-wrap:wrap;gap:12px;padding:10px 10px 4px 30px}.tdbg-blob{margin:0;display:flex;flex-direction:column;gap:4px;max-width:320px}.tdbg-blob-img{max-width:320px;max-height:240px;border:1px solid var(--tdbg-line);border-radius:6px;background:repeating-conic-gradient(#1b202840 0% 25%,transparent 0% 50%) 50% / 16px 16px;object-fit:contain}.tdbg-blob-file{display:inline-block;padding:8px 12px;color:var(--tdbg-accent);border:1px solid var(--tdbg-line);border-radius:6px;text-decoration:none}.tdbg-blob-file:hover{border-color:var(--tdbg-accent)}.tdbg-blob-cap{color:var(--tdbg-muted);font-size:11px;word-break:break-all}.tdbg-muted{color:var(--tdbg-muted)}.tdbg-ref{color:var(--tdbg-accent)}