letmeknow-cli 0.4.1 → 0.4.3

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,175 +1,84 @@
1
1
  # LetMeKnow
2
2
 
3
- LetMeKnow gives one agent and one human browser a temporary HTML and CSS workspace. The agent renders a page, receives normalized form and button actions, and responds with HTML fragments. A private browser WebSocket carries renders and actions; assets use ordinary HTTP.
3
+ LetMeKnow gives an agent-managed folder a public, live Vite preview. The CLI runs Vite in middleware mode and makes only an outbound WebSocket connection to the relay; it does not listen on a network port.
4
4
 
5
- It is not a localhost proxy or a programmable frontend. Agents provide presentation and semantic actions, not JavaScript or HTTP handlers.
5
+ ## Start
6
6
 
7
- ## CLI
8
-
9
- Node 22 or newer is required.
10
-
11
- ```bash
12
- npx letmeknow-cli
13
- ```
14
-
15
- The deployed service is used by default. Set `LETMEKNOW_URL` for local development:
7
+ Node.js 22.12 or newer is required.
16
8
 
17
9
  ```bash
18
- LETMEKNOW_URL=http://localhost:8787 npx letmeknow-cli
19
- ```
20
-
21
- stdin contains one compact JSON command per line. stdout contains one JSON event per line. Diagnostics go to stderr.
22
-
23
- ```bash
24
- npx letmeknow-cli --skill
25
- ```
26
-
27
- prints the agent instructions without connecting.
28
-
29
- ## Example
30
-
31
- Open a session:
32
-
33
- ```json
34
- {"type":"open","id":"open-1"}
10
+ npx letmeknow-cli ./workspace
35
11
  ```
36
12
 
37
- The CLI emits its temporary URL:
13
+ The CLI prints JSON lines to stdout. The first line contains the public preview URL:
38
14
 
39
15
  ```json
40
- {"type":"session","id":"open-1","url":"https://0123456789abcdef0123.letmeknow.dev/","expires_after_disconnect":600}
16
+ {"type":"ready","url":"https://0123456789abcdef0123.letmeknow.dev/"}
41
17
  ```
42
18
 
43
- The URL serves a trusted shell immediately. Send HTML and optional CSS with `render`:
19
+ Open that URL in one browser. The directory defaults to the current working directory. Set `LETMEKNOW_URL` to use another compatible relay:
44
20
 
45
- ```json
46
- {"type":"render","id":"render-1","body":"<h1>Search invoices</h1><form id=\"search\" action=\"search\" method=\"post\" data-lmk-target=\"results\"><label>Customer<input name=\"customer\" required></label><button>Search</button></form><section id=\"results\"><p>Enter a customer.</p></section>","css":"#results { margin-top: 2rem; }"}
47
- ```
48
-
49
- A connected browser receives the render immediately. The acknowledgement contains its revision:
50
-
51
- ```json
52
- {"type":"ack","id":"render-1","render_id":"b87438c2-4f1c-44bd-9875-6cc64370b8aa"}
53
- ```
54
-
55
- Submitting the form produces one normalized action:
56
-
57
- ```json
58
- {"type":"action","id":"2ee81a6b-1035-40a7-a90d-c1e02f426baa","render_id":"b87438c2-4f1c-44bd-9875-6cc64370b8aa","action_id":"search","form_id":"search","target_id":"results","trigger":{"id":null,"name":null,"value":null},"values":{"customer":"Acme Ltd"}}
21
+ ```bash
22
+ LETMEKNOW_URL=https://letmeknow.dev npx letmeknow-cli ./workspace
59
23
  ```
60
24
 
61
- Respond to that ID with HTML for the target's contents:
25
+ There are no `--host` or `--port` options because the CLI intentionally has no listening network socket. Diagnostics go to stderr. `--skill` prints the agent instructions without starting a session.
62
26
 
63
- ```json
64
- {"type":"response","id":"response-1","request_id":"2ee81a6b-1035-40a7-a90d-c1e02f426baa","body":"<table><tr><th>Invoice</th><th>Amount</th></tr><tr><td>INV-42</td><td>$800</td></tr></table>"}
65
- ```
66
-
67
- The browser inserts the fragment into `#results`. Without `data-lmk-target`, the response replaces the whole workspace.
27
+ ## File workflow
68
28
 
