@runuai/host 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +91 -0
  3. package/bin/uai-host.mjs +14 -0
  4. package/db/migrations/0000_host_tasks.sql +12 -0
  5. package/db/migrations/0001_host_ui.sql +11 -0
  6. package/db/migrations/0002_host_github_tokens.sql +8 -0
  7. package/db/migrations/0003_host_ssh_keys.sql +8 -0
  8. package/db/migrations/0004_host_owner_name.sql +1 -0
  9. package/db/migrations/meta/_journal.json +41 -0
  10. package/db/schema.ts +82 -0
  11. package/images/standard/Dockerfile +232 -0
  12. package/images/standard/README.md +122 -0
  13. package/images/standard/container/code-server-settings.json +36 -0
  14. package/images/standard/container/uai-init +215 -0
  15. package/images/standard/tool-versions +2 -0
  16. package/lib/agent.ts +292 -0
  17. package/lib/agents/claude.ts +343 -0
  18. package/lib/agents/codex.ts +522 -0
  19. package/lib/agents/factory.ts +34 -0
  20. package/lib/agents/mock.ts +133 -0
  21. package/lib/agents/proc.ts +172 -0
  22. package/lib/agents/registry.ts +109 -0
  23. package/lib/agents/types.ts +133 -0
  24. package/lib/attachments.ts +46 -0
  25. package/lib/cloud-state.ts +56 -0
  26. package/lib/command-db.ts +278 -0
  27. package/lib/db.ts +68 -0
  28. package/lib/env.ts +140 -0
  29. package/lib/git-diff.ts +370 -0
  30. package/lib/git-identity.ts +65 -0
  31. package/lib/github-tokens.ts +321 -0
  32. package/lib/orchestrator.ts +975 -0
  33. package/lib/preview-ports.ts +85 -0
  34. package/lib/repo-clone.ts +127 -0
  35. package/lib/runtime-state.ts +120 -0
  36. package/lib/secrets.ts +71 -0
  37. package/lib/ssh.ts +186 -0
  38. package/lib/standard-image.ts +152 -0
  39. package/lib/task-diff.ts +113 -0
  40. package/lib/task-status.ts +46 -0
  41. package/lib/transcript.ts +30 -0
  42. package/lib/ulid.ts +7 -0
  43. package/package.json +85 -0
  44. package/scripts/agent/_common.sh +248 -0
  45. package/scripts/agent/task-down.sh +113 -0
  46. package/scripts/agent/task-status.sh +54 -0
  47. package/scripts/agent/task-up.sh +457 -0
  48. package/scripts/install/darwin.ts +167 -0
  49. package/scripts/install/linux.ts +115 -0
  50. package/scripts/install/types.ts +35 -0
  51. package/scripts/install/util.ts +39 -0
  52. package/scripts/install/win.ts +130 -0
  53. package/src/cli.ts +445 -0
  54. package/src/index.ts +375 -0
  55. package/src/load-env.ts +52 -0
  56. package/src/main.ts +1156 -0
  57. package/src/paths.ts +64 -0
  58. package/src/protocol.ts +413 -0
  59. package/src/ui/server.ts +343 -0
  60. package/src/ui/types.ts +78 -0
  61. package/ui/app.js +264 -0
  62. package/ui/index.html +55 -0
  63. package/ui/style.css +359 -0
  64. package/ui/uai-logo-black.svg +9 -0
