@oharness/server 0.9.3 → 0.10.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.
Files changed (59) hide show
  1. package/dist/i18n.js +354 -62
  2. package/dist/i18n.js.map +1 -1
  3. package/dist/main.js +4 -0
  4. package/dist/main.js.map +1 -1
  5. package/dist/page/client/account.js +430 -23
  6. package/dist/page/client/account.js.map +1 -1
  7. package/dist/page/client/base.js +20 -1
  8. package/dist/page/client/base.js.map +1 -1
  9. package/dist/page/client/composer.js +693 -54
  10. package/dist/page/client/composer.js.map +1 -1
  11. package/dist/page/client/computer.d.ts +27 -0
  12. package/dist/page/client/computer.js +358 -0
  13. package/dist/page/client/computer.js.map +1 -0
  14. package/dist/page/client/dialogs.js +93 -6
  15. package/dist/page/client/dialogs.js.map +1 -1
  16. package/dist/page/client/events.js +94 -7
  17. package/dist/page/client/events.js.map +1 -1
  18. package/dist/page/client/files.js +86 -1
  19. package/dist/page/client/files.js.map +1 -1
  20. package/dist/page/client/index.js +4 -0
  21. package/dist/page/client/index.js.map +1 -1
  22. package/dist/page/client/panels.js +10 -6
  23. package/dist/page/client/panels.js.map +1 -1
  24. package/dist/page/client/routines.js +296 -41
  25. package/dist/page/client/routines.js.map +1 -1
  26. package/dist/page/client/shell.js +187 -51
  27. package/dist/page/client/shell.js.map +1 -1
  28. package/dist/page/client/state.js +273 -19
  29. package/dist/page/client/state.js.map +1 -1
  30. package/dist/page/client/tasks.d.ts +8 -0
  31. package/dist/page/client/tasks.js +293 -0
  32. package/dist/page/client/tasks.js.map +1 -0
  33. package/dist/page/client/transcript.js +137 -5
  34. package/dist/page/client/transcript.js.map +1 -1
  35. package/dist/page/client/usage.js +1 -11
  36. package/dist/page/client/usage.js.map +1 -1
  37. package/dist/page/style.js +568 -60
  38. package/dist/page/style.js.map +1 -1
  39. package/dist/page.js +90 -11
  40. package/dist/page.js.map +1 -1
  41. package/dist/prefs.d.ts +14 -0
  42. package/dist/prefs.js +8 -1
  43. package/dist/prefs.js.map +1 -1
  44. package/dist/remote-crypto.d.ts +78 -0
  45. package/dist/remote-crypto.js +216 -0
  46. package/dist/remote-crypto.js.map +1 -0
  47. package/dist/remote.d.ts +103 -0
  48. package/dist/remote.js +498 -0
  49. package/dist/remote.js.map +1 -0
  50. package/dist/server.d.ts +70 -0
  51. package/dist/server.js +1291 -49
  52. package/dist/server.js.map +1 -1
  53. package/dist/starters.d.ts +92 -0
  54. package/dist/starters.js +213 -0
  55. package/dist/starters.js.map +1 -0
  56. package/dist/turn-shots.d.ts +27 -0
  57. package/dist/turn-shots.js +71 -0
  58. package/dist/turn-shots.js.map +1 -0
  59. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","sourceRoot":"","sources":["../../src/page/style.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA;EAC7B,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4kGV,CAAC"}