69
- ## HTML actions
29
+ The CLI does not receive file commands. The agent reads and writes the directory directly. Keep a normal Vite entry point such as `index.html`; JavaScript, CSS, images, and other Vite-supported files can be requested through the relay.
70
30
 
71
- ### Forms
31
+ When a watched file changes, the browser receives an update. HTML changes replace the current document body without a page reload and preserve form values, focus, selection, and scroll position. CSS links are refreshed without navigating. Changes to a different HTML route do not disturb the current page.
72
32
 
73
- Interactive forms use standard HTML:
33
+ An optional element can display submission status:
74
34
 
75
35
  ```html
76
- <form id="decision" action="decide" method="post">
77
- <label>Comment<textarea name="comment"></textarea></label>
78
- <button name="decision" value="approve" formaction="approve">Approve</button>
79
- <button name="decision" value="reject" formaction="reject">Reject</button>
80
- </form>
36
+ <p data-letmeknow-status aria-live="polite"></p>
81
37
  ```
82
38
 
83
- Forms require `method="post"`, a stable `id`, a relative action identifier, and meaningful control names. A submit button's `formaction` overrides the form's `action`. Native validation runs locally. `FormData(form, submitter)` is captured before controls are disabled, so selected controls and the clicked button are included. Repeated names become string arrays. File inputs are rejected.
39
+ ## Form submissions
84
40
 
85
- ### Standalone actions
86
-
87
- A button can invoke an action without a form:
41
+ Forms are submitted without navigation. GET and POST forms are sent through the relay to the CLI, which prints each submission as one JSON line on stdout. The agent can read that line and edit the folder in response.
88
42
 
89
43
  ```html
90
- <button type="button" data-lmk-action="refresh-status" data-lmk-target="status">Refresh</button>
44
+ <form id="decision" action="/decide" method="post">
45
+ <label>Comment <textarea name="comment"></textarea></label>
46
+ <button name="decision" value="approve">Approve</button>
47
+ <button name="decision" value="reject">Reject</button>
48
+ </form>
91
49
  ```
92
50
 
93
- Its event has `form_id: null`, empty `values`, and its optional `id`, `name`, and `value` in `trigger`.
94
-
95
- ### Targeted updates
96
-
97
- `data-lmk-target` accepts one bare element ID. All updates use `innerHTML`. There are no alternate swap modes or selector targets. The default target is `lmk-view`, the whole workspace.
98
-
99
- Typing, focusing, expanding `<details>`, validation, scrolling, and other local browser behavior produce no agent events.
100
-
101
- ## CSS
102
-
103
- `render` accepts page CSS in its `css` field. A `response` may include `css` to replace it; omitting `css` preserves it.
104
-
105
- Modern CSS is supported, including grid, flexbox, media queries, variables, transitions, and print styles. External stylesheets, `@import`, scripts, inline handlers, and inline `style` attributes are blocked. Local assets work in HTML and CSS.
106
-
107
- ## One browser client
108
-
109
- At most one browser is active for a session. The first browser receives a private credential stored in that tab's `sessionStorage`.
110
-
111
- - Reloads and reconnects reuse the credential.
112
- - A competing browser triggers at most one liveness probe every five seconds.
113
- - If the active browser answers, the claimant sees “This session is open elsewhere.”
114
- - After a disconnect, the previous credential has a five-second exclusive reconnect period.
115
- - After five seconds, either the old browser or a new claimant may connect; first connection wins.
116
- - Laptop sleep does not invalidate the credential.
117
-
118
- The server sends a connecting browser one canonical snapshot of the current HTML, CSS, render ID, and pending actions. It does not replay user actions. The browser restores same-tab drafts from `sessionStorage`; drafts do not transfer to another browser.
119
-
120
- The server keeps committed page state and assets for the producer session. A full `render` replaces the page and clears old pending actions. A targeted response updates the canonical page. Browser disconnect alone does not delete state.
121
-
122
- ## Assets
123
-
124
- `put` stores or replaces passive resources only under `/assets/`:
51
+ Submitting `Approve` prints an event like:
125
52
 
