@jr2/orchestrator 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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -0
  3. package/bin/server.ts +23 -0
  4. package/console/canvas.ts +843 -0
  5. package/console/components/app.ts +79 -0
  6. package/console/components/drawer.ts +131 -0
  7. package/console/components/fleet.ts +117 -0
  8. package/console/components/machine-pane.ts +85 -0
  9. package/console/components/nav.ts +81 -0
  10. package/console/components/schema-form.ts +137 -0
  11. package/console/main.ts +383 -0
  12. package/console/page.html +28 -0
  13. package/console/store.ts +336 -0
  14. package/console/style.css +700 -0
  15. package/console/tsconfig.json +18 -0
  16. package/package.json +61 -0
  17. package/src/actor.ts +562 -0
  18. package/src/agent.ts +124 -0
  19. package/src/ambient.ts +50 -0
  20. package/src/config.ts +297 -0
  21. package/src/customize.ts +348 -0
  22. package/src/durability.ts +135 -0
  23. package/src/fingerprint.ts +92 -0
  24. package/src/gate.ts +76 -0
  25. package/src/harness-client.ts +503 -0
  26. package/src/http.ts +753 -0
  27. package/src/images.ts +303 -0
  28. package/src/index.ts +40 -0
  29. package/src/instance.ts +294 -0
  30. package/src/machine-doc.ts +334 -0
  31. package/src/names.ts +78 -0
  32. package/src/open.ts +17 -0
  33. package/src/parts.ts +500 -0
  34. package/src/pool.ts +284 -0
  35. package/src/registration.ts +340 -0
  36. package/src/repo-fetch.ts +259 -0
  37. package/src/repo-identity.ts +145 -0
  38. package/src/repos.ts +330 -0
  39. package/src/run-host.ts +1095 -0
  40. package/src/sandbox-kubectl.ts +1136 -0
  41. package/src/server.ts +220 -0
  42. package/src/setup.ts +360 -0
  43. package/src/snapshot-store.ts +150 -0
  44. package/src/stub-harness.ts +217 -0
  45. package/src/tokens.ts +126 -0
  46. package/src/vocabulary.ts +99 -0
  47. package/src/wire.ts +103 -0
  48. package/src/workspace.ts +874 -0
  49. package/tsconfig.instance.json +26 -0