1
+ {"version":3,"file":"style.js","sourceRoot":"","sources":["../../src/page/style.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,0EAA0E;AAC1E,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA;EAC7B,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwkHV,CAAC"}
package/dist/page.js CHANGED
@@ -83,14 +83,29 @@ ${ANALYTICS_STUB}
83
83
  because it is ⌘K on one machine and Ctrl K on the next. -->
84
84
  <!-- Home is the start page: a fresh session in the project on screen,
85
85
  the same thing New session does, at the top of the places.
86
- Lucide house on the 16 grid. -->
87
- <button class="nav-item" id="nav-home" type="button"><svg class="ico" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 6.67 8 2l6 4.67v6.66a1.33 1.33 0 0 1-1.33 1.34H3.33A1.33 1.33 0 0 1 2 13.33Z"/><path d="M6 14.67V8h4v6.67"/></svg><span data-t="nav.home"></span></button>
86
+ Lucide house (the current one) on the 16 grid: a rounded roof and a
87
+ door that stops short of the eaves. The older house's sharp peak
88
+ and full-height door were the one hard-cornered shape in a rail of
89
+ rounded ones. -->
90
+ <button class="nav-item" id="nav-home" type="button"><svg class="ico" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 14V9.33a.67.67 0 0 0-.67-.66H6.67a.67.67 0 0 0-.67.66V14"/><path d="M2 6.67a1.33 1.33 0 0 1 .47-1.02l4.67-4a1.33 1.33 0 0 1 1.72 0l4.67 4A1.33 1.33 0 0 1 14 6.67v6a1.33 1.33 0 0 1-1.33 1.33H3.33A1.33 1.33 0 0 1 2 12.67Z"/></svg><span data-t="nav.home"></span></button>
88
91
  <button class="nav-item" id="nav-search" type="button"><svg class="ico" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="7.33" cy="7.33" r="5.33"/><path d="m14 14-2.87-2.87"/></svg><span data-t="nav.search"></span><span class="count" id="palette-hint"></span></button>
89
92
  <!-- Artifacts sit above Models because they belong to the work, not to the
90
93
  configuration: this row answers "where did that chart go", which is a
91
- question asked mid-task, while the three below it are settings. -->
92
- <button class="nav-item" id="nav-artifacts" type="button"><svg class="ico" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="2.67" width="8" height="6" rx="1"/><rect x="6" y="7.33" width="8" height="6" rx="1"/></svg><span data-t="nav.artifacts"></span><span class="count" id="artifact-count"></span></button>
93
- <button class="nav-item" id="nav-sessions" type="button"><svg class="ico" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13.33 10a1.33 1.33 0 0 1-1.33 1.33H4.67L2 14V3.33A1.33 1.33 0 0 1 3.33 2H12a1.33 1.33 0 0 1 1.33 1.33Z"/><path d="M5.33 5.33h5.34"/><path d="M5.33 8h3.34"/></svg><span data-t="nav.sessions"></span></button>
94
+ question asked mid-task, while the three below it are settings.
95
+ Lucide shapes — things that were made. Two stacked rectangles read
96
+ as "copy" or "windows", and beside Sessions' two stacked bubbles
97
+ the pair were one icon drawn twice. -->
98
+ <button class="nav-item" id="nav-artifacts" type="button"><svg class="ico" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5.53 6.67a.47.47 0 0 1-.42-.72L7.6 2a.47.47 0 0 1 .8-.03l2.47 3.97a.47.47 0 0 1-.38.73Z"/><rect x="2" y="9.33" width="4.67" height="4.67" rx=".67"/><circle cx="11.67" cy="11.67" r="2.33"/></svg><span data-t="nav.artifacts"></span><span class="count" id="artifact-count"></span></button>
99
+ <!-- Lucide messages-square: two conversations. One bubble with text
100
+ lines read as "a message", and its lines blurred at 16px. -->
101
+ <button class="nav-item" id="nav-sessions" type="button"><svg class="ico" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.33 6a1.33 1.33 0 0 1-1.33 1.33H4L1.33 10V2.67a1.33 1.33 0 0 1 1.34-1.34H8a1.33 1.33 0 0 1 1.33 1.34Z"/><path d="M12 6h1.33a1.33 1.33 0 0 1 1.34 1.33v7.34L12 12H8a1.33 1.33 0 0 1-1.33-1.33V10"/></svg><span data-t="nav.sessions"></span></button>
102
+ <!-- Usage, which Settings also reaches: what the account spent is looked
103
+ up often enough to earn a place of its own. Lucide chart-column-big:
104
+ a bar chart is what "usage" looks like everywhere else, and bars
105
+ drawn as columns rather than strokes still read as bars at 16px —
106
+ the thin-stroke chart's short bar was a dot, and a gauge was too
107
+ abstract to name the page. -->
108
+ <button class="nav-item" id="nav-usage" type="button"><svg class="ico" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 2v10.67A1.33 1.33 0 0 0 3.33 14H14"/><rect x="10" y="3.33" width="2.67" height="8" rx=".67"/><rect x="4.67" y="6.67" width="2.67" height="4.67" rx=".67"/></svg><span data-t="nav.usage"></span></button>
94
109
  <button class="nav-item" id="nav-models" type="button"><svg class="ico" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 5.33a1.33 1.33 0 0 0-.67-1.15L8.67 1.51a1.33 1.33 0 0 0-1.33 0L2.67 4.18A1.33 1.33 0 0 0 2 5.33v5.33a1.33 1.33 0 0 0 .67 1.15l4.67 2.67a1.33 1.33 0 0 0 1.33 0l4.67-2.67A1.33 1.33 0 0 0 14 10.67Z"/><path d="m2.2 4.67 5.8 3.33 5.8-3.33"/><path d="M8 14.67V8"/></svg><span data-t="nav.models"></span><span class="count" id="model-count"></span></button>
95
110
  <button class="nav-item" id="nav-routines" type="button"><svg class="ico" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="8" cy="8" r="6.67"/><path d="M8 4v4l2.67 1.33"/></svg><span data-t="nav.routines"></span><span class="count" id="routine-count"></span></button>
96
111
  <button class="nav-item" id="nav-settings" type="button"><svg class="ico" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12.67 4.67h-6"/><path d="M9.33 11.33H3.33"/><circle cx="4.67" cy="4.67" r="2"/><circle cx="11.33" cy="11.33" r="2"/></svg><span data-t="nav.settings"></span></button>
@@ -107,7 +122,13 @@ ${ANALYTICS_STUB}
107
122
  <span class="chev"></span>
108
123
  </button>
109
124
  <div id="side-new">
110
- <button class="nav-item" id="new" type="button"><svg class="ico" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2H3.33a1.33 1.33 0 0 0-1.33 1.33v9.33a1.33 1.33 0 0 0 1.33 1.33h9.33a1.33 1.33 0 0 0 1.33-1.33V8"/><path d="M12.25 1.75a.67.67 0 0 1 2 2l-6.01 6.01a1.33 1.33 0 0 1-.57.34l-1.92.56a.33.33 0 0 1-.41-.41l.56-1.92a1.33 1.33 0 0 1 .34-.57z"/></svg><span data-t="nav.new"></span></button>
125
+ <!-- A soft square left open at the corner the pen comes in through, and a
126
+ plain capsule for the pen: no nib facet, which at 16px was a
127
+ smudge rather than a detail. The search row under it draws the
128
+ same lens as the rail's Search, as a bare icon at the row's end: a
129
+ second full row read as a second thing to do, and it is not. -->
130
+ <button class="nav-item" id="new" type="button"><svg class="ico" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7.33 2.67H5A2.33 2.33 0 0 0 2.67 5v6A2.33 2.33 0 0 0 5 13.33h6A2.33 2.33 0 0 0 13.33 11V8.67"/><path d="M12.1 2.9a1.27 1.27 0 0 1 1.8 1.8l-4.6 4.6a1.6 1.6 0 0 1-.72.4l-1.85.47.47-1.85a1.6 1.6 0 0 1 .4-.72Z"/></svg><span data-t="nav.new"></span></button>
131
+ <button id="side-search" type="button" data-t-title="nav.searchSessions" data-t-aria-label="nav.searchSessions"><svg class="ico-sm" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="7.33" cy="7.33" r="5.33"/><path d="m14 14-2.87-2.87"/></svg></button>
111
132
  </div>
112
133
  <div id="sessions"></div>
113
134
  <!-- Pinned under the session list, where an account belongs and where
@@ -130,6 +151,21 @@ ${ANALYTICS_STUB}
130
151
  <div id="split">
131
152
  <div id="main">
132
153
  <div id="tabs"></div>
154
+ <!-- The window Computer use is working in, live, floating over the
155
+ conversation while a turn works in it (see client/computer.ts).
156
+ Folded, it is the pill under it. -->
157
+ <div id="computer-float" hidden>
158
+ <!-- Over the picture, not above it: the app's name as a pill on the
159
+ left, the controls as round buttons on the right. -->
160
+ <div id="computer-head">
161
+ <span id="computer-label"><span id="computer-live" hidden><i></i></span><span id="computer-where"></span></span>
162
+ <button id="computer-grow" type="button" aria-pressed="false" data-t-title="computer.grow" data-t-aria-label="computer.grow"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 2.67h3.33V6"/><path d="M6 13.33H2.67V10"/><path d="m13.33 2.67-4 4"/><path d="m2.67 13.33 4-4"/></svg></button>
163
+ <button id="computer-stop" type="button" data-t-title="computer.stop" data-t-aria-label="computer.stop"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><rect x="4" y="4" width="8" height="8" rx="1.5"/></svg></button>
164
+ <button id="computer-hide" type="button" data-t-title="computer.hide" data-t-aria-label="computer.hide"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 8h8"/></svg></button>
165
+ </div>
166
+ <div id="computer-body"></div>
167
+ </div>
168
+ <button id="computer-pill" type="button" hidden data-t-title="computer.showPane"><i></i><span id="computer-pill-where"></span></button>
133
169
  <div id="log">
134
170
  <div id="empty">
135
171
  <div class="empty-brand">
@@ -147,9 +183,9 @@ ${ANALYTICS_STUB}
147
183
  </div>
148
184
  <h1 data-t="empty.title"></h1>
149
185
  <p><span data-t="empty.connected"></span> <span id="empty-cwd" class="mono"></span></p>
150
- <!-- Filled by paintStarters(): the chips are drawn at boot rather than
151
- written here, so the invitation differs from one session to the
152
- next instead of asking the same three questions forever. -->
186
+ <!-- Filled by paintStarters(): three rotating everyday offers for a
187
+ project with no memory, three written from its notes once it has
188
+ some. -->
153
189
  <div id="starters"></div>
154
190
  </div>
155
191
  </div>
@@ -164,9 +200,25 @@ ${ANALYTICS_STUB}
164
200
  rather than in it: what is in the box is the message being written,
165
201
  and a row sharing that surface reads as part of it. -->
166
202
  <div id="queued"></div>
203
+ <!-- What the message carries — pictures, clips, files, folders — as cards
204
+ above the box, not chips inside it (see renderTray). -->
205
+ <div id="tray"></div>
167
206
  <div class="composer" id="composer-box">
168
207
  <!-- The top edge is a grab handle: drag it to resize the field. -->
169
208
  <div id="grip" role="separator" aria-orientation="horizontal" data-t-title="composer.resize"></div>
209
+ <!-- Dictation, floating over the box's top edge by the mic: listening
210
+ (the voice as bars, the time, cancel and done), then transcribing
211
+ and tidying, or what went wrong. -->
212
+ <div id="dictate-pop" class="dictate-pop" hidden role="status" aria-live="polite">
213
+ <span class="dp-dot" aria-hidden="true"></span>
214
+ <span class="dp-spin" aria-hidden="true"></span>
215
+ <span class="dp-text"></span>
216
+ <span class="dp-time"></span>
217
+ <canvas class="dp-wave" width="168" height="32" aria-hidden="true"></canvas>
218
+ <button type="button" class="dp-btn dp-cancel" data-t-title="dictate.cancel"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7"/></svg></button>
219
+ <button type="button" class="dp-btn dp-done" data-t-title="dictate.done"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3.5 8.5l3 3 6-7"/></svg></button>
220
+ <button type="button" class="dp-btn dp-close" data-t-title="dictate.close"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7"/></svg></button>
221
+ </div>
170
222
  <div id="attachments"></div>
171
223
  <!-- A textarea draws its own glyphs and cannot style a range of them, so
172
224
  the chip behind a mention is painted on a layer underneath that
@@ -206,6 +258,7 @@ ${ANALYTICS_STUB}
206
258
  <div class="menu" hidden></div>
207
259
  </div>
208
260
  <span class="composer-spacer"></span>
261
+ <button id="mic" class="icon-btn mic-btn" type="button" aria-pressed="false" data-t-title="composer.dictate"><svg class="mic-idle" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5.75" y="1.75" width="4.5" height="8" rx="2.25"/><path d="M3.25 7.5a4.75 4.75 0 0 0 9.5 0M8 12.25V14.5"/></svg><svg class="mic-stop" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><rect x="4.5" y="4.5" width="7" height="7" rx="2"/></svg></button>
209
262
  <button id="stop" class="primary" type="button" hidden data-t-title="composer.stop"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><rect x="4" y="4" width="8" height="8" rx="1.5"/></svg></button>
210
263
  <button id="send" class="primary" type="submit" data-t-title="composer.send"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 13V3.4M3.6 7.8 8 3.2l4.4 4.6"/></svg></button>
211
264
  </div>
@@ -221,7 +274,7 @@ ${ANALYTICS_STUB}
221
274
  <span id="cost"></span>
222
275
  <span id="tokens"></span>
223
276
  <span id="cache" data-t-title="usage.cacheHint"></span>
224
- <div id="ctx" data-t-title="usage.context"><i></i></div>
277
+ <button id="ctx" type="button" aria-haspopup="dialog" aria-expanded="false"></button>
225
278
  <span id="ctxtext"></span>
226
279
  </div>
227
280
  </form>
@@ -261,7 +314,10 @@ ${ANALYTICS_STUB}
261
314
  </span>
262
315
  </div>
263
316
  <div id="explorer-body">
264
- <div id="explorer-tree" role="tree"></div>
317
+ <div id="explorer-side">
318
+ <label id="explorer-find"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5 14 14"/></svg><input id="explorer-q" type="search" autocomplete="off" spellcheck="false" data-t-placeholder="files.search" data-t-aria-label="files.search"></label>
319
+ <div id="explorer-tree" role="tree"></div>
320
+ </div>
265
321
  <div id="explorer-split" role="separator" tabindex="0" data-t-aria-label="files.resize"></div>
266
322
  <div id="explorer-view"></div>
267
323
  </div>
@@ -426,6 +482,29 @@ ${ANALYTICS_STUB}
426
482
  <!-- Importing a sign-in from the person's own browser. The confirmation is
427
483
  the only path that reads anything out of that browser, so it names the
428
484
  browser, the sites and what the agent can do with them afterwards. -->
485
+ <!-- Feedback: what kind, what happened, sent as the signed-in account. -->
486
+ <dialog id="feedback-dialog">
487
+ <div class="cn-head">
488
+ <div class="cn-naming">
489
+ <div class="cn-title" data-t="feedback.title"></div>
490
+ </div>
491
+ </div>
492
+ <div class="cn-body">
493
+ <div id="fb-kinds" class="fb-kinds" role="radiogroup"></div>
494
+ <textarea id="fb-message" rows="7" maxlength="5000"></textarea>
495
+ <!-- Attachments as square tiles, the last one the way to add another. -->
496
+ <div id="fb-files" class="fb-media"></div>
497
+ <input id="fb-file" type="file" multiple hidden
498
+ accept="image/png,image/jpeg,image/gif,image/webp,video/mp4,video/webm,video/quicktime">
499
+ <!-- Empty until there is something to say: an error, or that it was sent. -->
500
+ <p id="fb-status" class="fb-note"></p>
501
+ </div>
502
+ <div class="dlg-actions">
503
+ <button id="fb-cancel" type="button" class="quiet" data-t="models.cancel"></button>
504
+ <button id="fb-send" type="button" class="approve" data-t="feedback.send"></button>
505
+ </div>
506
+ </dialog>
507
+
429
508
  <dialog id="browser-import-dialog">
430
509
  <div class="cn-head">
431
510
  <div class="cn-naming">
package/dist/page.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"page.js","sourceRoot":"","sources":["../src/page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD;;;;;;;;;;;;;;GAcG;AAGH;;;;;;;GAOG;AACH,MAAM,OAAO,GAAG;;;;;yCAKyB,CAAC;AAE1C,MAAM,CAAC,MAAM,IAAI,GAAG;;;;;;;4CAOwB,kBAAkB,CAAC,OAAO,CAAC;SAC9D,KAAK;EACZ,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAuaK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;qBACvB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;EAC1C,aAAa;EACb,UAAU;EACV,gBAAgB;;;QAGV,CAAC"}
1
+ {"version":3,"file":"page.js","sourceRoot":"","sources":["../src/page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD;;;;;;;;;;;;;;GAcG;AAGH;;;;;;;GAOG;AACH,MAAM,OAAO,GAAG;;;;;yCAKyB,CAAC;AAE1C,MAAM,CAAC,MAAM,IAAI,GAAG;;;;;;;4CAOwB,kBAAkB,CAAC,OAAO,CAAC;SAC9D,KAAK;EACZ,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsfK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;qBACvB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;EAC1C,aAAa;EACb,UAAU;EACV,gBAAgB;;;QAGV,CAAC"}
package/dist/prefs.d.ts CHANGED
@@ -40,6 +40,20 @@ export interface Prefs {
40
40
  * routines fire when they are due rather than when the lid is next opened.
41
41
  */
42
42
  keepAwake?: boolean;
43
+ /**
44
+ * Whether a phone on this account may reach this machine through the relay
45
+ * (remote.ts). Absent is off: a machine is not offered to a phone until
46
+ * somebody at it says so.
47
+ */
48
+ remote?: boolean;
49
+ /**
50
+ * Whether Computer Use may take the real mouse and keyboard when an action
51
+ * cannot run in the background. `never` keeps it strictly in the background;
52
+ * absent is "ask", through the permission prompt each time.
53
+ */
54
+ computerTakeover?: "ask" | "never";
55
+ /** Apps Computer Use refuses outright, beyond the built-in list: ids or names. */
56
+ computerBlockedApps?: string[];
43
57
  /**
44
58
  * Sessions and projects kept at the top of their list.
45
59
  *
package/dist/prefs.js CHANGED
@@ -5,7 +5,7 @@ import { parseJson } from "@oharness/engine/util/json.ts";
5
5
  import { isEffortChoice } from "@oharness/engine/providers/types.ts";
6
6
  import { renameWithRetry } from "@oharness/engine/util/fs.ts";
7
7
  /** The lists that are arrays of strings, checked the same way on read. */
8
- const STRING_LISTS = ["pinnedSessions", "pinnedProjects", "projectOrder"];
8
+ const STRING_LISTS = ["pinnedSessions", "pinnedProjects", "projectOrder", "computerBlockedApps"];
9
9
  const FILE = () => userPath("prefs.json");
10
10
  export async function readPrefs() {
11
11
  let raw;
@@ -28,6 +28,13 @@ export async function readPrefs() {
28
28
  }
29
29
  if (typeof parsed.value.analytics === "boolean")
30
30
  prefs.analytics = parsed.value.analytics;
31
+ if (typeof parsed.value.keepAwake === "boolean")
32
+ prefs.keepAwake = parsed.value.keepAwake;
33
+ if (typeof parsed.value.remote === "boolean")
34
+ prefs.remote = parsed.value.remote;
35
+ if (parsed.value.computerTakeover === "ask" || parsed.value.computerTakeover === "never") {
36
+ prefs.computerTakeover = parsed.value.computerTakeover;
37
+ }
31
38
  for (const key of STRING_LISTS) {
32
39
  const value = parsed.value[key];
33
40
  if (Array.isArray(value)) {
package/dist/prefs.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"prefs.js","sourceRoot":"","sources":["../src/prefs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prefs.js","sourceRoot":"","sources":["../src/prefs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ import { type KeyObject } from "node:crypto";
2
+ export declare function b64(bytes: Uint8Array): string;
3
+ export declare function unb64(text: unknown, length?: number): Buffer;
4
+ /** A long-term Ed25519 identity, kept as PKCS#8 so it can be stored and reloaded. */
5
+ export interface Identity {
6
+ privateKey: KeyObject;
7
+ publicRaw: Buffer;
8
+ }
9
+ export declare function newIdentity(): {
10
+ identity: Identity;
11
+ pkcs8: string;
12
+ };
13
+ export declare function loadIdentity(pkcs8: string): Identity;
14
+ export declare function transcript(channel: string, phoneIdentity: Buffer, phoneEph: Buffer, macIdentity: Buffer, macEph: Buffer): Buffer;
15
+ export declare function pairingCode(t: Buffer): string;
16
+ /** One direction's key and counter. */
17
+ declare class Direction {
18
+ private counter;
19
+ private readonly key;
20
+ constructor(key: Buffer);
21
+ seal(plaintext: string): string;
22
+ open(frame: string): string;
23
+ }
24
+ export interface HelloFrame {
25
+ device: {
26
+ id: string;
27
+ name: string;
28
+ };
29
+ identity: Buffer;
30
+ eph: Buffer;
31
+ }
32
+ export declare function readHello(message: Record<string, unknown>): HelloFrame;
33
+ /**
34
+ * The Mac's side of one channel's handshake: answer the hello, then check the
35
+ * proof. `welcome()` is sent as plaintext; after `accept()` every frame is sealed.
36
+ */
37
+ export declare class MacHandshake {
38
+ readonly transcript: Buffer;
39
+ readonly code: string;
40
+ private readonly sendKey;
41
+ private readonly receiveKey;
42
+ private readonly welcomeFrame;
43
+ readonly hello: HelloFrame;
44
+ constructor(channel: string, hello: HelloFrame, identity: Identity);
45
+ welcome(): Record<string, unknown>;
46
+ /** Checks the phone's proof; on success, the sealed session for this channel. */
47
+ accept(message: Record<string, unknown>): SealedSession;
48
+ }
49
+ export declare class SealedSession {
50
+ private readonly outgoing;
51
+ private readonly incoming;
52
+ constructor(outgoing: Direction, incoming: Direction);
53
+ seal(message: Record<string, unknown>): string;
54
+ open(frame: string): Record<string, unknown>;
55
+ }
56
+ export declare class PhoneHandshake {
57
+ readonly eph: import("crypto").KeyPairKeyObjectResult;
58
+ private readonly ephRaw;
59
+ readonly channel: string;
60
+ readonly device: {
61
+ id: string;
62
+ name: string;
63
+ };
64
+ readonly identity: Identity;
65
+ constructor(channel: string, device: {
66
+ id: string;
67
+ name: string;
68
+ }, identity: Identity);
69
+ hello(): Record<string, unknown>;
70
+ /** Verifies the welcome and returns the proof to send, the code, and the sealed session. */
71
+ finish(welcome: Record<string, unknown>): {
72
+ proof: Record<string, unknown>;
73
+ code: string;
74
+ macIdentity: Buffer;
75
+ session: SealedSession;
76
+ };
77
+ }
78
+ export {};
@@ -0,0 +1,216 @@
1
+ import { createHash, createCipheriv, createDecipheriv, createPrivateKey, createPublicKey, diffieHellman, generateKeyPairSync, hkdfSync, sign, verify, } from "node:crypto";
2
+ /**
3
+ * The cryptography of remote control, Mac side. The phone's half is
4
+ * HarnessKit's `RemoteCrypto.swift`, and the two must agree byte for byte.
5
+ *
6
+ * Every channel starts with a handshake that the relay forwards and cannot
7
+ * take part in:
8
+ *
9
+ * phone → mac {hs:"hello", v:1, device:{id,name}, identity, eph}
10
+ * mac → phone {hs:"welcome", identity, eph, sig} sig = Ed25519_mac(T)
11
+ * phone → mac {hs:"proof", sig} sig = Ed25519_phone(T)
12
+ *
13
+ * `identity` is the side's long-term Ed25519 public key and `eph` a fresh
14
+ * X25519 public key, each as 32 raw bytes in base64. The transcript is
15
+ *
16
+ * T = SHA-256("oharness-remote-v1" ‖ 0x00 ‖ channel ‖ 0x00 ‖
17
+ * phoneIdentity ‖ phoneEph ‖ macIdentity ‖ macEph)
18
+ *
19
+ * which binds both identities, both ephemeral keys and this channel. Each
20
+ * direction gets its own AES-256-GCM key:
21
+ *
22
+ * key = HKDF-SHA256(ikm = X25519(eph, eph'), salt = T, info = "oharness phone->mac" | "oharness mac->phone")
23
+ *
24
+ * and every sealed frame is `s.` + base64url(counter₈ ‖ ciphertext ‖ tag₁₆),
25
+ * nonce = 0x00000000 ‖ counter₈. Counters start at 0 and must arrive in
26
+ * order: the relay is one ordered socket, so anything else is a replay or a
27
+ * drop, and either way the channel is closed.
28
+ *
29
+ * The pairing code both screens show is the first four bytes of
30
+ * SHA-256("oharness-remote-sas" ‖ T), big-endian, mod 1 000 000, six digits.
31
+ * A relay that put itself in the middle would hold two different
32
+ * transcripts, so the two screens would show two different codes.
33
+ */
34
+ const TRANSCRIPT_LABEL = "oharness-remote-v1";
35
+ const SAS_LABEL = "oharness-remote-sas";
36
+ const INFO_PHONE_TO_MAC = "oharness phone->mac";
37
+ const INFO_MAC_TO_PHONE = "oharness mac->phone";
38
+ export function b64(bytes) {
39
+ return Buffer.from(bytes).toString("base64");
40
+ }
41
+ export function unb64(text, length) {
42
+ if (typeof text !== "string")
43
+ throw new Error("expected base64");
44
+ const bytes = Buffer.from(text, "base64");
45
+ if (length !== undefined && bytes.length !== length)
46
+ throw new Error(`expected ${length} bytes`);
47
+ return bytes;
48
+ }
49
+ export function newIdentity() {
50
+ const { privateKey } = generateKeyPairSync("ed25519");
51
+ const pkcs8 = privateKey.export({ format: "der", type: "pkcs8" }).toString("base64");
52
+ return { identity: loadIdentity(pkcs8), pkcs8 };
53
+ }
54
+ export function loadIdentity(pkcs8) {
55
+ const privateKey = createPrivateKey({ key: Buffer.from(pkcs8, "base64"), format: "der", type: "pkcs8" });
56
+ return { privateKey, publicRaw: rawPublic(createPublicKey(privateKey)) };
57
+ }
58
+ function rawPublic(key) {
59
+ const jwk = key.export({ format: "jwk" });
60
+ if (typeof jwk.x !== "string")
61
+ throw new Error("not an OKP key");
62
+ return Buffer.from(jwk.x, "base64url");
63
+ }
64
+ function okpPublic(raw, crv) {
65
+ return createPublicKey({ key: { kty: "OKP", crv, x: raw.toString("base64url") }, format: "jwk" });
66
+ }
67
+ export function transcript(channel, phoneIdentity, phoneEph, macIdentity, macEph) {
68
+ return createHash("sha256")
69
+ .update(TRANSCRIPT_LABEL)
70
+ .update(Buffer.from([0]))
71
+ .update(channel)
72
+ .update(Buffer.from([0]))
73
+ .update(phoneIdentity)
74
+ .update(phoneEph)
75
+ .update(macIdentity)
76
+ .update(macEph)
77
+ .digest();
78
+ }
79
+ export function pairingCode(t) {
80
+ const digest = createHash("sha256").update(SAS_LABEL).update(t).digest();
81
+ return String(digest.readUInt32BE(0) % 1_000_000).padStart(6, "0");
82
+ }
83
+ /** One direction's key and counter. */
84
+ class Direction {
85
+ counter = 0n;
86
+ key;
87
+ constructor(key) {
88
+ this.key = key;
89
+ }
90
+ seal(plaintext) {
91
+ const counter = Buffer.alloc(8);
92
+ counter.writeBigUInt64BE(this.counter++);
93
+ const cipher = createCipheriv("aes-256-gcm", this.key, Buffer.concat([Buffer.alloc(4), counter]));
94
+ const body = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final(), cipher.getAuthTag()]);
95
+ return "s." + Buffer.concat([counter, body]).toString("base64url");
96
+ }
97
+ open(frame) {
98
+ if (!frame.startsWith("s."))
99
+ throw new Error("expected a sealed frame");
100
+ const bytes = Buffer.from(frame.slice(2), "base64url");
101
+ if (bytes.length < 8 + 16)
102
+ throw new Error("sealed frame too short");
103
+ const counter = bytes.readBigUInt64BE(0);
104
+ if (counter !== this.counter)
105
+ throw new Error("sealed frame out of order");
106
+ const decipher = createDecipheriv("aes-256-gcm", this.key, Buffer.concat([Buffer.alloc(4), bytes.subarray(0, 8)]));
107
+ decipher.setAuthTag(bytes.subarray(bytes.length - 16));
108
+ const plaintext = Buffer.concat([decipher.update(bytes.subarray(8, bytes.length - 16)), decipher.final()]).toString("utf8");
109
+ this.counter++;
110
+ return plaintext;
111
+ }
112
+ }
113
+ export function readHello(message) {
114
+ if (message["hs"] !== "hello" || message["v"] !== 1)
115
+ throw new Error("expected a v1 hello");
116
+ const device = message["device"];
117
+ if (!device || typeof device.id !== "string" || !/^[A-Za-z0-9_-]{8,64}$/.test(device.id))
118
+ throw new Error("hello without a device id");
119
+ const name = typeof device.name === "string" && device.name.trim() ? device.name.trim().slice(0, 80) : device.id;
120
+ return { device: { id: device.id, name }, identity: unb64(message["identity"], 32), eph: unb64(message["eph"], 32) };
121
+ }
122
+ /**
123
+ * The Mac's side of one channel's handshake: answer the hello, then check the
124
+ * proof. `welcome()` is sent as plaintext; after `accept()` every frame is sealed.
125
+ */
126
+ export class MacHandshake {
127
+ transcript;
128
+ code;
129
+ sendKey;
130
+ receiveKey;
131
+ welcomeFrame;
132
+ hello;
133
+ constructor(channel, hello, identity) {
134
+ this.hello = hello;
135
+ const { privateKey: ephPrivate, publicKey: ephPublic } = generateKeyPairSync("x25519");
136
+ const macEph = rawPublic(ephPublic);
137
+ this.transcript = transcript(channel, hello.identity, hello.eph, identity.publicRaw, macEph);
138
+ this.code = pairingCode(this.transcript);
139
+ const shared = diffieHellman({ privateKey: ephPrivate, publicKey: okpPublic(hello.eph, "X25519") });
140
+ this.receiveKey = Buffer.from(hkdfSync("sha256", shared, this.transcript, INFO_PHONE_TO_MAC, 32));
141
+ this.sendKey = Buffer.from(hkdfSync("sha256", shared, this.transcript, INFO_MAC_TO_PHONE, 32));
142
+ this.welcomeFrame = {
143
+ hs: "welcome",
144
+ identity: b64(identity.publicRaw),
145
+ eph: b64(macEph),
146
+ sig: b64(sign(null, this.transcript, identity.privateKey)),
147
+ };
148
+ }
149
+ welcome() {
150
+ return this.welcomeFrame;
151
+ }
152
+ /** Checks the phone's proof; on success, the sealed session for this channel. */
153
+ accept(message) {
154
+ if (message["hs"] !== "proof")
155
+ throw new Error("expected the proof");
156
+ const ok = verify(null, this.transcript, okpPublic(this.hello.identity, "Ed25519"), unb64(message["sig"], 64));
157
+ if (!ok)
158
+ throw new Error("the phone's proof does not verify");
159
+ return new SealedSession(new Direction(this.sendKey), new Direction(this.receiveKey));
160
+ }
161
+ }
162
+ export class SealedSession {
163
+ outgoing;
164
+ incoming;
165
+ constructor(outgoing, incoming) {
166
+ this.outgoing = outgoing;
167
+ this.incoming = incoming;
168
+ }
169
+ seal(message) {
170
+ return this.outgoing.seal(JSON.stringify(message));
171
+ }
172
+ open(frame) {
173
+ const value = JSON.parse(this.incoming.open(frame));
174
+ if (!value || typeof value !== "object" || Array.isArray(value))
175
+ throw new Error("sealed frame is not an object");
176
+ return value;
177
+ }
178
+ }
179
+ // ---- The phone's half, in TypeScript: used by tests, and by anything that
180
+ // ---- wants to drive a Mac from Node. It mirrors RemoteCrypto.swift.
181
+ export class PhoneHandshake {
182
+ eph = generateKeyPairSync("x25519");
183
+ ephRaw = rawPublic(this.eph.publicKey);
184
+ channel;
185
+ device;
186
+ identity;
187
+ constructor(channel, device, identity) {
188
+ this.channel = channel;
189
+ this.device = device;
190
+ this.identity = identity;
191
+ }
192
+ hello() {
193
+ return { hs: "hello", v: 1, device: this.device, identity: b64(this.identity.publicRaw), eph: b64(this.ephRaw) };
194
+ }
195
+ /** Verifies the welcome and returns the proof to send, the code, and the sealed session. */
196
+ finish(welcome) {
197
+ if (welcome["hs"] !== "welcome")
198
+ throw new Error("expected the welcome");
199
+ const macIdentity = unb64(welcome["identity"], 32);
200
+ const macEph = unb64(welcome["eph"], 32);
201
+ const t = transcript(this.channel, this.identity.publicRaw, this.ephRaw, macIdentity, macEph);
202
+ if (!verify(null, t, okpPublic(macIdentity, "Ed25519"), unb64(welcome["sig"], 64))) {
203
+ throw new Error("the Mac's signature does not verify");
204
+ }
205
+ const shared = diffieHellman({ privateKey: this.eph.privateKey, publicKey: okpPublic(macEph, "X25519") });
206
+ const send = Buffer.from(hkdfSync("sha256", shared, t, INFO_PHONE_TO_MAC, 32));
207
+ const receive = Buffer.from(hkdfSync("sha256", shared, t, INFO_MAC_TO_PHONE, 32));
208
+ return {
209
+ proof: { hs: "proof", sig: b64(sign(null, t, this.identity.privateKey)) },
210
+ code: pairingCode(t),
211
+ macIdentity,
212
+ session: new SealedSession(new Direction(send), new Direction(receive)),
213
+ };
214
+ }
215
+ }
216
+ //# sourceMappingURL=remote-crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-crypto.js","sourceRoot":"","sources":["../src/remote-crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,mBAAmB,EACnB,QAAQ,EACR,IAAI,EACJ,MAAM,GAEP,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,MAAM,gBAAgB,GAAG,oBAAoB,CAAC;AAC9C,MAAM,SAAS,GAAG,qBAAqB,CAAC;AACxC,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;AAChD,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;AAEhD,MAAM,UAAU,GAAG,CAAC,KAAiB;IACnC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,IAAa,EAAE,MAAe;IAClD,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC1C,IAAI,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,YAAY,MAAM,QAAQ,CAAC,CAAC;IACjG,OAAO,KAAK,CAAC;AACf,CAAC;AAQD,MAAM,UAAU,WAAW;IACzB,MAAM,EAAE,UAAU,EAAE,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACrF,OAAO,EAAE,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,MAAM,UAAU,GAAG,gBAAgB,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACzG,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;AAC3E,CAAC;AAED,SAAS,SAAS,CAAC,GAAc;IAC/B,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1C,IAAI,OAAO,GAAG,CAAC,CAAC,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACjE,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,SAAS,CAAC,GAAW,EAAE,GAAyB;IACvD,OAAO,eAAe,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;AACpG,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAe,EAAE,aAAqB,EAAE,QAAgB,EAAE,WAAmB,EAAE,MAAc;IACtH,OAAO,UAAU,CAAC,QAAQ,CAAC;SACxB,MAAM,CAAC,gBAAgB,CAAC;SACxB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACxB,MAAM,CAAC,OAAO,CAAC;SACf,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACxB,MAAM,CAAC,aAAa,CAAC;SACrB,MAAM,CAAC,QAAQ,CAAC;SAChB,MAAM,CAAC,WAAW,CAAC;SACnB,MAAM,CAAC,MAAM,CAAC;SACd,MAAM,EAAE,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,CAAS;IACnC,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACzE,OAAO,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACrE,CAAC;AAED,uCAAuC;AACvC,MAAM,SAAS;IACL,OAAO,GAAG,EAAE,CAAC;IACJ,GAAG,CAAS;IAC7B,YAAY,GAAW;QACrB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAED,IAAI,CAAC,SAAiB;QACpB,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChC,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;QAClG,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QACpG,OAAO,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,CAAC,KAAa;QAChB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACxE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;QACvD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,GAAG,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QACrE,MAAM,OAAO,GAAG,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACzC,IAAI,OAAO,KAAK,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC3E,MAAM,QAAQ,GAAG,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnH,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC5H,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AAQD,MAAM,UAAU,SAAS,CAAC,OAAgC;IACxD,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC5F,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAiD,CAAC;IACjF,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IACvI,MAAM,IAAI,GAAG,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACjH,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AACvH,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,YAAY;IACd,UAAU,CAAS;IACnB,IAAI,CAAS;IACL,OAAO,CAAS;IAChB,UAAU,CAAS;IACnB,YAAY,CAA0B;IAC9C,KAAK,CAAa;IAE3B,YAAY,OAAe,EAAE,KAAiB,EAAE,QAAkB;QAChE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QACvF,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;QACpC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,EAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAC7F,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QACpG,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC,CAAC;QAClG,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/F,IAAI,CAAC,YAAY,GAAG;YAClB,EAAE,EAAE,SAAS;YACb,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC;YACjC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC;YAChB,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;SAC3D,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,iFAAiF;IACjF,MAAM,CAAC,OAAgC;QACrC,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACrE,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/G,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAC9D,OAAO,IAAI,aAAa,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IACxF,CAAC;CACF;AAED,MAAM,OAAO,aAAa;IACP,QAAQ,CAAY;IACpB,QAAQ,CAAY;IACrC,YAAY,QAAmB,EAAE,QAAmB;QAClD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,IAAI,CAAC,OAAgC;QACnC,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,CAAC,KAAa;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAClH,OAAO,KAAgC,CAAC;IAC1C,CAAC;CACF;AAED,4EAA4E;AAC5E,sEAAsE;AAEtE,MAAM,OAAO,cAAc;IAChB,GAAG,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC5B,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC/C,OAAO,CAAS;IAChB,MAAM,CAA+B;IACrC,QAAQ,CAAW;IAE5B,YAAY,OAAe,EAAE,MAAoC,EAAE,QAAkB;QACnF,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,KAAK;QACH,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;IACnH,CAAC;IAED,4FAA4F;IAC5F,MAAM,CAAC,OAAgC;QACrC,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACzE,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACzC,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;QAC9F,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,SAAS,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;YACnF,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC1G,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/E,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC,CAAC;QAClF,OAAO;YACL,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,EAAE;YACzE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;YACpB,WAAW;YACX,OAAO,EAAE,IAAI,aAAa,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,SAAS,CAAC,OAAO,CAAC,CAAC;SACxE,CAAC;IACJ,CAAC;CACF"}