126
53
  ```json
127
- {"type":"put","id":"logo","path":"/assets/logo.png","content_type":"image/png","encoding":"base64","body":"iVBORw0KGgo..."}
54
+ {"type":"submit","id":"","method":"POST","action":"/decide","form_id":"decision","trigger":{"id":null,"name":"decision","value":"approve"},"values":{"comment":"Looks good","decision":"approve"}}
128
55
  ```
129
56
 
130
- Reference them relatively:
131
-
132
- ```html
133
- <img src="assets/logo.png" alt="Company logo">
134
- ```
135
-
136
- Assets answer only `GET` and `HEAD`. Each body is limited to 1 MiB. A session accepts 100 assets and 10 MiB decoded asset data. There is no `delete`; assets disappear with the session.
137
-
138
- ## Protocol
139
-
140
- Commands:
57
+ Repeated field names become arrays. Native browser validation still runs before a submission is sent. File inputs and cross-origin form actions are not supported. Forms can use a submitter's standard `formaction`, `formmethod`, and `name`/`value` attributes.
141
58
 
142
- - `open`: create the session; it must be first.
143
- - `render`: replace the committed HTML and CSS and push it to the browser.
144
- - `put`: store an asset under `/assets/`.
145
- - `response`: resolve one pending action with HTML and optional CSS.
146
- - `close`: destroy the session immediately.
59
+ The event ID identifies the submission. It is not a request/response handle: update the files and let the live preview show the result.
147
60
 
148
- Events:
61
+ ## Security
149
62
 
150
- - `session`: public URL and producer disconnect grace.
151
- - `action`: normalized form or standalone-button action.
152
- - `ack`: command completion.
153
- - `error`: invalid command or protocol state.
154
- - `closing`: explicit session destruction.
63
+ The preview URL is a bearer capability. The relay receives the served files and submitted values. Do not put secrets in the preview folder or submit credentials unless that is intentional. The folder is trusted executable code from the browser's perspective.
155
64
 
156
- Actions remain pending until `response`, a superseding full `render`, or session close. One form or overlapping target can be pending at a time; independent regions may proceed concurrently. Match responses by action ID.
65
+ The CLI's Vite configuration is disabled and its filesystem access is limited to the selected folder. The CLI itself still requires an outbound network connection to the relay. It does not accept inbound browser connections.
157
66
 
158
- HTML, CSS, asset, and action bodies are each limited to 1 MiB. Up to 32 actions may be pending.
67
+ ## Hosted compatibility
159
68
 
160
- ## Security and lifecycle
69
+ Existing clients using the older command protocol can still connect explicitly without a folder:
161
70
 
162
- HTML fragments are sanitized. Scripts, style elements, inline handlers, HTMX attributes, frames, active metadata, external form actions, and invalid LetMeKnow attributes are removed. CSP blocks arbitrary browser connections and external resources.
163
-
164
- The URL is a bearer secret. Share it only with the intended human. Browser and producer reconnect credentials remain private and never appear in protocol output.
165
-
166
- If the producer disconnects, the current page remains visible, drafts are preserved, and actions are disabled. The CLI can reconnect for ten minutes. Browser traffic does not extend that grace. `close` or producer-grace expiry deletes page state, credentials, pending actions, and assets.
71
+ ```bash
72
+ LETMEKNOW_URL=https://letmeknow.dev npx letmeknow-cli
73
+ ```
167
74
 
168
75
  ## Development
169
76
 
170
77
  ```bash
171
78
  npm install
172
- npm run dev
173
79
  npm test
80
+ npm run dev
174
81
  npm run deploy
175
82
  ```
83
+
84
+ `npm run dev` and `npm run deploy` operate the Cloudflare relay.
package/SKILL.md CHANGED
@@ -1,164 +1,100 @@
1
1
  ---
2
2
  name: letmeknow
