letmeknow-cli 0.3.1 → 0.4.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 +45 -50
- package/SKILL.md +40 -43
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# LetMeKnow
|
|
2
2
|
|
|
3
|
-
LetMeKnow gives
|
|
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.
|
|
4
4
|
|
|
5
5
|
It is not a localhost proxy or a programmable frontend. Agents provide presentation and semantic actions, not JavaScript or HTTP handlers.
|
|
6
6
|
|
|
@@ -20,12 +20,12 @@ LETMEKNOW_URL=http://localhost:8787 npx letmeknow-cli
|
|
|
20
20
|
|
|
21
21
|
stdin contains one compact JSON command per line. stdout contains one JSON event per line. Diagnostics go to stderr.
|
|
22
22
|
|
|
23
|
-
Print the agent instructions without connecting:
|
|
24
|
-
|
|
25
23
|
```bash
|
|
26
24
|
npx letmeknow-cli --skill
|
|
27
25
|
```
|
|
28
26
|
|
|
27
|
+
prints the agent instructions without connecting.
|
|
28
|
+
|
|
29
29
|
## Example
|
|
30
30
|
|
|
31
31
|
Open a session:
|
|
@@ -40,13 +40,13 @@ The CLI emits its temporary URL:
|
|
|
40
40
|
{"type":"session","id":"open-1","url":"https://0123456789abcdef0123.letmeknow.dev/","expires_after_disconnect":600}
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
The URL
|
|
43
|
+
The URL serves a trusted shell immediately. Send HTML and optional CSS with `render`:
|
|
44
44
|
|
|
45
45
|
```json
|
|
46
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
47
|
```
|
|
48
48
|
|
|
49
|
-
A
|
|
49
|
+
A connected browser receives the render immediately. The acknowledgement contains its revision:
|
|
50
50
|
|
|
51
51
|
```json
|
|
52
52
|
{"type":"ack","id":"render-1","render_id":"b87438c2-4f1c-44bd-9875-6cc64370b8aa"}
|
|
@@ -55,16 +55,16 @@ A successful render receives a revision:
|
|
|
55
55
|
Submitting the form produces one normalized action:
|
|
56
56
|
|
|
57
57
|
```json
|
|
58
|
-
{"type":"action","id":"
|
|
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"}}
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
Respond with HTML for the target's contents:
|
|
61
|
+
Respond to that ID with HTML for the target's contents:
|
|
62
62
|
|
|
63
63
|
```json
|
|
64
|
-
{"type":"response","id":"response-1","request_id":"
|
|
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
65
|
```
|
|
66
66
|
|
|
67
|
-
The
|
|
67
|
+
The browser inserts the fragment into `#results`. Without `data-lmk-target`, the response replaces the whole workspace.
|
|
68
68
|
|
|
69
69
|
## HTML actions
|
|
70
70
|
|
|
@@ -74,54 +74,50 @@ Interactive forms use standard HTML:
|
|
|
74
74
|
|
|
75
75
|
```html
|
|
76
76
|
<form id="decision" action="decide" method="post">
|
|
77
|
-
<label>
|
|
78
|
-
Comment
|
|
79
|
-
<textarea name="comment"></textarea>
|
|
80
|
-
</label>
|
|
77
|
+
<label>Comment<textarea name="comment"></textarea></label>
|
|
81
78
|
<button name="decision" value="approve" formaction="approve">Approve</button>
|
|
82
79
|
<button name="decision" value="reject" formaction="reject">Reject</button>
|
|
83
80
|
</form>
|
|
84
81
|
```
|
|
85
82
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
- `method="post"`
|
|
89
|
-
- A stable form `id`
|
|
90
|
-
- A relative action identifier such as `search`, `approve`, or `invoice:inspect`
|
|
91
|
-
- Meaningful control `name` values
|
|
92
|
-
|
|
93
|
-
A submit button's standard `formaction` overrides the form's `action`. Native `required`, input types, ranges, and patterns validate locally. `FormData(form, submitter)` is captured before the form is disabled, so selected controls and the clicked submit button are included. Repeated names become string arrays. File inputs are rejected.
|
|
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.
|
|
94
84
|
|
|
95
85
|
### Standalone actions
|
|
96
86
|
|
|
97
87
|
A button can invoke an action without a form:
|
|
98
88
|
|
|
99
89
|
```html
|
|
100
|
-
<button type="button" data-lmk-action="refresh-status" data-lmk-target="status">
|
|
101
|
-
Refresh
|
|
102
|
-
</button>
|
|
90
|
+
<button type="button" data-lmk-action="refresh-status" data-lmk-target="status">Refresh</button>
|
|
103
91
|
```
|
|
104
92
|
|
|
105
|
-
Its event has `form_id: null`, empty `values`, and
|
|
93
|
+
Its event has `form_id: null`, empty `values`, and its optional `id`, `name`, and `value` in `trigger`.
|
|
106
94
|
|
|
107
95
|
### Targeted updates
|
|
108
96
|
|
|
109
|
-
`data-lmk-target` accepts one element ID
|
|
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.
|
|
110
98
|
|
|
111
|
-
|
|
112
|
-
<button data-lmk-action="load-more" data-lmk-target="results">Load more</button>
|
|
113
|
-
<section id="results"></section>
|
|
114
|
-
```
|
|
99
|
+
Typing, focusing, expanding `<details>`, validation, scrolling, and other local browser behavior produce no agent events.
|
|
115
100
|
|
|
116
|
-
|
|
101
|
+
## CSS
|
|
117
102
|
|
|
118
|
-
|
|
103
|
+
`render` accepts page CSS in its `css` field. A `response` may include `css` to replace it; omitting `css` preserves it.
|
|
119
104
|
|
|
120
|
-
|
|
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
|
|
121
108
|
|
|
122
|
-
|
|
109
|
+
At most one browser is active for a session. The first browser receives a private credential stored in that tab's `sessionStorage`.
|
|
123
110
|
|
|
124
|
-
|
|
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.
|
|
125
121
|
|
|
126
122
|
## Assets
|
|
127
123
|
|
|
@@ -131,45 +127,44 @@ Normal modern CSS is supported, including grid, flexbox, media queries, variable
|
|
|
131
127
|
{"type":"put","id":"logo","path":"/assets/logo.png","content_type":"image/png","encoding":"base64","body":"iVBORw0KGgo..."}
|
|
132
128
|
```
|
|
133
129
|
|
|
134
|
-
Reference them relatively
|
|
130
|
+
Reference them relatively:
|
|
135
131
|
|
|
136
132
|
```html
|
|
137
133
|
<img src="assets/logo.png" alt="Company logo">
|
|
138
134
|
```
|
|
139
135
|
|
|
140
|
-
Assets answer only `GET` and `HEAD`. Each body is limited to 1 MiB. A session accepts
|
|
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.
|
|
141
137
|
|
|
142
138
|
## Protocol
|
|
143
139
|
|
|
144
|
-
|
|
140
|
+
Commands:
|
|
145
141
|
|
|
146
142
|
- `open`: create the session; it must be first.
|
|
147
|
-
- `render`:
|
|
143
|
+
- `render`: replace the committed HTML and CSS and push it to the browser.
|
|
148
144
|
- `put`: store an asset under `/assets/`.
|
|
149
|
-
- `response`:
|
|
145
|
+
- `response`: resolve one pending action with HTML and optional CSS.
|
|
150
146
|
- `close`: destroy the session immediately.
|
|
151
147
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
### Events
|
|
148
|
+
Events:
|
|
155
149
|
|
|
156
|
-
- `session`:
|
|
157
|
-
- `action`:
|
|
150
|
+
- `session`: public URL and producer disconnect grace.
|
|
151
|
+
- `action`: normalized form or standalone-button action.
|
|
152
|
+
- `cancelled`: the browser stopped waiting for an action.
|
|
158
153
|
- `ack`: command completion.
|
|
159
154
|
- `error`: invalid command or protocol state.
|
|
160
155
|
- `closing`: explicit session destruction.
|
|
161
156
|
|
|
162
|
-
|
|
157
|
+
Actions remain pending for at most five minutes. One form or overlapping target can be pending at a time; independent regions may proceed concurrently. Match responses by action ID.
|
|
163
158
|
|
|
164
|
-
HTML, CSS, asset, and action bodies are each limited to 1 MiB. Up to 32
|
|
159
|
+
HTML, CSS, asset, and action bodies are each limited to 1 MiB. Up to 32 actions may be pending.
|
|
165
160
|
|
|
166
161
|
## Security and lifecycle
|
|
167
162
|
|
|
168
|
-
HTML fragments are sanitized. Scripts, style elements, inline
|
|
163
|
+
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.
|
|
169
164
|
|
|
170
|
-
The URL is a bearer secret. Share it only with the intended human.
|
|
165
|
+
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.
|
|
171
166
|
|
|
172
|
-
|
|
167
|
+
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.
|
|
173
168
|
|
|
174
169
|
## Development
|
|
175
170
|
|
package/SKILL.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: letmeknow
|
|
3
|
-
description: Show
|
|
3
|
+
description: Show one human a temporary HTML and CSS workspace and handle normalized form or button actions through the LetMeKnow NDJSON CLI.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# LetMeKnow
|
|
7
7
|
|
|
8
|
-
Use LetMeKnow when
|
|
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.
|
|
9
9
|
|
|
10
10
|
LetMeKnow is not a localhost proxy, persistent application, or arbitrary JavaScript environment.
|
|
11
11
|
|
|
@@ -19,8 +19,6 @@ npx letmeknow-cli
|
|
|
19
19
|
|
|
20
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.
|
|
21
21
|
|
|
22
|
-
Open first:
|
|
23
|
-
|
|
24
22
|
```json
|
|
25
23
|
{"type":"open","id":"open-1"}
|
|
26
24
|
```
|
|
@@ -31,25 +29,25 @@ Wait for the session URL:
|
|
|
31
29
|
{"type":"session","id":"open-1","url":"https://0123456789abcdef0123.letmeknow.dev/","expires_after_disconnect":600}
|
|
32
30
|
```
|
|
33
31
|
|
|
34
|
-
The URL immediately serves a shell with a waiting message.
|
|
32
|
+
The URL immediately serves a shell with a waiting message. One browser may use it at a time.
|
|
35
33
|
|
|
36
34
|
## Render HTML and CSS
|
|
37
35
|
|
|
38
|
-
Send an HTML fragment and optional page CSS:
|
|
39
|
-
|
|
40
36
|
```json
|
|
41
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; }"}
|
|
42
38
|
```
|
|
43
39
|
|
|
44
|
-
A browser
|
|
40
|
+
A connected browser receives the render immediately. Wait for its acknowledgement and retain the `render_id`:
|
|
45
41
|
|
|
46
42
|
```json
|
|
47
43
|
{"type":"ack","id":"render-1","render_id":"b87438c2-4f1c-44bd-9875-6cc64370b8aa"}
|
|
48
44
|
```
|
|
49
45
|
|
|
50
|
-
Use semantic HTML
|
|
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.
|
|
47
|
+
|
|
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.
|
|
51
49
|
|
|
52
|
-
|
|
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.
|
|
53
51
|
|
|
54
52
|
## Forms
|
|
55
53
|
|
|
@@ -66,29 +64,27 @@ Interactive forms use standard HTML:
|
|
|
66
64
|
Rules:
|
|
67
65
|
|
|
68
66
|
- Use `method="post"`.
|
|
69
|
-
- Give
|
|
67
|
+
- Give each form a stable, unique `id`.
|
|
70
68
|
- Give controls meaningful `name` values.
|
|
71
|
-
- Use
|
|
69
|
+
- Use an action identifier containing letters, digits, `.`, `_`, `:`, or `-`.
|
|
72
70
|
- A submit button's standard `formaction` may override the form action.
|
|
73
71
|
- Native `required`, input types, ranges, and patterns validate locally.
|
|
74
72
|
|
|
75
|
-
The runtime captures `FormData(form, submitter)` before disabling
|
|
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.
|
|
76
74
|
|
|
77
75
|
## Standalone actions
|
|
78
76
|
|
|
79
|
-
Use a button when an action does not need a form:
|
|
80
|
-
|
|
81
77
|
```html
|
|
82
78
|
<button type="button" data-lmk-action="refresh-status" data-lmk-target="status">
|
|
83
79
|
Refresh
|
|
84
80
|
</button>
|
|
85
81
|
```
|
|
86
82
|
|
|
87
|
-
|
|
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`.
|
|
88
84
|
|
|
89
85
|
## Whole and partial updates
|
|
90
86
|
|
|
91
|
-
|
|
87
|
+
Responses replace the whole workspace by default. To update a region, put `data-lmk-target="element-id"` on the form or action button:
|
|
92
88
|
|
|
93
89
|
```html
|
|
94
90
|
<form id="search" action="search" method="post" data-lmk-target="results">
|
|
@@ -98,63 +94,62 @@ By default, a response replaces the contents of the whole workspace. To update a
|
|
|
98
94
|
<section id="results"></section>
|
|
99
95
|
```
|
|
100
96
|
|
|
101
|
-
The target is one bare element ID without `#`. Every update uses `innerHTML
|
|
97
|
+
The target is one bare element ID without `#`. Every update uses `innerHTML`. A submit button may override its form's target.
|
|
102
98
|
|
|
103
99
|
## Handle actions
|
|
104
100
|
|
|
105
|
-
A completed interaction produces one normalized event:
|
|
106
|
-
|
|
107
101
|
```json
|
|
108
|
-
{"type":"action","id":"
|
|
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"}}
|
|
109
103
|
```
|
|
110
104
|
|
|
111
105
|
Use:
|
|
112
106
|
|
|
113
|
-
- `id` to respond to this exact
|
|
114
|
-
- `
|
|
115
|
-
- `render_id` to identify the interface revision that produced it.
|
|
107
|
+
- `id` to respond to this exact action.
|
|
108
|
+
- `render_id` to identify the page revision that produced it.
|
|
116
109
|
- `action_id` for user intent.
|
|
117
|
-
- `form_id` and `target_id` for
|
|
118
|
-
- `trigger`
|
|
119
|
-
- `values` for the
|
|
110
|
+
- `form_id` and `target_id` for context.
|
|
111
|
+
- `trigger` for the clicked button.
|
|
112
|
+
- `values` for the submitted form snapshot.
|
|
120
113
|
|
|
121
|
-
Validate
|
|
114
|
+
Validate actions and values. Treat values as untrusted and HTML-escape reflected text.
|
|
122
115
|
|
|
123
|
-
Respond with HTML for the target
|
|
116
|
+
Respond with HTML for the target contents:
|
|
124
117
|
|
|
125
118
|
```json
|
|
126
|
-
{"type":"response","id":"response-1","request_id":"
|
|
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>"}
|
|
127
120
|
```
|
|
128
121
|
|
|
129
|
-
A response may include `css` to replace the page CSS. Omitting
|
|
122
|
+
A response may include `css` to replace the page CSS. Omitting it preserves the existing CSS:
|
|
130
123
|
|
|
131
124
|
```json
|
|
132
|
-
{"type":"response","id":"response-2","request_id":"event-2","body":"<h1 class=\"success\">Approved</h1>","css":".success { color: green;
|
|
125
|
+
{"type":"response","id":"response-2","request_id":"event-2","body":"<h1 class=\"success\">Approved</h1>","css":".success { color: green; }"}
|
|
133
126
|
```
|
|
134
127
|
|
|
135
|
-
Wait for the acknowledgement
|
|
128
|
+
Wait for the acknowledgement and its new `render_id`. Actions time out after five minutes. A `cancelled` event means the browser stopped waiting; do not respond to that action afterward. Independent regions may be pending concurrently, so always match by action ID.
|
|
136
129
|
|
|
137
|
-
One
|
|
130
|
+
## One-browser behavior
|
|
138
131
|
|
|
139
|
-
|
|
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.
|
|
140
133
|
|
|
141
|
-
|
|
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.
|
|
135
|
+
|
|
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.
|
|
137
|
+
|
|
138
|
+
## Assets
|
|
142
139
|
|
|
143
140
|
```json
|
|
144
141
|
{"type":"put","id":"logo","path":"/assets/logo.png","content_type":"image/png","encoding":"base64","body":"iVBORw0KGgo..."}
|
|
145
142
|
```
|
|
146
143
|
|
|
147
|
-
|
|
144
|
+
Store resources only under `/assets/` and reference them relatively:
|
|
148
145
|
|
|
149
146
|
```html
|
|
150
147
|
<img src="assets/logo.png" alt="Company logo">
|
|
151
148
|
```
|
|
152
149
|
|
|
153
|
-
`encoding` is `utf8` by default or `base64` for binary data. Assets answer only `GET` and `HEAD`. There is no `delete`;
|
|
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.
|
|
154
151
|
|
|
155
|
-
## Close and
|
|
156
|
-
|
|
157
|
-
Destroy the session when finished:
|
|
152
|
+
## Close and lifecycle
|
|
158
153
|
|
|
159
154
|
```json
|
|
160
155
|
{"type":"close","id":"close-1"}
|
|
@@ -162,6 +157,8 @@ Destroy the session when finished:
|
|
|
162
157
|
|
|
163
158
|
Wait for `ack` and `closing`. Closing stdin only disconnects the producer.
|
|
164
159
|
|
|
165
|
-
|
|
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; 32 pending actions; five minutes per action.
|
|
166
163
|
|
|
167
|
-
|
|
164
|
+
Treat the URL as a bearer secret. Never expose either private reconnect credential.
|