package/ui/index.html ADDED
@@ -0,0 +1,55 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Uai host</title>
7
+ <!-- Local monitor UI (ADR-028). Served from 127.0.0.1 by the host service. -->
8
+ <link rel="icon" type="image/svg+xml" href="/uai-logo-black.svg" />
9
+ <link rel="stylesheet" href="/style.css" />
10
+ </head>
11
+ <body>
12
+ <main>
13
+ <header class="topbar">
14
+ <div class="brand">
15
+ <img class="logo" src="/uai-logo-black.svg" alt="Uai" />
16
+ <div class="brand-text">
17
+ <div class="host-name" id="host-name">…</div>
18
+ <div class="host-sub" id="host-sub">host monitor</div>
19
+ </div>
20
+ </div>
21
+ <div class="badge-wrap">
22
+ <button class="badge" id="badge" type="button" aria-expanded="false">
23
+ <span class="dot" id="badge-dot"></span>
24
+ <span id="badge-label">connecting…</span>
25
+ </button>
26
+ <div class="badge-detail" id="badge-detail" hidden></div>
27
+ </div>
28
+ </header>
29
+
30
+ <section class="panel">
31
+ <h2>Service</h2>
32
+ <dl class="kv" id="service-kv"></dl>
33
+ </section>
34
+
35
+ <section class="panel">
36
+ <h2>Active tasks <span class="count" id="tasks-count"></span></h2>
37
+ <div id="tasks" class="tasks">
38
+ <p class="empty" id="tasks-empty">No active tasks.</p>
39
+ </div>
40
+ </section>
41
+
42
+ <section class="panel">
43
+ <h2>Recent events</h2>
44
+ <ol class="events" id="events">
45
+ <li class="empty" id="events-empty">No events yet.</li>
46
+ </ol>
47
+ </section>
48
+
49
+ <footer class="foot">
50
+ <span id="foot-note">read-only · 127.0.0.1 · polls every 2s</span>
51
+ </footer>
52
+ </main>
53
+ <script src="/app.js"></script>
54
+ </body>
55
+ </html>
package/ui/style.css ADDED
@@ -0,0 +1,359 @@
1
+ /* Local host UI (ADR-028). Design tokens borrowed from the marketing site:
2
+ warm off-white, dark via prefers-color-scheme, system fonts, quiet UI. */
3
+
4
+ :root {
5
+ --bg: #fafaf9;
6
+ --fg: #18181b;
7
+ --fg-muted: #5f5f66;
8
+ --line: #e7e5e4;
9
+ --tint: #f4f4f2;
10
+ --accent: #18181b;
11
+ --accent-fg: #fafaf9;
12
+ --ok: #15803d;
13
+ --warn: #b45309;
14
+ --bad: #b91c1c;
15
+ --radius: 10px;
16
+ --maxw: 56rem;
17
+ --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
18
+ "Helvetica Neue", Arial, "Noto Sans", sans-serif;
19
+ --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
20
+ }
21
+
22
+ @media (prefers-color-scheme: dark) {
23
+ :root {
24
+ --bg: #0e0e10;
25
+ --fg: #f5f5f4;
26
+ --fg-muted: #a1a1aa;
27
+ --line: #27272a;
28
+ --tint: #19191c;
29
+ --accent: #f5f5f4;
30
+ --accent-fg: #0e0e10;
31
+ --ok: #4ade80;
32
+ --warn: #fbbf24;
33
+ --bad: #f87171;
34
+ }
35
+ }
36
+
37
+ * {
38
+ box-sizing: border-box;
39
+ }
40
+
41
+ html {
42
+ color-scheme: light dark;
43
+ }
44
+
45
+ body {
46
+ margin: 0;
47
+ background: var(--bg);
48
+ color: var(--fg);
49
+ font-family: var(--font);
50
+ font-size: 15px;
51
+ line-height: 1.5;
52
+ -webkit-font-smoothing: antialiased;
53
+ }
54
+
55
+ main {
56
+ max-width: var(--maxw);
57
+ margin: 0 auto;
58
+ padding: clamp(1.25rem, 4vw, 3rem) clamp(1rem, 4vw, 2rem) 4rem;
59
+ }
60
+
61
+ code,
62
+ .mono {
63
+ font-family: var(--mono);
64
+ font-size: 0.92em;
65
+ }
66
+
67
+ /* --- header --------------------------------------------------------------- */
68
+
69
+ .topbar {
70
+ display: flex;
71
+ align-items: center;
72
+ justify-content: space-between;
73
+ gap: 1rem;
74
+ padding-bottom: 1.5rem;
75
+ margin-bottom: 0.5rem;
76
+ border-bottom: 1px solid var(--line);
77
+ flex-wrap: wrap;
78
+ }
79
+
80
+ .brand {
81
+ display: flex;
82
+ align-items: center;
83
+ gap: 0.75rem;
84
+ }
85
+
86
+ .logo {
87
+ height: 30px;
88
+ width: auto;
89
+ }
90
+
91
+ @media (prefers-color-scheme: dark) {
92
+ .logo {
93
+ filter: invert(1);
94
+ }
95
+ }
96
+
97
+ .host-name {
98
+ font-weight: 600;
99
+ font-size: 1.05rem;
100
+ }
101
+
102
+ .host-sub {
103
+ color: var(--fg-muted);
104
+ font-size: 0.82rem;
105
+ }
106
+
107
+ /* --- connection badge ----------------------------------------------------- */
108
+
109
+ .badge-wrap {
110
+ position: relative;
111
+ }
112
+
113
+ .badge {
114
+ display: inline-flex;
115
+ align-items: center;
116
+ gap: 0.5rem;
117
+ padding: 0.4rem 0.75rem;
118
+ border: 1px solid var(--line);
119
+ border-radius: 999px;
120
+ background: var(--tint);
121
+ color: var(--fg);
122
+ font: inherit;
123
+ font-size: 0.85rem;
124
+ cursor: pointer;
125
+ }
126
+
127
+ .dot {
128
+ width: 8px;
129
+ height: 8px;
130
+ border-radius: 999px;
131
+ background: var(--fg-muted);
132
+ }
133
+
134
+ .dot.ok {
135
+ background: var(--ok);
136
+ }
137
+ .dot.warn {
138
+ background: var(--warn);
139
+ }
140
+ .dot.bad {
141
+ background: var(--bad);
142
+ }
143
+
144
+ .badge-detail {
145
+ position: absolute;
146
+ right: 0;
147
+ top: calc(100% + 0.4rem);
148
+ z-index: 10;
149
+ min-width: 16rem;
150
+ padding: 0.75rem 0.9rem;
151
+ border: 1px solid var(--line);
152
+ border-radius: var(--radius);
153
+ background: var(--bg);
154
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
155
+ font-size: 0.85rem;
156
+ }
157
+
158
+ .badge-detail .row {
159
+ display: flex;
160
+ justify-content: space-between;
161
+ gap: 1rem;
162
+ }
163
+
164
+ .badge-detail .err {
165
+ margin-top: 0.4rem;
166
+ color: var(--bad);
167
+ word-break: break-word;
168
+ }
169
+
170
+ /* --- panels --------------------------------------------------------------- */
171
+
172
+ .panel {
173
+ margin-top: 2rem;
174
+ }
175
+
176
+ .panel h2 {
177
+ font-size: 0.78rem;
178
+ text-transform: uppercase;
179
+ letter-spacing: 0.06em;
180
+ color: var(--fg-muted);
181
+ font-weight: 600;
182
+ margin: 0 0 0.75rem;
183
+ }
184
+
185
+ .count {
186
+ color: var(--fg-muted);
187
+ font-weight: 400;
188
+ }
189
+
190
+ .empty {
191
+ color: var(--fg-muted);
192
+ font-size: 0.9rem;
193
+ margin: 0.25rem 0;
194
+ }
195
+
196
+ /* --- service key/value ---------------------------------------------------- */
197
+
198
+ .kv {
199
+ display: grid;
200
+ grid-template-columns: max-content 1fr;
201
+ gap: 0.35rem 1.25rem;
202
+ margin: 0;
203
+ }
204
+
205
+ .kv dt {
206
+ color: var(--fg-muted);
207
+ font-size: 0.85rem;
208
+ }
209
+
210
+ .kv dd {
211
+ margin: 0;
212
+ font-family: var(--mono);
213
+ font-size: 0.85rem;
214
+ word-break: break-all;
215
+ }
216
+
217
+ /* --- tasks ---------------------------------------------------------------- */
218
+
219
+ .task {
220
+ border: 1px solid var(--line);
221
+ border-radius: var(--radius);
222
+ margin-bottom: 0.6rem;
223
+ overflow: hidden;
224
+ }
225
+
226
+ .task-head {
227
+ display: flex;
228
+ align-items: center;
229
+ gap: 0.75rem;
230
+ padding: 0.7rem 0.9rem;
231
+ cursor: pointer;
232
+ }
233
+
234
+ .task-head:hover {
235
+ background: var(--tint);
236
+ }
237
+
238
+ .task-main {
239
+ flex: 1;
240
+ min-width: 0;
241
+ }
242
+
243
+ .task-title {
244
+ display: flex;
245
+ align-items: baseline;
246
+ gap: 0.5rem;
247
+ flex-wrap: wrap;
248
+ }
249
+
250
+ .task-owner {
251
+ font-weight: 600;
252
+ }
253
+
254
+ .slug {
255
+ display: inline-block;
256
+ font-family: var(--mono);
257
+ font-size: 0.78rem;
258
+ color: var(--fg-muted);
259
+ background: var(--tint);
260
+ border: 1px solid var(--line);
261
+ border-radius: 6px;
262
+ padding: 0 0.35rem;
263
+ margin-right: 0.25rem;
264
+ }
265
+
266
+ .task-meta {
267
+ color: var(--fg-muted);
268
+ font-size: 0.82rem;
269
+ margin-top: 0.2rem;
270
+ }
271
+
272
+ .task-status {
273
+ font-size: 0.78rem;
274
+ color: var(--fg-muted);
275
+ }
276
+
277
+ .task-id {
278
+ font-family: var(--mono);
279
+ font-size: 0.72rem;
280
+ color: var(--fg-muted);
281
+ }
282
+
283
+ .chev {
284
+ color: var(--fg-muted);
285
+ transition: transform 0.15s ease;
286
+ }
287
+
288
+ .task.open .chev {
289
+ transform: rotate(90deg);
290
+ }
291
+
292
+ .task-events {
293
+ border-top: 1px solid var(--line);
294
+ background: var(--tint);
295
+ padding: 0.5rem 0.9rem 0.7rem;
296
+ }
297
+
298
+ .task-events ol {
299
+ margin: 0;
300
+ padding-left: 1.1rem;
301
+ }
302
+
303
+ /* --- events --------------------------------------------------------------- */
304
+
305
+ .events {
306
+ list-style: none;
307
+ margin: 0;
308
+ padding: 0;
309
+ }
310
+
311
+ .events li:not(.empty) {
312
+ display: flex;
313
+ align-items: baseline;
314
+ gap: 0.75rem;
315
+ padding: 0.4rem 0;
316
+ border-bottom: 1px solid var(--line);
317
+ font-size: 0.88rem;
318
+ }
319
+
320
+ .ev-kind {
321
+ font-family: var(--mono);
322
+ font-size: 0.8rem;
323
+ }
324
+
325
+ .ev-kind.created {
326
+ color: var(--fg-muted);
327
+ }
328
+ .ev-kind.started {
329
+ color: var(--ok);
330
+ }
331
+ .ev-kind.ended {
332
+ color: var(--fg-muted);
333
+ }
334
+ .ev-kind.ship {
335
+ color: var(--warn);
336
+ }
337
+
338
+ .ev-time {
339
+ color: var(--fg-muted);
340
+ font-size: 0.8rem;
341
+ white-space: nowrap;
342
+ }
343
+
344
+ .ev-task {
345
+ font-family: var(--mono);
346
+ font-size: 0.78rem;
347
+ color: var(--fg-muted);
348
+ margin-left: auto;
349
+ }
350
+
351
+ /* --- footer --------------------------------------------------------------- */
352
+
353
+ .foot {
354
+ margin-top: 3rem;
355
+ padding-top: 1rem;
356
+ border-top: 1px solid var(--line);
357
+ color: var(--fg-muted);
358
+ font-size: 0.78rem;
359
+ }
@@ -0,0 +1,9 @@
1
+ <svg width="250" height="148" viewBox="0 0 250 148" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M100.415 69.076C100.415 93.25 81.6825 112.847 58.5752 112.847H41.8394C18.7322 112.847 0 93.25 0 69.076V3.38385e-07H29.2876V68.3921C29.2876 80.4791 38.6537 90.2775 50.2073 90.2775C61.761 90.2775 71.127 80.4791 71.127 68.3921V3.38385e-07H100.415V69.076Z" fill="black"/>
3
+ <path d="M112.847 64.6305C112.847 47.4219 126.762 33.4715 143.928 33.4715H156.36C173.525 33.4716 187.441 47.4219 187.441 64.6305V113.803H165.684V65.1174C165.684 56.5131 158.726 49.5379 150.144 49.5379C141.561 49.5379 134.603 56.5131 134.603 65.1174V113.803H112.847V64.6305Z" fill="black"/>
4
+ <path d="M197.96 38.2532H219.956V113.803H197.96V38.2532Z" fill="black"/>
5
+ <path d="M233.344 8.1288C233.344 3.63939 236.984 3.38385e-07 241.473 3.38385e-07C245.963 3.38385e-07 249.602 3.63939 249.602 8.1288V139.146C249.602 143.635 245.963 147.275 241.473 147.275C236.984 147.275 233.344 143.635 233.344 139.146V8.1288Z" fill="black"/>
6
+ <path d="M112.847 16.2576V5.81055e-06L245.777 0V16.2576L112.847 16.2576Z" fill="black"/>
7
+ <path d="M115.716 90.8513V74.5937L182.659 74.5937V90.8513H115.716Z" fill="black"/>
8
+ <path d="M2.86899 147.275L2.86899 131.017L249.602 131.017V147.275L2.86899 147.275Z" fill="black"/>
9
+ </svg>