3
- description: Show one human a temporary HTML and CSS workspace and handle normalized form or button actions through the LetMeKnow NDJSON CLI.
3
+ description: Give a human a live public preview of an agent-managed folder and receive browser form submissions as JSON lines.
4
4
  ---
5
5
 
6
6
  # LetMeKnow
7
7
 
8
- Use LetMeKnow when one human needs a temporary rich document, dashboard, report, preview, form, approval, quiz, table, or status view. The agent supplies semantic HTML and CSS. The browser sends only declared actions; typing and local UI state do not create events.
8
+ Use LetMeKnow when one human needs to inspect or interact with a temporary page, report, dashboard, approval form, quiz, table, or status view. The agent owns the files in a folder. The CLI runs Vite in middleware mode, connects to the public relay over an outbound WebSocket, and reports browser submissions on stdout.
9
9
 
10
- LetMeKnow is not a localhost proxy, persistent application, or arbitrary JavaScript environment.
10
+ The CLI does **not** listen on a local network port.
11
11
 
12
12
  ## Start
13
13
 
14
- Node.js 22 or newer is required.
14
+ Run the CLI as a long-lived child process with the folder you will edit:
15
15
 
16
16
  ```bash
17
- npx letmeknow-cli
17
+ npx letmeknow-cli ./workspace
18
18
  ```
19
19
 
20
- Run it as a long-lived child process. Write one compact JSON object per line to stdin, keep stdin open, and read one JSON event per line from stdout. Read stderr separately.
20
+ Node.js 22.12 or newer is required. Read stdout and stderr separately. Stdout is JSONL; the first event is:
21
21
 
22
22
  ```json
23
- {"type":"open","id":"open-1"}
23
+ {"type":"ready","url":"https://0123456789abcdef0123.letmeknow.dev/"}
24
24
  ```
25
25
 
26
- Wait for the session URL:
26
+ Open the public URL for the human. The directory defaults to the current working directory. Set `LETMEKNOW_URL` to use another compatible relay. There are no host or port options: the CLI intentionally has no listening socket.
27
27
 
28
- ```json
29
- {"type":"session","id":"open-1","url":"https://0123456789abcdef0123.letmeknow.dev/","expires_after_disconnect":600}
30
- ```
31
-
32
- The URL immediately serves a shell with a waiting message. One browser may use it at a time.
33
-
34
- ## Render HTML and CSS
35
-
36
- ```json
37
- {"type":"render","id":"render-1","body":"<h1>Search invoices</h1><form id=\"search\" action=\"search\" method=\"post\" data-lmk-target=\"results\"><label>Customer<input name=\"customer\" required></label><button>Search</button></form><section id=\"results\"><p>Enter a customer.</p></section>","css":"#results { margin-top: 2rem; }"}
38
- ```
39
-
40
- A connected browser receives the render immediately. Wait for its acknowledgement and retain the `render_id`:
41
-
42
- ```json
43
- {"type":"ack","id":"render-1","render_id":"b87438c2-4f1c-44bd-9875-6cc64370b8aa"}
44
- ```
28
+ Do not send file commands to stdin. Read and write the folder directly. Keep the process running while the human uses the page.
45
29
 
46
- Use semantic HTML: headings, sections, paragraphs, lists, tables, `dl`, forms, labels, controls, buttons, `details`, progress, and images. Do not include `<html>`, `<head>`, `<body>`, `<main id="lmk-view">`, scripts, style elements, inline handlers, inline `style`, iframes, or HTMX attributes.
30
+ ## Build the page
47
31
 
48
- The optional `css` field supports modern CSS, including grid, flexbox, media queries, variables, transitions, and print styles. External stylesheets, `@import`, and remote resources do not work. Use relative session assets.
32
+ Create an ordinary Vite page in the folder, usually `index.html`, plus any CSS, JavaScript, images, or other assets it needs. Use semantic HTML and accessible labels, headings, sections, tables, and controls.
49
33
 
50
- A full `render` intentionally replaces the current page, resets drafts, and cancels actions from the previous render. Use `response` rather than `render` after a user action.
34
+ The relay is live:
51
35
 
