@byokit/ui 0.0.0-stage → 0.7.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/CHANGELOG.md +76 -0
- package/LICENSE +202 -0
- package/README.md +362 -2
- package/dist/accounts.d.ts +148 -0
- package/dist/accounts.js +175 -0
- package/dist/approvals.d.ts +47 -0
- package/dist/approvals.js +63 -0
- package/dist/browser.d.ts +101 -0
- package/dist/browser.js +97 -0
- package/dist/connect.d.ts +92 -0
- package/dist/connect.js +74 -0
- package/dist/follow.d.ts +17 -0
- package/dist/follow.js +72 -0
- package/dist/herdr.d.ts +103 -0
- package/dist/herdr.js +88 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -0
- package/dist/key.d.ts +19 -0
- package/dist/key.js +12 -0
- package/dist/kits.d.ts +6 -0
- package/dist/kits.js +7 -0
- package/dist/link.d.ts +48 -0
- package/dist/link.js +63 -0
- package/dist/phase.d.ts +23 -0
- package/dist/phase.js +21 -0
- package/dist/route.d.ts +28 -0
- package/dist/route.js +118 -0
- package/dist/run.d.ts +95 -0
- package/dist/run.js +85 -0
- package/dist/steps.d.ts +25 -0
- package/dist/steps.js +16 -0
- package/dist/useAccounts.d.ts +48 -0
- package/dist/useAccounts.js +156 -0
- package/dist/useConnect.d.ts +18 -0
- package/dist/useConnect.js +17 -0
- package/dist/useKits.d.ts +62 -0
- package/dist/useKits.js +30 -0
- package/dist/useSignIn.d.ts +32 -0
- package/dist/useSignIn.js +38 -0
- package/dist/words.json +66 -0
- package/package.json +75 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.7.0 (2026-10-12)
|
|
6
|
+
|
|
7
|
+
- Dependency update: pins @byokit/accounts 0.22.0.
|
|
8
|
+
|
|
9
|
+
- FEAT: Add `useAccounts` and `useModelPicker` to `@byokit/ui` (with the `accountsSource`/`cliSource` one-line-per-member adapters): live rows with `refresh()`, a failing room leaving only its row `Room left unknown`, a `remove()` landing mid-refresh still dropping the row, and a picker whose `select()` marks the selection at once and keeps it across refreshes. The openclaw adapter stays pending O14.
|
|
10
|
+
- FEAT: Add `accountRows`, `rowsOf`, `nameSuggestions` and the `AccountsSource`, `AccountRow`, `Account` and `Room` types (also `@byokit/ui/accounts`, no React): one plain-words row per account (`ChatGPT · Work` / `Plus · 72% left this week`), honest words for every sign-in state, `low` at or below 20%, a `sign_in`/`wait` action, and untaken account-name suggestions from an email's local part that never put an email in a title.
|
|
11
|
+
- FEAT: `herdrTreeView` groups carry `project`, the workspace folder's own name without the tab label, so rows can name an agent (computer, project, agent) without internal tab labels.
|
|
12
|
+
- FEAT: Browser sign-in sheet and live panel views: `signInSheetView(request, { name, holder })` gives the sentence, buttons and chat chip for each request state; `signInsStore(oc)` keeps requests live from sign-in pings; `livePanelView(state)` says when to draw frames, take input, show "Reconnecting…" or ask to confirm an unexpected address. Typed structurally against `@byokit/openclaw` spec 5.17.
|
|
13
|
+
- FEAT: Add `connectView(routes, step)`, `connectStep`, `signInFor` (also `@byokit/ui/connect`, no React) and `useConnect`: every account route the kits list, grouped as Plans (offered by default), Pay per use, On this computer, Your own server, Cloud account, Other ways to connect, and Speech, search and media, each row with plain billing, method and readiness words. Billing decides the group, so no API, cloud, local, server or unknown-billing row is ever in Plans; key routes reuse `keyView`, sign-in routes hand `useSignIn` their row.
|
|
14
|
+
- FEAT: Add `modelPicker` and `bestMoveTarget` to `@byokit/ui/accounts` (no React, no Node): the run picker with the `Auto — most room left` row carrying the pick's own reason, one plain row per account with its model rows and the selection marked, unavailable rows with a typed why and `until`, Auto disabled with a typed reason when no account can be named, and the Auto-ordered account a running session should move to (`Move to Work` / `Couldn't move to Work`).
|
|
15
|
+
- FEAT: plain pairing words for the phone page: `connectedWords(hostName, url?, kind?)` renders the connected line with the route just dialled (`Connected to Kitchen computer - same Wi-Fi.`), `routeShort(route)` gives the inline label, and `pairErrorWords(error)` reads a failed pairing as expired, wrong-code or the link's own sentence from @byokit/link's error code.
|
|
16
|
+
- FEAT: `describeRoute` labels in plain words: `Same Wi-Fi`, `Temporary public link`, `Away from home` (this kit's relay address) and `Your own server`. No route, choice or status sentence carries an address, a protocol name or relay jargon.
|
|
17
|
+
- The PWA example's account card now runs on the shared views: `accountRows`/`modelPicker` from `@byokit/ui/accounts` draw the rows and the run picker (Auto carries its own reason, a signed-out pick shows `Sign in first` and runs nothing), and `bestMoveTarget`/`moveWords` suggest the roomier account when the chosen one is low.
|
|
18
|
+
- `@byokit/ui-core` is now published as `@byokit/ui`, with the same API and the same entries (`.`, `./phase`, `./route`, `./link`, `./kits`, `./steps`, `./connect`); its earlier versions are the `@byokit/ui-core` releases below. **Deprecated:** the `@byokit/ui-core` name; it stays published as a shim that re-exports this package through 0.7.x and is removed in 0.8.0.
|
|
19
|
+
- Accept OpenClaw's admitted/thinking progress frames structurally without changing the existing run reducer's rendered state.
|
|
20
|
+
|
|
21
|
+
- Dependency update: pins @byokit/accounts 0.21.0.
|
|
22
|
+
|
|
23
|
+
- Run views accept structured-output failures and display the kit’s plain validation message.
|
|
24
|
+
|
|
25
|
+
## 0.6.0 (2026-10-01)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
- API key (billed per use): framework-free `keyStep` and `keyView` entry-card states and plain billing/error words, without keeping a credential in view state.
|
|
30
|
+
|
|
31
|
+
## 0.5.0 (2026-10-01)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
- `routeChoices()`: every onboarding route (`tailscale`, `tailscale-direct`, `private`, `lan`, plus `cloudflare`
|
|
36
|
+
and `external`) in everyday words (`{ code, title, sentence, needs }`), in recommendation order. The one word
|
|
37
|
+
table reach's `recommend()` draws `sentence` and `needs` from.
|
|
38
|
+
|
|
39
|
+
## 0.4.0 (2026-09-30)
|
|
40
|
+
|
|
41
|
+
- `@byokit/ui-core/steps` (no React): `stepsView(steps)` turns the caller's own steps into the rows a stepper or
|
|
42
|
+
inline checks draw (`{ id, state: todo | checking | ok | failed, words, fix }`, same in a terminal and on a
|
|
43
|
+
phone); `stepsText(rows, { title })` draws the same rows as text lines for a terminal. Step ids stay
|
|
44
|
+
caller-defined, and a failed step without its own `fix` says "Try again."
|
|
45
|
+
|
|
46
|
+
## 0.3.0 (2026-09-30)
|
|
47
|
+
|
|
48
|
+
- FIX: pairing links parse on React Native, where `URL.canParse` is missing and `hostname` is empty for `ws://`:
|
|
49
|
+
`describeRoute` reads the scheme and host itself.
|
|
50
|
+
- `@byokit/ui-core/kits` (no React): view state for the runtime kits, typed structurally against
|
|
51
|
+
`@byokit/openclaw/device` and `@byokit/herdr/device` so neither kit is a dependency. Reducers `runStep`,
|
|
52
|
+
`approvalsStep` and `herdrStep`; views `runView` (the reply, the tool at work, and how a run ended in the kit's own
|
|
53
|
+
sentence), `approvalWords`, `herdrTreeView`, `agentIn` and `blockedView`; live stores `runStore`, `approvalsStore`
|
|
54
|
+
(listed once the event stream is open, dropped as each approval expires) and `herdrStore` (one `hd.events` stream
|
|
55
|
+
per device however many views watch it; a waiting agent's revision follows the tree, so an answer is never stale).
|
|
56
|
+
Stores reopen their stream soon after the link comes back (a stopped or refused link included, after its `retry()`)
|
|
57
|
+
and stop for good once the pairing is removed.
|
|
58
|
+
- React hooks `useRun`, `useApprovals`, `useHerdrTree` and `useBlocked` over those stores, for React and React Native.
|
|
59
|
+
- `qrMatrix(text, { border })` takes the quiet border in modules (2 unless given, as before), and `qrText(text,
|
|
60
|
+
{ border })` draws the same QR as half-block text rows for a terminal, so a setup script shows a code a phone can scan.
|
|
61
|
+
- `consentWords` and `pairingView` take an optional `device` (`phone`, or `browser`, which opens the pairing link
|
|
62
|
+
instead of scanning) and one app sentence (`detail`) after the title.
|
|
63
|
+
- `useSignIn` works without `start`: when something else starts the sign-in, the sheet only watches, and nothing
|
|
64
|
+
begins as it opens.
|
|
65
|
+
|
|
66
|
+
## 0.2.0
|
|
67
|
+
|
|
68
|
+
- `@byokit/ui-core/link`: `qrMatrix`, `consentWords`, `pairingView` and `linkWords` for pairing with `@byokit/link` in any UI.
|
|
69
|
+
|
|
70
|
+
## 0.1.1
|
|
71
|
+
|
|
72
|
+
- The Apache-2.0 LICENSE ships in the tarball.
|
|
73
|
+
|
|
74
|
+
## 0.1.0
|
|
75
|
+
|
|
76
|
+
- Headless sign-in state and plain-words copy for any UI.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,3 +1,363 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center">@byokit/ui</h1>
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://www.npmjs.com/package/@byokit/ui"><img alt="npm" src="https://img.shields.io/npm/v/@byokit/ui?style=flat&label=npm" /></a>
|
|
5
|
+
<a href="https://github.com/umeranjum17/byokit/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/umeranjum17/byokit/ci.yml?style=flat&branch=main" /></a>
|
|
6
|
+
<a href="LICENSE"><img alt="Apache 2.0" src="https://img.shields.io/badge/license-Apache--2.0-666?style=flat" /></a>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center"><strong>Headless sign-in, pairing and runtime-kit state, in plain words, for any UI.</strong><br/>
|
|
10
|
+
It turns what your back end reports into the phase to draw and the sentence to show: a "Sign in with …" sheet, a
|
|
11
|
+
pairing sheet, the link's status, the route a phone takes home, and a runtime kit's runs, approvals and agents. Your
|
|
12
|
+
app keeps its own look. Everything except the React hooks is framework-free.</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<img src="https://raw.githubusercontent.com/umeranjum17/byokit/main/docs/images/pwa-2-code.png" width="240" alt="A phone-width page titled Umer's AI plans, with the ChatGPT card reading 'Signing in to ChatGPT…' and 'On the ChatGPT page, type this code:', the code MOCK-10001 in large letters, 'Code expires in 14:59', and Open ChatGPT and Cancel buttons." />
|
|
16
|
+
<img src="https://raw.githubusercontent.com/umeranjum17/byokit/main/examples/herdr-kit/docs/2-compare.png" width="240" alt="A phone-width page titled 'Agents' with a 'Pair this phone' card reading 'Check your computer shows these two words, then say yes there.' above the words 'coast comet'." />
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center"><sub>Left: the <code>code</code> phase from <code>phaseOf()</code>, drawn by <a href="../../examples/pwa">examples/pwa</a> against the mock OpenAI sign-in. Right: <code>pairingView()</code>'s <code>compare</code> words, drawn by <a href="../../examples/herdr-kit">examples/herdr-kit</a>.</sub></p>
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm install @byokit/ui
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
[](https://www.npmjs.com/package/@byokit/ui) · [Latest release](https://github.com/umeranjum17/byokit/releases?q=ui-v) · [All releases](https://github.com/umeranjum17/byokit/releases)
|
|
28
|
+
|
|
29
|
+
Migrating from `@byokit/ui-core`? See the [deprecated shim's migration guide](../ui-core/README.md#migrate).
|
|
30
|
+
|
|
31
|
+
## Quickstart
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npm install @byokit/ui
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
React is an optional peer dependency, needed only for the hooks (`useSignIn`, `useRun`, `useApprovals`,
|
|
38
|
+
`useHerdrTree`, `useBlocked`, `useConnect`, `useAccounts`, `useModelPicker`).
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { phaseOf, stepOf } from '@byokit/ui/phase';
|
|
42
|
+
import { describeRoute } from '@byokit/ui/route';
|
|
43
|
+
import { consentWords, linkWords, pairingView, qrMatrix } from '@byokit/ui/link';
|
|
44
|
+
|
|
45
|
+
// What the back end reports: a sign-in waiting on a typed code.
|
|
46
|
+
const phase = phaseOf({ ready: false, signIn: { state: 'waiting', via: 'code', code: 'ABCD-1234' } });
|
|
47
|
+
console.log(phase, stepOf(phase));
|
|
48
|
+
console.log(phaseOf({ ready: true, work: true }));
|
|
49
|
+
console.log(phaseOf({ signIn: { state: 'failed', why: 'busy' } }));
|
|
50
|
+
|
|
51
|
+
console.log(describeRoute('wss://home.tail1234.ts.net/link'));
|
|
52
|
+
console.log(describeRoute('ws://100.101.102.103:7777'));
|
|
53
|
+
|
|
54
|
+
console.log(consentWords({ hostName: 'Kitchen computer', role: 'control' }));
|
|
55
|
+
console.log(pairingView({ phase: 'compare', hostName: 'Kitchen computer', words: 'coast comet' }));
|
|
56
|
+
console.log(linkWords('offline', 'Kitchen computer'));
|
|
57
|
+
console.log(qrMatrix('hello').length, 'rows');
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Output from running it with Node:
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
code 1
|
|
64
|
+
work
|
|
65
|
+
busy
|
|
66
|
+
Tailscale
|
|
67
|
+
Private network
|
|
68
|
+
Pair with Kitchen computer? This device will be able to see and change things on it, until you remove it there.
|
|
69
|
+
{
|
|
70
|
+
phase: 'compare',
|
|
71
|
+
title: 'Check Kitchen computer shows these two words, then say yes there.',
|
|
72
|
+
words: 'coast comet'
|
|
73
|
+
}
|
|
74
|
+
Can't reach Kitchen computer right now. This device keeps trying by itself.
|
|
75
|
+
25 rows
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
In React, `useSignIn` drives the whole sheet from three calls to your back end:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { useSignIn } from '@byokit/ui';
|
|
82
|
+
import type { AccountView } from '@byokit/ui';
|
|
83
|
+
|
|
84
|
+
// Your back end's routes; the hook only needs these three calls.
|
|
85
|
+
declare const api: {
|
|
86
|
+
account(): Promise<AccountView | null>;
|
|
87
|
+
signIn(body?: { via?: 'code'; fresh?: boolean }): Promise<unknown>;
|
|
88
|
+
cancel(): Promise<unknown>;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export function useChatGptSheet() {
|
|
92
|
+
const s = useSignIn({ read: () => api.account(), start: (b) => api.signIn(b), cancel: () => api.cancel() });
|
|
93
|
+
// s.phase: what to draw. s.code / s.url: what to show. s.start({ via: 'code' }): "Having trouble? Use a code instead".
|
|
94
|
+
// s.cancel(), s.close(), s.keepWork(): the sheet's buttons. Leave `start` out when something else starts the
|
|
95
|
+
// sign-in: the sheet then only watches, and nothing begins as it opens.
|
|
96
|
+
return s;
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## API at a glance
|
|
101
|
+
|
|
102
|
+
| Export | What it does |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `phaseOf(account, { offline, cancelled, keepWork })` | The sign-in phase to draw, from an `@byokit/accounts` `view()` plus whether the account is signed in |
|
|
105
|
+
| `stepOf(phase)` | Where the three-step progress bar ("Open", "Say yes", "Done") stands |
|
|
106
|
+
| `useSignIn({ read, start?, cancel, offline?, ms?, pinned? })` | React hook: starts the sign-in as the sheet opens, polls, and handles cancel, close, keep-work and "use a code instead"; without `start` it only watches and nothing begins |
|
|
107
|
+
| `describeRoute(url, kind?)` | Names the route a dial address takes, for a pairing or settings screen |
|
|
108
|
+
| `routeShort(route)` | The route as words inside a connected line (`same Wi-Fi`) |
|
|
109
|
+
| `connectedWords(hostName, url?, kind?)` | The connected line to render (`Connected to Kitchen computer - same Wi-Fi.`), plain with no route |
|
|
110
|
+
| `routeChoices()` | Every onboarding route in everyday words (`{ code, title, sentence, needs }`), in recommendation order |
|
|
111
|
+
| `qrMatrix(text, { border }?)` | The pairing QR as rows of dark and light modules, with its quiet border (2 unless given) |
|
|
112
|
+
| `qrText(text, { border }?)` | The same QR as half-block text rows for a terminal |
|
|
113
|
+
| `consentWords({ hostName, role, device?, detail? })` | The question before pairing |
|
|
114
|
+
| `pairingView({ phase, hostName?, words?, error?, device?, detail? })` | The pairing sheet's title and words for each phase |
|
|
115
|
+
| `linkWords(status, hostName)` | The link's status in one sentence |
|
|
116
|
+
| `pairErrorWords(error)` | A failed pairing as `expired`, `wrong` or the link's own sentence |
|
|
117
|
+
| `stepsView(steps)` | The stepper rows to draw, from the caller's own step ids and states |
|
|
118
|
+
| `stepsText(rows, { title })` | The same rows as text lines for a terminal |
|
|
119
|
+
| `runStore(oc)`, `runView(state, { words, name })` | One run: the reply streaming in, the tool at work, how it ended in the kit's words |
|
|
120
|
+
| `approvalsStore(oc)`, `approvalWords(a, words, helper)` | The approvals waiting for a yes, live, each gone when answered or expired |
|
|
121
|
+
| `herdrStore(hd)`, `herdrTreeView(tree)`, `blockedView(state)` | Herdr's agents by where they run, and the ones waiting for an answer, live |
|
|
122
|
+
| `signInsStore(oc)`, `signInSheetView(request, { name, holder })`, `livePanelView(state)` | The browser sign-in sheet (sentence, buttons, chat chip) and live panel for the OpenClaw kit's handoff |
|
|
123
|
+
| `useRun`, `useApprovals`, `useHerdrTree`, `useBlocked` | React hooks over those stores |
|
|
124
|
+
| `connectView(routes, step?)`, `connectStep(step, action)` | Every account route the kits list, grouped by how the person pays, with the picked row and its key card |
|
|
125
|
+
| `signInFor(view, { read, start?, cancel })`, `useConnect({ routes, read, start?, cancel })` | `useSignIn`'s options for the picked sign-in row; the hook holds the step |
|
|
126
|
+
| `accountRows(accounts, room, now)`, `rowsOf(source, now)`, `nameSuggestions(email, taken, provider?)` | One plain-words row per account (`ChatGPT · Work` / `Plus · 72% left this week`), the honest words for every sign-in state, and untaken account-name suggestions |
|
|
127
|
+
| `useAccounts(source)`, `useModelPicker(source, initial?)` | React hooks: the rows live with `refresh()` and `remove()` (a failing room leaves only its row unknown; a mid-refresh remove still drops it), and the run picker with `select()` keeping the caller's selection across refreshes; `accountsSource(accounts, member, room?)` and `cliSource(cli, room?)` fill the source with one line per member (the openclaw adapter stays pending O14) |
|
|
128
|
+
| Types | `Phase`, `SignInView`, `AccountView`, `UseSignIn`, `Route`, `RouteChoice`, `RouteCode`, `PairPhase`, `Role`, `DeviceKind`, `LinkStatus`; `RouteKind` from `@byokit/ui/route`; `Account`, `AccountRow`, `AccountAction`, `AccountsSource`, `Billing`, `Room`, `RoomSpan`, `SignInState` from `@byokit/ui/accounts` |
|
|
129
|
+
|
|
130
|
+
Entry points: `@byokit/ui` (everything, including the React hooks), `@byokit/ui/phase`,
|
|
131
|
+
`@byokit/ui/route`, `@byokit/ui/link`, `@byokit/ui/kits`, `@byokit/ui/steps`,
|
|
132
|
+
`@byokit/ui/connect` and `@byokit/ui/accounts` (no React dependency).
|
|
133
|
+
|
|
134
|
+
## Sign-in phases
|
|
135
|
+
|
|
136
|
+
`phaseOf()` turns what your back end reports (an `@byokit/accounts` `view()` plus whether the account is signed in)
|
|
137
|
+
into the phase to draw:
|
|
138
|
+
|
|
139
|
+
| Phase | Meaning |
|
|
140
|
+
|---|---|
|
|
141
|
+
| `opening` | getting the provider's page or a code ready |
|
|
142
|
+
| `waiting` | say yes on the provider's page |
|
|
143
|
+
| `code` | type this code on the provider's page instead |
|
|
144
|
+
| `done` | signed in, and it works |
|
|
145
|
+
| `work` | signed in with a work plan: offer a personal one |
|
|
146
|
+
| `cancelled` | declined on the page, or cancelled here |
|
|
147
|
+
| `busy` | something else on this computer is signing in |
|
|
148
|
+
| `expired` | the page or code ran out of time |
|
|
149
|
+
| `failed` | anything else, with a plain sentence |
|
|
150
|
+
| `offline` | the home computer isn't answering |
|
|
151
|
+
|
|
152
|
+
`phaseOf()` and `stepOf()` also come from `@byokit/ui/phase`, which has no React dependency.
|
|
153
|
+
|
|
154
|
+
## Routes
|
|
155
|
+
|
|
156
|
+
`describeRoute(url, kind?)` (also `@byokit/ui/route`, no React) names the route a dial address takes, for a
|
|
157
|
+
pairing or settings screen. Pass `tailscale`, `direct`, `private`, or `lan` when known (map
|
|
158
|
+
[`@byokit/discover`](../discover)'s `tailscale-direct` to `direct`); without provenance, 100.64/10 reads as a private
|
|
159
|
+
network rather than assumed to be Tailscale, and the other home ranges read as same Wi-Fi.
|
|
160
|
+
|
|
161
|
+
`routeChoices()` (same entry, no React) is the one word table for the onboarding route list: `{ code, title,
|
|
162
|
+
sentence, needs }` per route in recommendation order, a superset of `@byokit/discover`'s `Via` (it also covers the
|
|
163
|
+
temporary public link and your own server, which `reach()` there does not probe). `recommend()` from the same package
|
|
164
|
+
takes `sentence` and `needs` from here and only adds availability.
|
|
165
|
+
|
|
166
|
+
`connectedWords(hostName, url?, kind?)` (same entry) is the connected line a consumer renders: the computer with
|
|
167
|
+
the route just dialled (`Connected to Kitchen computer - same Wi-Fi.`), or plain (`Connected to Kitchen computer.`)
|
|
168
|
+
when there is no route to name.
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import { routeChoices } from '@byokit/ui/route';
|
|
172
|
+
|
|
173
|
+
console.log(routeChoices().map((c) => `${c.title}: ${c.sentence}`));
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Connect an account
|
|
177
|
+
|
|
178
|
+
`connectView(routes, step?)` (also `@byokit/ui/connect`, no React) draws the "Connect an AI account" list from
|
|
179
|
+
the routes the kits discover (D18 in `docs/runtime-kits.md`): pass `@byokit/accounts` `routes(host)` and
|
|
180
|
+
`@byokit/openclaw` `routes(facts)` together, unfiltered. Every route is listed, unavailable ones too, in these groups
|
|
181
|
+
(empty ones left out):
|
|
182
|
+
|
|
183
|
+
| Group | Rows | Offered by default |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| Plans | subscription and plan-key routes the kit offers by default | yes, when ready |
|
|
186
|
+
| Pay per use | API-billed routes | no, only when picked |
|
|
187
|
+
| On this computer | local runtimes | no |
|
|
188
|
+
| Your own server | endpoint routes; the person says how it is paid for | no |
|
|
189
|
+
| Cloud account | cloud credentials | no |
|
|
190
|
+
| Other ways to connect | free or unknown billing ("Billing set by …") | no |
|
|
191
|
+
| Speech, search and media | non-chat services | no |
|
|
192
|
+
|
|
193
|
+
Billing decides the group, never the route's `offer` alone: an API, cloud, local, server, free or unknown row can
|
|
194
|
+
never sit among Plans. Each row has `billingWords` ("Uses your ChatGPT plan", "Charged per use to your OpenAI
|
|
195
|
+
account"), `method` ("Sign in with a code") and `status` (a sentence per readiness), plus `offered`, `ready` and
|
|
196
|
+
`does`: `signin`, `key`, `setup` (the app's own cloud, local or server form) or `unavailable`. The view copies only
|
|
197
|
+
these fields, so a credential on a route never reaches it; nothing in it names a provider.
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
import { connectStep, connectView, signInFor } from '@byokit/ui/connect';
|
|
201
|
+
import { routes } from '@byokit/accounts';
|
|
202
|
+
|
|
203
|
+
declare const api: { account(id: string): Promise<null>; signIn(id: string, body?: object): Promise<void>; cancel(id: string): Promise<void> };
|
|
204
|
+
const step = connectStep({ at: 'list' }, { type: 'pick', key: 'accounts/openai:code' });
|
|
205
|
+
const view = connectView(routes({ platform: 'browser' }), step);
|
|
206
|
+
// view.groups → the list; view.chosen.row.does === 'signin' → draw the sign-in sheet:
|
|
207
|
+
const options = signInFor(view, { read: (row) => api.account(row.id), start: (row, body) => api.signIn(row.id, body), cancel: (row) => api.cancel(row.id) });
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
A key route's `chosen.key` is the existing `keyView` card, labelled for its billing ("Key from your plan" or "Key
|
|
211
|
+
(charged per use)"); submit the input straight to the kit and dispatch only `{ type: 'result', result }`.
|
|
212
|
+
`useConnect({ routes, read, start?, cancel })` holds the step in React and returns the view with `pick`, `back`,
|
|
213
|
+
`act` and `signIn`, the options to pass to `useSignIn` in a sheet keyed by `chosen.row.key`.
|
|
214
|
+
|
|
215
|
+
## Account rows
|
|
216
|
+
|
|
217
|
+
`accountRows(accounts, room, now)` (also `@byokit/ui/accounts`, no React) turns a list of accounts and a room reading
|
|
218
|
+
into one plain row each: the provider and the person's own name for the account (`ChatGPT · Work`), then the plan and
|
|
219
|
+
the room left (`Plus · 72% left this week`). A row also carries `state`, `billing`, `low` (room left at or below 20%,
|
|
220
|
+
exactly 20 included, never true for an unknown room) and, when it helps, an `action`: `sign_in` for a signed-out or
|
|
221
|
+
lapsed account, `wait` plus the `until` refill time for a resting one. `accountRows` reads only the fields it names,
|
|
222
|
+
so a credential on an account never reaches a row. A row that is not ready shows its own honest words ("Checking…",
|
|
223
|
+
"Cooling down", "Not signed in", "Sign in again", "Not in your plan") instead of the room.
|
|
224
|
+
|
|
225
|
+
`AccountsSource` is the shape an app fills from its own back end — `accounts()` and `room(account)` — and `rowsOf(source, now)`
|
|
226
|
+
awaits both and builds the same rows; `@byokit/accounts` `list()` and a room reading fit it with one line per member.
|
|
227
|
+
`nameSuggestions(email, taken, provider?)` offers up to three untaken account names from an email's local part
|
|
228
|
+
(deduped case-insensitively against `taken`), falling back to the provider's name; the email itself is never offered.
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
import { accountRows, nameSuggestions, type Account, type Room } from '@byokit/ui/accounts';
|
|
232
|
+
|
|
233
|
+
declare const list: () => readonly Account[];
|
|
234
|
+
declare const reading: (account: Account) => Room;
|
|
235
|
+
const rows = accountRows(list(), reading, Date.now());
|
|
236
|
+
// rows → [{ title: 'ChatGPT · Work', detail: 'Plus · 72% left this week', state: 'ready', low: false, ... }]
|
|
237
|
+
console.log(nameSuggestions('umer@example.com', rows.map((r) => r.title)));
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
In React, `useAccounts(source)` keeps those rows live (`refresh()` re-reads; `remove(id)` removes through
|
|
241
|
+
the back end and drops the row even mid-refresh), and `useModelPicker(source)` keeps the run picker live
|
|
242
|
+
(`select()` marks the selection at once and keeps it across refreshes). `accountsSource(accounts, member,
|
|
243
|
+
room?)` fills the source from a live `@byokit/accounts` member and `cliSource(cli, room?)` from a live
|
|
244
|
+
`cliAccounts()` kit, each with one line per member; the openclaw adapter stays pending O14:
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
import { accountsSource, cliSource, useAccounts, useModelPicker } from '@byokit/ui';
|
|
248
|
+
import type { Accounts } from '@byokit/accounts';
|
|
249
|
+
import { cliAccounts } from '@byokit/accounts/cli';
|
|
250
|
+
import type { Account, Room } from '@byokit/ui/accounts';
|
|
251
|
+
|
|
252
|
+
declare const accounts: Accounts<any, number>;
|
|
253
|
+
declare const cli: ReturnType<typeof cliAccounts>;
|
|
254
|
+
declare const reading: (account: Account) => Room;
|
|
255
|
+
const fromMember = accountsSource(accounts, 1, reading);
|
|
256
|
+
const fromCli = cliSource(cli, reading);
|
|
257
|
+
|
|
258
|
+
export function useAccountList() {
|
|
259
|
+
return useAccounts(fromCli); // { rows, ready, error, refresh, remove }
|
|
260
|
+
}
|
|
261
|
+
export function useRunPicker() {
|
|
262
|
+
return useModelPicker(fromMember); // { rows, picker, selection, ready, error, select, refresh, remove }
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Pairing words
|
|
267
|
+
|
|
268
|
+
Pairing with `@byokit/pair` (published as `@byokit/link` up to 0.8.x), from `@byokit/ui/link` (no React either):
|
|
269
|
+
|
|
270
|
+
- `qrMatrix(offer.text, { border }?)`: the pairing QR as rows of dark and light modules, with its quiet border
|
|
271
|
+
(2 unless given), to draw in any UI.
|
|
272
|
+
- `qrText(offer.text, { border }?)`: the same QR as half-block text rows, so `console.log` in a setup script shows a
|
|
273
|
+
code a phone can scan.
|
|
274
|
+
- `consentWords({ hostName, role, device?, detail? })`: the question before pairing ("Pair with Kitchen computer? This device
|
|
275
|
+
will be able to see and change things on it, until you remove it there."). `device` names the pairing side when it
|
|
276
|
+
isn't the app (`phone`, or `browser` for the web pairing link); `detail` adds one app sentence after.
|
|
277
|
+
- `pairingView({ phase, hostName, words, error, device?, detail? })`: scan, compare the two words, waiting for a yes, paired, failed.
|
|
278
|
+
- `linkWords(status, hostName)`: the link's status in one sentence.
|
|
279
|
+
|
|
280
|
+
## Stepper rows
|
|
281
|
+
|
|
282
|
+
`stepsView()` (also `@byokit/ui/steps`, no React) turns the caller's own steps into the rows a stepper or
|
|
283
|
+
inline checks draw, so a terminal and a phone show the same words. Each step keeps the id its caller chose, the
|
|
284
|
+
state it is in (`todo`, `checking`, `ok`, `failed`), the plain words to show, and the one next action when it
|
|
285
|
+
failed. `stepsText()` draws the same rows as text lines for a terminal; a phone draws the rows in its own list.
|
|
286
|
+
A failed step without its own `fix` says "Try again."
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
import { stepsText, stepsView } from '@byokit/ui/steps';
|
|
290
|
+
|
|
291
|
+
// Onboarding "Checking this computer": ids are the app's own, states come from its probes.
|
|
292
|
+
const rows = stepsView([
|
|
293
|
+
{ id: 'runner', state: 'ok', label: 'Agent runner', detail: 'ready' },
|
|
294
|
+
{ id: 'agents', state: 'ok', label: 'Agents found', detail: 'Claude Code (signed in) · Codex (needs sign-in)' },
|
|
295
|
+
{ id: 'wifi', state: 'ok', label: 'Wi-Fi', detail: 'this computer is at 192.168.1.144' },
|
|
296
|
+
{ id: 'tailscale', state: 'todo', label: 'Tailscale', detail: 'not installed on this computer' },
|
|
297
|
+
]);
|
|
298
|
+
console.log(stepsText(rows, { title: 'Checking this computer' }));
|
|
299
|
+
|
|
300
|
+
// "Setting up" reuses the same rows with its own ids; a failed check names its fix:
|
|
301
|
+
console.log(stepsView([
|
|
302
|
+
{ id: 'serve', state: 'failed', label: 'muxr', detail: 'not answering', fix: "muxr didn't start. Press r to retry." },
|
|
303
|
+
])[0]);
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Output from running it with Node:
|
|
307
|
+
|
|
308
|
+
```text
|
|
309
|
+
Checking this computer
|
|
310
|
+
✓ Agent runner: ready
|
|
311
|
+
✓ Agents found: Claude Code (signed in) · Codex (needs sign-in)
|
|
312
|
+
✓ Wi-Fi: this computer is at 192.168.1.144
|
|
313
|
+
○ Tailscale: not installed on this computer
|
|
314
|
+
{
|
|
315
|
+
id: 'serve',
|
|
316
|
+
state: 'failed',
|
|
317
|
+
words: 'muxr: not answering',
|
|
318
|
+
fix: "muxr didn't start. Press r to retry."
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Runtime kits' state
|
|
323
|
+
|
|
324
|
+
Runtime kits' state, from `@byokit/ui/kits` (no React), typed to fit `@byokit/openclaw/device` and
|
|
325
|
+
`@byokit/herdr/device` as they are:
|
|
326
|
+
|
|
327
|
+
- Runs: `runStore(oc)` with `send(message)` and `stop()`; `runView(state, { words, name })` gives the reply so far, the
|
|
328
|
+
tool at work and, for a failed run, the kit's sentence (`words` from `@byokit/openclaw/device`, `name` "ChatGPT").
|
|
329
|
+
OpenClaw's `started` and `thinking` frames are accepted without changing the reply, tools or rendered state;
|
|
330
|
+
the running phase begins at `send`, not Gateway acceptance.
|
|
331
|
+
- Approvals: `approvalsStore(oc)` keeps the waiting list live (each leaves when answered anywhere or when it expires);
|
|
332
|
+
`approvalWords(approval, words, 'Your helper')` asks the question.
|
|
333
|
+
- Herdr: `herdrStore(hd)` keeps the tree and the agents waiting for an answer live from one stream;
|
|
334
|
+
`herdrTreeView(tree)` groups the agents by where they run, with the status the kit's `agentWords` takes;
|
|
335
|
+
`blockedView(state)` names each waiting agent.
|
|
336
|
+
- Browser sign-in (`@byokit/openclaw` spec 5.17): `signInsStore(oc)` keeps the requests live from sign-in pings;
|
|
337
|
+
`signInSheetView(request, { name, holder })` gives the sentence (a kit word key and its values), the buttons and the
|
|
338
|
+
chat chip, which reads "entered" until a verified sign-in; `livePanelView(state)` says when to draw frames, take
|
|
339
|
+
input, show "Reconnecting…", or ask to confirm an address the person didn't expect. Watching asks the helper nothing.
|
|
340
|
+
- Each store is `{ get, subscribe }`: draw from `get()` in any UI; the first `subscribe` starts following, the last
|
|
341
|
+
unsubscribe stops. `runStep`, `approvalsStep` and `herdrStep` are the reducers underneath.
|
|
342
|
+
|
|
343
|
+
In React or React Native: `useRun(oc, { words, name })`, `useApprovals(oc)`, `useHerdrTree(hd)` and `useBlocked(hd)`,
|
|
344
|
+
with the device client made once (`useMemo(() => herdrDevice(link), [link])`).
|
|
345
|
+
|
|
346
|
+
## Links
|
|
347
|
+
|
|
348
|
+
- [byokit](../../README.md): the monorepo and its other packages, including
|
|
349
|
+
[`@byokit/accounts`](../accounts) and [`@byokit/pair`](../pair).
|
|
350
|
+
- Examples: [`examples/pwa`](../../examples/pwa) (sign-in phases in a browser),
|
|
351
|
+
[`examples/herdr-kit`](../../examples/herdr-kit) (pairing words and `herdrStore` from a phone browser),
|
|
352
|
+
[`examples/expo`](../../examples/expo) (React Native).
|
|
353
|
+
- [CHANGELOG.md](CHANGELOG.md)
|
|
354
|
+
|
|
355
|
+
## License
|
|
356
|
+
|
|
357
|
+
Apache-2.0. See [LICENSE](LICENSE) and [NOTICE](../../NOTICE).
|
|
358
|
+
|
|
359
|
+
`@byokit/ui/kits` also exports `keyStep(state, action)` and `keyView(state, words)` for an explicit
|
|
360
|
+
**API key (billed per use)** entry card. States are `entry`, `checking`, `ok`, `invalid`, `not_included`;
|
|
361
|
+
actions are `submit`, `edit`, and `result` with the kit's add-key outcome. Supply the OpenClaw kit's `words`
|
|
362
|
+
function for the label and sentences. These views never contain the entered key; submit it directly to the host
|
|
363
|
+
and clear the input afterwards.
|