@@ -0,0 +1,700 @@
1
+ /* The jr2 Console — dark schematic. One committed look: ink canvas with a faint dot grid,
2
+ monospace instrumentation type, phosphor-green live highlight. */
3
+
4
+ :root {
5
+ --ink: #0a0f1a;
6
+ --ink-2: #0e1524;
7
+ --line: #223048;
8
+ --line-soft: #182338;
9
+ --text: #c8d6e8;
10
+ --text-dim: #5f7189;
11
+ --phosphor: #4ef0a7;
12
+ --phosphor-soft: rgba(78, 240, 167, 0.12);
13
+ --amber: #f0c04e;
14
+ --red: #f06a5f;
15
+ --blue: #6aa7f0;
16
+ --mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
17
+ }
18
+
19
+ * {
20
+ box-sizing: border-box;
21
+ }
22
+
23
+ html,
24
+ body {
25
+ margin: 0;
26
+ height: 100%;
27
+ background: var(--ink);
28
+ color: var(--text);
29
+ font-family: var(--mono);
30
+ font-size: 13px;
31
+ }
32
+
33
+ body {
34
+ display: flex;
35
+ flex-direction: column;
36
+ }
37
+
38
+ /* The vdom's mount root (ADR-0034): layout-transparent, so body's flex column still reaches the
39
+ header/main the components render. */
40
+ #root {
41
+ display: contents;
42
+ }
43
+
44
+ header {
45
+ display: flex;
46
+ align-items: baseline;
47
+ gap: 12px;
48
+ padding: 10px 16px;
49
+ border-bottom: 1px solid var(--line);
50
+ background: var(--ink-2);
51
+ }
52
+
53
+ .mark {
54
+ color: var(--ink);
55
+ background: var(--phosphor);
56
+ font-weight: 700;
57
+ padding: 1px 7px;
58
+ border-radius: 3px;
59
+ letter-spacing: 0.05em;
60
+ }
61
+
62
+ header h1 {
63
+ font-size: 15px;
64
+ font-weight: 600;
65
+ margin: 0;
66
+ letter-spacing: 0.04em;
67
+ }
68
+ .dim {
69
+ color: var(--text-dim);
70
+ }
71
+
72
+ .zoom {
73
+ margin-left: auto;
74
+ display: flex;
75
+ gap: 4px;
76
+ }
77
+
78
+ button {
79
+ font: inherit;
80
+ color: var(--text);
81
+ background: transparent;
82
+ border: 1px solid var(--line);
83
+ border-radius: 3px;
84
+ padding: 1px 8px;
85
+ cursor: pointer;
86
+ }
87
+ button:hover {
88
+ border-color: var(--phosphor);
89
+ color: var(--phosphor);
90
+ }
91
+
92
+ main {
93
+ flex: 1;
94
+ display: flex;
95
+ min-height: 0;
96
+ }
97
+
98
+ #canvas {
99
+ flex: 1;
100
+ overflow: hidden; /* the viewport transform moves the diagram; nothing here scrolls */
101
+ display: flex;
102
+ flex-direction: column;
103
+ background-image: radial-gradient(var(--line-soft) 1px, transparent 1px);
104
+ background-size: 22px 22px;
105
+ cursor: grab;
106
+ }
107
+
108
+ #canvas.panning {
109
+ cursor: grabbing;
110
+ user-select: none;
111
+ }
112
+
113
+ #machine-svg {
114
+ flex: 1;
115
+ min-height: 0;
116
+ width: 100%;
117
+ display: block;
118
+ }
119
+
120
+ /* The notices sit in flow above the diagram; the old canvas padding lives on them now. */
121
+ #placeholder,
122
+ #error {
123
+ margin: 28px 28px 0;
124
+ }
125
+
126
+ #root-transitions {
127
+ margin: 28px 28px 16px;
128
+ }
129
+
130
+ #error {
131
+ max-width: 520px;
132
+ border: 1px solid var(--red);
133
+ border-radius: 4px;
134
+ padding: 14px 18px;
135
+ color: var(--red);
136
+ background: var(--ink-2);
137
+ }
138
+ #error .dim {
139
+ display: block;
140
+ margin-top: 8px;
141
+ color: var(--text-dim);
142
+ }
143
+ /* A caveat about the diagram, not a failure to draw one — see `opaqueStates`. */
144
+ #error.notice {
145
+ border-color: var(--amber);
146
+ color: var(--amber);
147
+ }
148
+
149
+ #root-transitions {
150
+ display: flex;
151
+ gap: 18px;
152
+ flex-wrap: wrap;
153
+ margin-bottom: 16px;
154
+ }
155
+ #root-transitions span {
156
+ color: var(--text-dim);
157
+ border: 1px dashed var(--line);
158
+ border-radius: 3px;
159
+ padding: 2px 8px;
160
+ }
161
+ #root-transitions:empty {
162
+ display: none;
163
+ }
164
+
165
+ /* ---- Machine (SVG) ---- */
166
+
167
+ /* Every box is click-to-select (outline only — the click is reserved beyond that). */
168
+ .state {
169
+ cursor: pointer;
170
+ }
171
+ .state > rect {
172
+ fill: var(--ink-2);
173
+ stroke: var(--line);
174
+ stroke-width: 1.2;
175
+ transition:
176
+ stroke 0.25s,
177
+ fill 0.25s,
178
+ filter 0.25s;
179
+ }
180
+ .state--compound > rect,
181
+ .state--parallel > rect {
182
+ fill: rgba(14, 21, 36, 0.55);
183
+ }
184
+ .state--parallel > rect {
185
+ stroke-dasharray: 5 4;
186
+ }
187
+ .state--final > rect.final-inner {
188
+ fill: none;
189
+ stroke: var(--line);
190
+ }
191
+ .state-title {
192
+ fill: var(--text);
193
+ font: 600 13px var(--mono);
194
+ letter-spacing: 0.03em;
195
+ }
196
+ .state-row {
197
+ fill: var(--text-dim);
198
+ font: 12px var(--mono);
199
+ }
200
+ .state-row--invoke {
201
+ fill: var(--blue);
202
+ }
203
+ .state-row--self {
204
+ fill: var(--text-dim);
205
+ }
206
+ .state-row--tags {
207
+ fill: var(--amber);
208
+ }
209
+
210
+ .state.active > rect:first-of-type {
211
+ stroke: var(--phosphor);
212
+ stroke-width: 1.8;
213
+ fill: var(--phosphor-soft);
214
+ filter: drop-shadow(0 0 6px rgba(78, 240, 167, 0.55));
215
+ }
216
+ .state.active > .state-title {
217
+ fill: var(--phosphor);
218
+ }
219
+
220
+ /* The selected node: outline only, one at a time. Declared after `.active` so the stroke wins on a
221
+ box that is both, while the live fill and glow stay. */
222
+ .state.selected > rect:first-of-type {
223
+ stroke: var(--text);
224
+ stroke-width: 2.2;
225
+ }
226
+
227
+ /* The gate pin (ADR-0032): this run is parked here, waiting for outside input. Amber, like the
228
+ inbox card it mirrors. */
229
+ .gate-pin {
230
+ fill: var(--amber);
231
+ font: 13px var(--mono);
232
+ filter: drop-shadow(0 0 4px rgba(240, 192, 78, 0.6));
233
+ }
234
+
235
+ /* ---- Child machines: one subgraph per live instance, or a dimmed template when none ---- */
236
+
237
+ .child-machine > rect {
238
+ fill: rgba(106, 167, 240, 0.04);
239
+ stroke: var(--blue);
240
+ stroke-width: 1.2;
241
+ stroke-dasharray: 2 3;
242
+ }
243
+ .child-machine > .state-title {
244
+ fill: var(--blue);
245
+ }
246
+ .child-machine:hover > rect {
247
+ stroke-width: 1.8;
248
+ }
249
+ .child-machine--template {
250
+ opacity: 0.55;
251
+ }
252
+ .child-machine--collapsed > rect {
253
+ stroke-dasharray: none;
254
+ }
255
+ .child-machine.active > rect:first-of-type {
256
+ fill: rgba(78, 240, 167, 0.05);
257
+ }
258
+ .state-row--dim {
259
+ fill: var(--text-dim);
260
+ }
261
+
262
+ /* The ⊞/⊟ fold control, top-right of a child-machine box — folding moved OFF the box body, which
263
+ now selects like any other node. */
264
+ .fold-icon > rect {
265
+ fill: var(--ink);
266
+ stroke: var(--line);
267
+ stroke-width: 1;
268
+ }
269
+ .fold-icon > text {
270
+ fill: var(--text-dim);
271
+ font: 12px var(--mono);
272
+ }
273
+ .fold-icon:hover > rect {
274
+ stroke: var(--blue);
275
+ }
276
+ .fold-icon:hover > text {
277
+ fill: var(--blue);
278
+ }
279
+
280
+ .initial-dot {
281
+ fill: var(--text-dim);
282
+ }
283
+
284
+ .edge path {
285
+ fill: none;
286
+ stroke: var(--text-dim);
287
+ stroke-width: 1.2;
288
+ }
289
+ .edge-label {
290
+ fill: var(--text);
291
+ font: 11px var(--mono);
292
+ paint-order: stroke;
293
+ stroke: var(--ink);
294
+ stroke-width: 3px;
295
+ }
296
+ .edge--initial path {
297
+ stroke: var(--text-dim);
298
+ }
299
+ .edge--always path {
300
+ stroke-dasharray: 4 3;
301
+ }
302
+ .edge--done path {
303
+ stroke: var(--phosphor);
304
+ opacity: 0.75;
305
+ }
306
+ .edge--done .edge-label {
307
+ fill: var(--phosphor);
308
+ }
309
+ .edge--error path {
310
+ stroke: var(--red);
311
+ }
312
+ .edge--error .edge-label {
313
+ fill: var(--red);
314
+ }
315
+ .edge--after path {
316
+ stroke-dasharray: 1 3;
317
+ stroke-linecap: round;
318
+ }
319
+
320
+ marker path {
321
+ fill: var(--text-dim);
322
+ }
323
+ marker.arrow--done path {
324
+ fill: var(--phosphor);
325
+ }
326
+ marker.arrow--error path {
327
+ fill: var(--red);
328
+ }
329
+
330
+ /* ---- Nav (breadcrumb · connection · attention · token) ---- */
331
+
332
+ #breadcrumb {
333
+ display: flex;
334
+ align-items: baseline;
335
+ gap: 8px;
336
+ }
337
+ #breadcrumb a {
338
+ text-decoration: none;
339
+ }
340
+
341
+ /* The feed's state. `retrying` is normal and self-healing (EventSource reconnects on its own), so it
342
+ is a caution and not an error — the page is not lying about anything while it says so. `idle` is
343
+ `/`: no selection, no socket. */
344
+ .conn {
345
+ font-size: 11px;
346
+ letter-spacing: 0.04em;
347
+ color: var(--text-dim);
348
+ }
349
+ .conn--live {
350
+ color: var(--phosphor);
351
+ }
352
+ .conn--retrying {
353
+ color: var(--amber);
354
+ }
355
+
356
+ #attention-badge {
357
+ border-color: var(--amber);
358
+ color: var(--amber);
359
+ }
360
+ #attention-badge:hover {
361
+ border-color: var(--amber);
362
+ color: var(--ink);
363
+ background: var(--amber);
364
+ }
365
+
366
+ /* The token field (ADR-0032): the page's one credential, entered here and nowhere else. */
367
+ #token-box {
368
+ display: flex;
369
+ align-items: center;
370
+ gap: 6px;
371
+ }
372
+ #token {
373
+ font: inherit;
374
+ color: var(--text);
375
+ background: var(--ink);
376
+ border: 1px solid var(--line);
377
+ border-radius: 3px;
378
+ padding: 2px 8px;
379
+ width: 150px;
380
+ }
381
+ #token:focus {
382
+ outline: none;
383
+ border-color: var(--phosphor);
384
+ }
385
+ .token-state {
386
+ font-size: 11px;
387
+ letter-spacing: 0.04em;
388
+ min-width: 42px;
389
+ color: var(--text-dim);
390
+ }
391
+ .token-state--live {
392
+ color: var(--phosphor);
393
+ }
394
+ .token-state--invalid {
395
+ color: var(--red);
396
+ }
397
+
398
+ /* ---- Panels shared ---- */
399
+
400
+ #fleet,
401
+ #drawer {
402
+ width: 280px;
403
+ background: var(--ink-2);
404
+ overflow-y: auto;
405
+ display: flex;
406
+ flex-direction: column;
407
+ flex-shrink: 0;
408
+ }
409
+ #fleet {
410
+ border-right: 1px solid var(--line);
411
+ }
412
+ #drawer {
413
+ border-left: 1px solid var(--line);
414
+ }
415
+
416
+ .sidebar-head {
417
+ display: flex;
418
+ align-items: center;
419
+ justify-content: space-between;
420
+ padding: 10px 12px 6px;
421
+ }
422
+ .sidebar-head h2 {
423
+ margin: 0;
424
+ font-size: 11px;
425
+ font-weight: 600;
426
+ letter-spacing: 0.14em;
427
+ text-transform: uppercase;
428
+ color: var(--text-dim);
429
+ }
430
+
431
+ ul {
432
+ list-style: none;
433
+ margin: 0;
434
+ padding: 0 8px 8px;
435
+ }
436
+
437
+ .empty {
438
+ color: var(--text-dim);
439
+ cursor: default;
440
+ padding: 7px 9px;
441
+ }
442
+
443
+ #placeholder {
444
+ color: var(--text-dim);
445
+ padding: 14px 18px;
446
+ border: 1px dashed var(--line);
447
+ border-radius: 4px;
448
+ max-width: 320px;
449
+ }
450
+
451
+ /* ---- The fleet rail: workflows, each expandable to its runs ---- */
452
+
453
+ .wf {
454
+ margin: 2px 0;
455
+ border: 1px solid transparent;
456
+ border-radius: 4px;
457
+ }
458
+ .wf--selected {
459
+ border-color: var(--line);
460
+ background: rgba(78, 240, 167, 0.04);
461
+ }
462
+
463
+ .wf-head {
464
+ display: flex;
465
+ align-items: baseline;
466
+ gap: 6px;
467
+ padding: 6px 8px;
468
+ cursor: pointer;
469
+ }
470
+ .wf-head:hover .wf-name {
471
+ color: var(--phosphor);
472
+ }
473
+ .wf--selected .wf-name {
474
+ color: var(--phosphor);
475
+ }
476
+ .wf-caret {
477
+ border: none;
478
+ padding: 0 2px;
479
+ color: var(--text-dim);
480
+ }
481
+ .wf-name {
482
+ color: var(--text);
483
+ letter-spacing: 0.03em;
484
+ }
485
+ .wf-count {
486
+ color: var(--text-dim);
487
+ font-size: 11px;
488
+ }
489
+ .wf-start-btn {
490
+ margin-left: auto;
491
+ font-size: 11px;
492
+ padding: 0 6px;
493
+ }
494
+
495
+ .wf-runs {
496
+ padding: 0 4px 6px 18px;
497
+ }
498
+ .wf-runs li {
499
+ padding: 5px 8px;
500
+ border: 1px solid transparent;
501
+ border-radius: 4px;
502
+ cursor: pointer;
503
+ display: flex;
504
+ gap: 8px;
505
+ align-items: baseline;
506
+ }
507
+ .wf-runs li:hover {
508
+ border-color: var(--line);
509
+ }
510
+ .wf-runs li.selected {
511
+ border-color: var(--phosphor);
512
+ background: var(--phosphor-soft);
513
+ }
514
+ .wf-runs .run-id {
515
+ color: var(--text);
516
+ }
517
+ .wf-runs .run-status {
518
+ margin-left: auto;
519
+ font-size: 11px;
520
+ color: var(--text-dim);
521
+ }
522
+ .wf-runs li.selected .run-status {
523
+ color: var(--phosphor);
524
+ }
525
+ .wf-runs .run-status.status--error {
526
+ color: var(--red);
527
+ }
528
+ .wf-runs .run-status.status--done {
529
+ color: var(--phosphor);
530
+ }
531
+ /* Runs this page watched finish. Dimmed, but still selectable: where a run ENDED is worth reading,
532
+ and it is the client's own memory — a reload starts empty (ADR-0014/0022). */
533
+ .wf-runs li.settled .run-id {
534
+ color: var(--text-dim);
535
+ }
536
+
537
+ .gate-badge {
538
+ color: var(--amber);
539
+ font-size: 11px;
540
+ }
541
+
542
+ /* ---- The drawer: Attention (gate inbox) + Activity (emit log) ---- */
543
+
544
+ .drawer-tabs {
545
+ display: flex;
546
+ gap: 4px;
547
+ padding: 10px 12px 6px;
548
+ }
549
+ .drawer-tabs button {
550
+ font-size: 11px;
551
+ letter-spacing: 0.14em;
552
+ text-transform: uppercase;
553
+ color: var(--text-dim);
554
+ border-color: transparent;
555
+ }
556
+ .drawer-tabs button.tab--active {
557
+ color: var(--text);
558
+ border-color: var(--line);
559
+ }
560
+
561
+ .inbox-scope {
562
+ display: flex;
563
+ align-items: center;
564
+ gap: 6px;
565
+ padding: 2px 12px 6px;
566
+ color: var(--text-dim);
567
+ font-size: 11px;
568
+ cursor: pointer;
569
+ }
570
+
571
+ /* A gate card: one parked decision — meta to read, one form per accepted event. Emptied only by
572
+ the next frame's re-fetch coming back bare, never by this page's own bookkeeping (ADR-0032). */
573
+ .gate-card {
574
+ border: 1px solid var(--amber);
575
+ border-radius: 4px;
576
+ margin: 6px 0;
577
+ padding: 8px 10px;
578
+ background: var(--ink);
579
+ }
580
+ .gate-head {
581
+ display: flex;
582
+ align-items: baseline;
583
+ justify-content: space-between;
584
+ gap: 8px;
585
+ cursor: pointer;
586
+ }
587
+ .gate-id {
588
+ color: var(--amber);
589
+ }
590
+ .gate-head:hover .gate-id {
591
+ text-decoration: underline;
592
+ }
593
+ .gate-meta {
594
+ margin: 6px 0 0;
595
+ display: grid;
596
+ grid-template-columns: max-content 1fr;
597
+ gap: 2px 10px;
598
+ }
599
+ .gate-meta dt {
600
+ color: var(--text-dim);
601
+ }
602
+ .gate-meta dd {
603
+ margin: 0;
604
+ color: var(--text);
605
+ overflow-wrap: anywhere;
606
+ }
607
+ .gate-event {
608
+ margin-top: 8px;
609
+ border-top: 1px solid var(--line-soft);
610
+ padding-top: 6px;
611
+ }
612
+ .gate-event-name {
613
+ color: var(--blue);
614
+ margin-bottom: 4px;
615
+ }
616
+
617
+ #emit-log li {
618
+ padding: 5px 9px;
619
+ color: var(--amber);
620
+ border-left: 2px solid var(--amber);
621
+ margin: 4px 0;
622
+ animation: emit-in 0.3s ease-out;
623
+ }
624
+ #emit-log li.empty {
625
+ color: var(--text-dim);
626
+ border-left-color: transparent;
627
+ animation: none;
628
+ }
629
+ #emit-log .dim {
630
+ margin-left: 6px;
631
+ }
632
+
633
+ @keyframes emit-in {
634
+ from {
635
+ opacity: 0;
636
+ transform: translateX(6px);
637
+ }
638
+ to {
639
+ opacity: 1;
640
+ transform: none;
641
+ }
642
+ }
643
+
644
+ /* ---- The shared form (start-run + gate delivery — ADR-0033) ---- */
645
+
646
+ .start-form {
647
+ padding: 0 8px 8px;
648
+ color: var(--text-dim);
649
+ }
650
+
651
+ .schema-form {
652
+ display: flex;
653
+ flex-direction: column;
654
+ gap: 6px;
655
+ margin-top: 4px;
656
+ }
657
+ .schema-form label {
658
+ display: flex;
659
+ align-items: center;
660
+ gap: 8px;
661
+ }
662
+ .schema-form label > span {
663
+ color: var(--text-dim);
664
+ min-width: 72px;
665
+ }
666
+ .schema-form input[type="text"],
667
+ .schema-form input[type="number"],
668
+ .schema-form select,
669
+ .schema-form textarea {
670
+ font: inherit;
671
+ color: var(--text);
672
+ background: var(--ink);
673
+ border: 1px solid var(--line);
674
+ border-radius: 3px;
675
+ padding: 2px 8px;
676
+ flex: 1;
677
+ min-width: 0;
678
+ }
679
+ .schema-form textarea {
680
+ resize: vertical;
681
+ }
682
+ .schema-form input:focus,
683
+ .schema-form select:focus,
684
+ .schema-form textarea:focus {
685
+ outline: none;
686
+ border-color: var(--phosphor);
687
+ }
688
+ .schema-form button[type="submit"] {
689
+ align-self: flex-start;
690
+ }
691
+
692
+ /* The server's 400 body, rendered where the reader typed — it names the accepted shape, so the
693
+ error text IS the form's documentation. */
694
+ .form-error {
695
+ color: var(--red);
696
+ border: 1px solid var(--red);
697
+ border-radius: 3px;
698
+ padding: 4px 8px;
699
+ overflow-wrap: anywhere;
700
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ // The Console's checker config (ADR-0034): browser code, so it gets the DOM libs the package
3
+ // tsconfig deliberately withholds from Node code — which is why console/ has its own project
4
+ // instead of a place in the main include. `moduleResolution: "Bundler"` because the browser
5
+ // resolves bare specifiers through the page's import map, not Node's algorithm; imports keep
6
+ // their `.ts` extensions because the server serves the source erased, never compiled. `tsc`
7
+ // here is a checker, never an emitter — the typecheck script runs both projects.
8
+ "extends": "../../../tsconfig.base.json",
9
+ "compilerOptions": {
10
+ "rootDir": ".",
11
+ "lib": ["ES2023", "DOM", "DOM.Iterable"],
12
+ "module": "ESNext",
13
+ "moduleResolution": "Bundler",
14
+ "allowImportingTsExtensions": true,
15
+ "noEmit": true
16
+ },
17
+ "include": ["*.ts", "components/*.ts"]
18
+ }