52
- ## Forms
36
+ - HTML changes update the current document body without a full page reload.
37
+ - Existing form values, focus, text selection, and scroll position are restored after an HTML update.
38
+ - CSS links are refreshed without navigating.
39
+ - Changes to another HTML route do not replace the current route.
53
40
 
54
- Interactive forms use standard HTML:
41
+ An optional status element gives the human feedback after a form submission:
55
42
 
56
43
  ```html
57
- <form id="decision" action="decide" method="post">
58
- <label>Reason<textarea name="reason" required></textarea></label>
59
- <button name="decision" value="approve" formaction="approve">Approve</button>
60
- <button name="decision" value="reject" formaction="reject">Reject</button>
61
- </form>
44
+ <p data-letmeknow-status aria-live="polite"></p>
62
45
  ```
63
46
 
64
- Rules:
65
-
66
- - Use `method="post"`.
67
- - Give each form a stable, unique `id`.
68
- - Give controls meaningful `name` values.
69
- - Use an action identifier containing letters, digits, `.`, `_`, `:`, or `-`.
70
- - A submit button's standard `formaction` may override the form action.
71
- - Native `required`, input types, ranges, and patterns validate locally.
47
+ ## Receive form submissions
72
48
 
73
- The runtime captures `FormData(form, submitter)` before disabling controls. Selected radios, checked boxes, ordinary controls, and the clicked submit button are included. Repeated names become string arrays. File inputs are rejected.
74
-
75
- ## Standalone actions
49
+ GET and POST forms are intercepted before navigation and sent through the relay to the CLI. Read stdout for a `submit` event:
76
50
 
77
51
  ```html
78
- <button type="button" data-lmk-action="refresh-status" data-lmk-target="status">
79
- Refresh
80
- </button>
81
- ```
82
-
83
- Use standalone actions for refresh, retry, cancel, generate, inspect, load-more, and export. Their events have `form_id: null`, empty `values`, and the button's optional `id`, `name`, and `value` in `trigger`.
84
-
85
- ## Whole and partial updates
86
-
87
- Responses replace the whole workspace by default. To update a region, put `data-lmk-target="element-id"` on the form or action button:
88
-
89
- ```html
90
- <form id="search" action="search" method="post" data-lmk-target="results">
91
- <input name="query">
92
- <button>Search</button>
52
+ <form id="search" action="/search" method="post">
53
+ <label>Query <input name="query" required></label>
54
+ <button name="scope" value="all">Search all</button>
93
55
  </form>
94
- <section id="results"></section>
95
56
  ```
96
57
 
97
- The target is one bare element ID without `#`. Every update uses `innerHTML`. A submit button may override its form's target.
98
-
99
- ## Handle actions
58
+ The event is:
100
59
 
101
60
  ```json
102
- {"type":"action","id":"2ee81a6b-1035-40a7-a90d-c1e02f426baa","render_id":"b87438c2-4f1c-44bd-9875-6cc64370b8aa","action_id":"search","form_id":"search","target_id":"results","trigger":{"id":null,"name":null,"value":null},"values":{"query":"quarterly report"}}
61
+ {"type":"submit","id":"","method":"POST","action":"/search","form_id":"search","trigger":{"id":null,"name":"scope","value":"all"},"values":{"query":"quarterly report","scope":"all"}}
103
62
  ```
104
63
 
105
- Use:
106
-
107
- - `id` to respond to this exact action.
108
- - `render_id` to identify the page revision that produced it.
109
- - `action_id` for user intent.
110
- - `form_id` and `target_id` for context.
111
- - `trigger` for the clicked button.
112
- - `values` for the submitted form snapshot.
113
-
114
- Validate actions and values. Treat values as untrusted and HTML-escape reflected text.
115
-
116
- Respond with HTML for the target contents:
117
-
118
- ```json
119
- {"type":"response","id":"response-1","request_id":"2ee81a6b-1035-40a7-a90d-c1e02f426baa","body":"<table><tr><th>Invoice</th><th>Amount</th></tr><tr><td>INV-42</td><td>$800</td></tr></table>"}
120
- ```
121
-
122
- A response may include `css` to replace the page CSS. Omitting it preserves the existing CSS:
123
-
124
- ```json
125
- {"type":"response","id":"response-2","request_id":"event-2","body":"<h1 class=\"success\">Approved</h1>","css":".success { color: green; }"}
126
- ```
64
+ Rules:
127
65
 
128
- Wait for the acknowledgement and its new `render_id`. An action remains pending until `response`, a superseding full `render`, or session close. Independent regions may be pending concurrently, so always match by action ID.
66
+ - Give interactive forms a stable, meaningful `id`.
67
+ - Give controls meaningful `name` values.
68
+ - Use normal relative or same-origin actions.
69
+ - Use `formaction` and `formmethod` on submitters when different buttons have different intents.
70
+ - Native `required`, input types, ranges, and patterns validate in the browser before the event is sent.
71
+ - Repeated names become string arrays.
72
+ - File inputs and cross-origin actions are not supported.
129
73
 
130
- ## One-browser behavior
74
+ The event ID identifies the submission. There is no response packet. Validate its values, edit the files, and let the live preview show the result. Do not write JSON commands to stdin.
131
75
 
132
- The first browser claims the session with a private credential. Reload and laptop wake reconnect automatically. A second browser cannot connect while the first is active.
76
+ ## Example response workflow
133
77
 
134
- Liveness is checked only when another browser tries to claim, at most once every five seconds. After a browser disconnect, its credential has five seconds of exclusive reconnect priority. After that, the first old or new browser to connect wins.
78
+ 1. Render the initial state in `index.html`.
79
+ 2. Wait for a `submit` event on stdout.
80
+ 3. Validate its `values` and `action`.
81
+ 4. Rewrite the relevant HTML or data file in the workspace.
82
+ 5. The browser updates in place through the relay.
135
83
 
136
- A reconnect receives one canonical snapshot of committed HTML, CSS, render ID, and pending actions. It does not replay old user actions. Same-tab drafts are restored from `sessionStorage`; drafts do not transfer during takeover.
84
+ Escape untrusted values before placing them in HTML. Treat browser input as untrusted even though the folder is local to the agent.
137
85
 
138
- ## Assets
86
+ ## Security
139
87
 
140
- ```json
141
- {"type":"put","id":"logo","path":"/assets/logo.png","content_type":"image/png","encoding":"base64","body":"iVBORw0KGgo..."}
142
- ```
88
+ The public URL is a bearer capability. The relay receives the served files and submitted values. Do not put secrets in the preview folder or submit credentials unless that is intentional. The folder is trusted executable code from the browser's perspective.
143
89
 
144
- Store resources only under `/assets/` and reference them relatively:
90
+ The CLI disables Vite config discovery and limits filesystem access to the selected folder. It makes outbound relay connections only; it does not accept inbound browser connections.
145
91
 
146
- ```html
147
- <img src="assets/logo.png" alt="Company logo">
148
- ```
92
+ ## Stop
149
93
 
150
- `encoding` is `utf8` by default or `base64` for binary data. Assets answer only `GET` and `HEAD`. There is no `delete`; another `put` replaces an asset.
94
+ Send `SIGINT` or `SIGTERM` to stop the CLI. The relay session expires after producer disconnect. `--skill` prints these instructions without starting a session.
151
95
 
152
- ## Close and lifecycle
96
+ Existing clients using the older hosted command protocol can still connect explicitly without a folder:
153
97
 
154
- ```json
155
- {"type":"close","id":"close-1"}
98
+ ```bash
99
+ LETMEKNOW_URL=https://letmeknow.dev npx letmeknow-cli
156
100
  ```
157
-
158
- Wait for `ack` and `closing`. Closing stdin only disconnects the producer.
159
-
160
- If the producer disconnects, the page remains visible, drafts remain, and actions are disabled. The CLI has ten minutes to reconnect. Browser traffic does not extend that period. Explicit close or expiry deletes HTML, CSS, credentials, actions, and assets.
161
-
162
- Limits: 1 MiB per HTML, CSS, asset, or action body; 100 assets; 10 MiB decoded asset storage; and 32 pending actions.
163
-
164
- Treat the URL as a bearer secret. Never expose either private reconnect credential.