@monster-orange/den 0.2.8 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/CHANGELOG.md +1731 -0
  2. package/README.md +4 -2
  3. package/bin/den +2 -1
  4. package/docs/distributions.md +730 -6
  5. package/make/den.mk +19 -4
  6. package/make/journeys.mk +2 -1
  7. package/make/local_dev.mk +6 -13
  8. package/make/secrets-1password.mk +18 -0
  9. package/make/test.mk +1 -1
  10. package/package.json +12 -2
  11. package/schemas/den.plugin.v1.json +156 -0
  12. package/schemas/den.theme.v1.json +269 -0
  13. package/scripts/auth-configure.ts +16 -8
  14. package/scripts/heal-config.ts +0 -5
  15. package/scripts/setup-1password.ts +41 -19
  16. package/scripts/setup-secrets.ts +67 -101
  17. package/src/pillars/api/global-setup.ts +1 -1
  18. package/src/pillars/journeys/sandbox.ts +23 -18
  19. package/src/pillars/journeys/tools/browser-session.ts +2 -2
  20. package/src/scripts/setup-default-workspace.ts +2 -2
  21. package/src/shared/agents/default-pack/be-dev.md +20 -0
  22. package/src/shared/agents/default-pack/devops.md +14 -0
  23. package/src/shared/agents/default-pack/fe-dev.md +20 -0
  24. package/src/shared/agents/default-pack/orchestrator.md +16 -0
  25. package/src/shared/agents/default-pack/planner.md +17 -0
  26. package/src/shared/agents/default-pack/reviewer.md +14 -0
  27. package/src/shared/agents/default-pack/ux-designer.md +15 -0
  28. package/src/shared/agents/den-claude.ts +173 -0
  29. package/src/shared/agents/generic.ts +52 -0
  30. package/src/shared/agents/personas.ts +218 -0
  31. package/src/shared/agents/records.ts +118 -0
  32. package/src/shared/agents/runtime.ts +192 -0
  33. package/src/shared/agents/runtimes.ts +47 -0
  34. package/src/shared/auth/headless-login.ts +1 -1
  35. package/src/shared/auth/sudo-vault.ts +93 -31
  36. package/src/shared/auth/token-providers/index.ts +7 -3
  37. package/src/shared/brand.ts +7 -0
  38. package/src/shared/config-error.ts +33 -3
  39. package/src/shared/constants.ts +3 -24
  40. package/src/shared/den-config.ts +257 -5
  41. package/src/shared/edge/config.ts +147 -21
  42. package/src/shared/edge/exec.ts +31 -0
  43. package/src/shared/edge/included.ts +169 -0
  44. package/src/shared/edge/render.ts +69 -14
  45. package/src/shared/edge/runtime.ts +128 -121
  46. package/src/shared/edge/status.ts +68 -8
  47. package/src/shared/edge/tls.ts +328 -0
  48. package/src/shared/edge/types.ts +25 -9
  49. package/src/shared/engine.ts +23 -0
  50. package/src/shared/environment-manager/default-workspace.ts +16 -1
  51. package/src/shared/environment-manager/health.ts +73 -38
  52. package/src/shared/environment-manager/scope.ts +168 -0
  53. package/src/shared/environment-manager/service-runner.ts +789 -43
  54. package/src/shared/environment-manager/up-targets.ts +132 -0
  55. package/src/shared/environment-manager/workspaces.ts +19 -0
  56. package/src/shared/health/rollup.ts +48 -0
  57. package/src/shared/open-url.ts +21 -2
  58. package/src/shared/org-sources.ts +78 -59
  59. package/src/shared/org.ts +53 -5
  60. package/src/shared/plugins/manifest.ts +406 -0
  61. package/src/shared/plugins/store.ts +358 -0
  62. package/src/shared/process/legacy-adopt.ts +285 -0
  63. package/src/shared/process/process-group.ts +202 -0
  64. package/src/shared/process/spawn-streaming.ts +7 -0
  65. package/src/shared/secret-env.ts +70 -0
  66. package/src/shared/secrets/doppler.ts +1 -1
  67. package/src/shared/secrets/index.ts +279 -50
  68. package/src/shared/secrets/none.ts +56 -0
  69. package/src/shared/secrets/strategies.ts +202 -0
  70. package/src/shared/secrets/types.ts +1 -1
  71. package/src/shared/sessions/client.ts +277 -0
  72. package/src/shared/sessions/den-pty.ts +97 -0
  73. package/src/shared/sessions/env.ts +79 -0
  74. package/src/shared/sessions/host.ts +667 -0
  75. package/src/shared/sessions/names.ts +85 -0
  76. package/src/shared/sessions/paths.ts +27 -0
  77. package/src/shared/sessions/protocol.ts +72 -0
  78. package/src/shared/sessions/pty.ts +141 -0
  79. package/src/shared/sessions/ring-buffer.ts +160 -0
  80. package/src/shared/sessions/types.ts +81 -0
  81. package/src/shared/smoke/git-remote.ts +40 -0
  82. package/src/shared/stacks/manifest-commands.ts +1 -1
  83. package/src/shared/stacks/namespace.ts +178 -0
  84. package/src/shared/stacks/owners.ts +303 -0
  85. package/src/shared/stacks/render.ts +8 -3
  86. package/src/shared/stacks/runtime.ts +100 -16
  87. package/src/shared/stacks/status.ts +63 -12
  88. package/src/shared/strategies/registry.ts +89 -0
  89. package/src/shared/theme/contract.ts +538 -0
  90. package/src/shared/types/index.ts +12 -3
  91. package/src/ui/cli/agents-command.ts +812 -0
  92. package/src/ui/cli/create.ts +24 -7
  93. package/src/ui/cli/doctor-command.ts +260 -0
  94. package/src/ui/cli/edge-command.ts +56 -10
  95. package/src/ui/cli/env-probe.ts +266 -0
  96. package/src/ui/cli/flag-parser.ts +18 -5
  97. package/src/ui/cli/health-display.ts +25 -0
  98. package/src/ui/cli/index.ts +580 -60
  99. package/src/ui/cli/notice.ts +20 -0
  100. package/src/ui/cli/plugin-command.ts +849 -0
  101. package/src/ui/cli/sessions-command.ts +718 -0
  102. package/src/ui/cli/stack-command.ts +63 -2
  103. package/src/ui/cli/test-runner.ts +11 -9
  104. package/src/ui/cli/theme-command.ts +136 -0
  105. package/src/ui/cli/web-command.ts +47 -23
  106. package/src/ui/mcp/api.ts +28 -2
  107. package/src/ui/mcp/scope.ts +145 -20
  108. package/src/ui/mcp/server.ts +27 -2
  109. package/src/ui/mcp/tools.ts +94 -12
  110. package/src/ui/server/appearance-prefs.ts +95 -0
  111. package/src/ui/server/audit.ts +35 -0
  112. package/src/ui/server/daemon-reaper.ts +29 -1
  113. package/src/ui/server/engine-stamp.ts +83 -0
  114. package/src/ui/server/github-service.ts +23 -44
  115. package/src/ui/server/graph-page.ts +20 -0
  116. package/src/ui/server/graph-prefs.ts +44 -1
  117. package/src/ui/server/http-errors.ts +293 -0
  118. package/src/ui/server/http-helpers.ts +22 -3
  119. package/src/ui/server/index.ts +539 -51
  120. package/src/ui/server/public/appearance.js +531 -0
  121. package/src/ui/server/public/edge-page.js +248 -0
  122. package/src/ui/server/public/edge.html +251 -0
  123. package/src/ui/server/public/environments.html +65 -83
  124. package/src/ui/server/public/error-page.js +247 -0
  125. package/src/ui/server/public/error.html +260 -0
  126. package/src/ui/server/public/graph-model.js +332 -14
  127. package/src/ui/server/public/graph.css +141 -47
  128. package/src/ui/server/public/graph.js +223 -44
  129. package/src/ui/server/public/header.js +396 -600
  130. package/src/ui/server/public/health-fit.js +248 -0
  131. package/src/ui/server/public/health-rollup.js +128 -0
  132. package/src/ui/server/public/i18n/es.js +287 -0
  133. package/src/ui/server/public/i18n/ja.js +276 -0
  134. package/src/ui/server/public/index.html +139 -438
  135. package/src/ui/server/public/local-dev.html +3468 -4044
  136. package/src/ui/server/public/logs.html +71 -208
  137. package/src/ui/server/public/panel.js +14 -2
  138. package/src/ui/server/public/release.html +175 -389
  139. package/src/ui/server/public/services.html +596 -770
  140. package/src/ui/server/public/settings-page.js +759 -0
  141. package/src/ui/server/public/settings-registry.js +521 -0
  142. package/src/ui/server/public/settings.html +590 -0
  143. package/src/ui/server/public/shell.js +3869 -0
  144. package/src/ui/server/public/topbar.js +1413 -0
  145. package/src/ui/server/public/version-label.js +12 -0
  146. package/src/ui/server/public/workspace-picker.js +74 -34
  147. package/src/ui/server/readonly-routes.ts +37 -3
  148. package/src/ui/server/services-list.ts +94 -17
  149. package/src/ui/server/settings-distribution.ts +74 -0
  150. package/src/ui/server/shell-prefs.ts +192 -0
  151. package/src/ui/server/theme-routes.ts +292 -0
  152. package/src/ui/server/theme-runtime.ts +407 -0
  153. package/src/ui/server/ui-assets.ts +16 -1
  154. package/src/ui/server/ui-language.ts +50 -0
  155. package/src/ui/server/workspace-routes.ts +48 -18
  156. package/themes/den/den-theme.json +72 -0
  157. package/themes/den/favicon.ico +0 -0
  158. package/themes/den/mark.svg +6 -0
@@ -3,8 +3,30 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <script>
7
+ // Mode before first paint: yours (the server fills it in below),
8
+ // else the system's, else the theme's default, else dark.
9
+ (function () {
10
+ var m = "@@DEN_MODE@@";
11
+ if (m !== "dark" && m !== "light") {
12
+ var q = function (s) {
13
+ return (
14
+ window.matchMedia &&
15
+ matchMedia("(prefers-color-scheme: " + s + ")").matches
16
+ );
17
+ };
18
+ m = q("light")
19
+ ? "light"
20
+ : q("dark")
21
+ ? "dark"
22
+ : "@@DEN_DEFAULT_MODE@@";
23
+ }
24
+ document.documentElement.dataset.mode =
25
+ m === "light" ? "light" : "dark";
26
+ })();
27
+ </script>
28
+ <link rel="stylesheet" href="/theme.css" id="theme-css" />
6
29
  <title>__DEN_TITLE__ · Logs</title>
7
- <link rel="icon" href="/favicon.ico?v=2" />
8
30
  <style>
9
31
  *,
10
32
  *::before,
@@ -14,25 +36,12 @@
14
36
  padding: 0;
15
37
  }
16
38
 
17
- :root {
18
- --bg: #0d1117;
19
- --surface: #161b22;
20
- --border: #30363d;
21
- --text: #c9d1d9;
22
- --muted: #8b949e;
23
- --accent: #58a6ff;
24
- --green: #3fb950;
25
- --red: #f85149;
26
- --yellow: #d29922;
27
- --mono: "SF Mono", "Cascadia Code", "Fira Code", "Menlo", monospace;
28
- }
29
-
30
39
  html,
31
40
  body {
32
41
  height: 100%;
33
42
  background: var(--bg);
34
43
  color: var(--text);
35
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
44
+ font-family: var(--sans);
36
45
  font-size: 13px;
37
46
  }
38
47
  body {
@@ -42,51 +51,13 @@
42
51
  }
43
52
 
44
53
  /* ── Header ───────────────────────────────────────────────────────────── */
45
- header {
46
- display: flex;
47
- align-items: center;
48
- gap: 10px;
49
- padding: 7px 14px;
50
- background: var(--surface);
51
- border-bottom: 1px solid var(--border);
52
- flex-shrink: 0;
53
- }
54
- header h1 {
55
- font-family: var(--mono);
56
- font-size: 14px;
57
- font-weight: 600;
58
- letter-spacing: -0.02em;
59
- }
60
54
  .h-spacer {
61
55
  flex: 1;
62
56
  }
63
- nav {
64
- display: flex;
65
- gap: 2px;
66
- }
67
- nav a {
68
- color: var(--muted);
69
- text-decoration: none;
70
- padding: 3px 10px;
71
- border-radius: 4px;
72
- font-size: 12px;
73
- font-weight: 500;
74
- transition: color 0.1s;
75
- display: flex;
76
- align-items: center;
77
- gap: 5px;
78
- }
79
- nav a:hover {
80
- color: var(--text);
81
- background: var(--border);
82
- }
83
- nav a.active {
84
- color: var(--text);
85
- }
86
57
 
87
58
  .badge {
88
- background: var(--yellow);
89
- color: #0d1117;
59
+ background: var(--warn);
60
+ color: var(--bg);
90
61
  font-size: 10px;
91
62
  font-weight: 700;
92
63
  padding: 1px 5px;
@@ -99,35 +70,6 @@
99
70
  }
100
71
 
101
72
  /* ── Health dots ──────────────────────────────────────────────────────── */
102
- .health {
103
- display: flex;
104
- align-items: center;
105
- gap: 10px;
106
- padding: 0 4px;
107
- }
108
- .health-item {
109
- display: flex;
110
- align-items: center;
111
- gap: 5px;
112
- font-size: 11px;
113
- color: var(--muted);
114
- }
115
- .health-group {
116
- display: flex;
117
- align-items: center;
118
- gap: 4px;
119
- font-size: 11px;
120
- color: var(--muted);
121
- }
122
- .health-group-label {
123
- color: var(--muted);
124
- margin-right: 1px;
125
- }
126
- .health-group-dots {
127
- display: flex;
128
- align-items: center;
129
- gap: 3px;
130
- }
131
73
  .dot {
132
74
  width: 7px;
133
75
  height: 7px;
@@ -137,13 +79,13 @@
137
79
  display: inline-block;
138
80
  }
139
81
  .dot.ok {
140
- background: var(--green);
82
+ background: var(--ok);
141
83
  }
142
84
  .dot.err {
143
- background: var(--red);
85
+ background: var(--bad);
144
86
  }
145
87
  .dot.running {
146
- background: var(--yellow);
88
+ background: var(--warn);
147
89
  animation: pulse 1s ease-in-out infinite;
148
90
  }
149
91
  @keyframes pulse {
@@ -156,55 +98,9 @@
156
98
  }
157
99
  }
158
100
 
159
- /* ── Sub-bar ──────────────────────────────────────────────────────────── */
160
- .subbar {
161
- display: flex;
162
- align-items: center;
163
- gap: 8px;
164
- padding: 5px 14px;
165
- background: var(--surface);
166
- border-bottom: 1px solid var(--border);
167
- flex-shrink: 0;
168
- }
169
- .spacer {
170
- flex: 1;
171
- }
172
-
173
- .env-badge {
174
- display: inline-flex;
175
- align-items: center;
176
- gap: 5px;
177
- padding: 2px 8px 2px 6px;
178
- border-radius: 999px;
179
- background: var(--surface);
180
- border: 1px solid var(--border);
181
- font-size: 11px;
182
- font-weight: 500;
183
- color: var(--muted);
184
- }
185
- .env-badge::before {
186
- content: "env";
187
- font-size: 9px;
188
- font-weight: 600;
189
- letter-spacing: 0.05em;
190
- color: var(--muted);
191
- opacity: 0.6;
192
- text-transform: uppercase;
193
- }
194
- .env-badge select {
195
- background: transparent;
196
- border: none;
197
- outline: none;
198
- color: var(--text);
199
- font-size: 11px;
200
- font-weight: 600;
201
- cursor: pointer;
202
- padding: 0;
203
- -webkit-appearance: none;
204
- appearance: none;
205
- }
206
- .env-badge select option {
207
- background: var(--surface);
101
+ /* The layout toggle sits last in the toolbar, after the search. */
102
+ #log-dir-seg {
103
+ order: 100;
208
104
  }
209
105
 
210
106
  .seg {
@@ -279,7 +175,7 @@
279
175
  cursor: pointer;
280
176
  }
281
177
  .hist-search-wrap button:hover {
282
- color: var(--accent);
178
+ color: var(--accent-text);
283
179
  border-color: var(--accent);
284
180
  }
285
181
  .hist-btn {
@@ -310,18 +206,18 @@
310
206
  }
311
207
  .cmd-btn:hover:not(:disabled) {
312
208
  border-color: var(--accent);
313
- color: var(--accent);
209
+ color: var(--accent-text);
314
210
  }
315
211
  .logs-btn {
316
212
  border-color: var(--muted);
317
213
  }
318
214
  .logs-btn:hover:not(:disabled) {
319
- border-color: var(--yellow) !important;
320
- color: var(--yellow) !important;
215
+ border-color: var(--warn) !important;
216
+ color: var(--warn) !important;
321
217
  }
322
218
  .logs-btn.streaming {
323
- border-color: var(--yellow);
324
- color: var(--yellow);
219
+ border-color: var(--warn);
220
+ color: var(--warn);
325
221
  }
326
222
 
327
223
  /* ── Logs area ────────────────────────────────────────────────────────── */
@@ -438,15 +334,15 @@
438
334
  display: none !important;
439
335
  }
440
336
  .log-line-match mark {
441
- background: color-mix(in srgb, var(--yellow) 30%, transparent);
337
+ background: color-mix(in srgb, var(--warn) 30%, transparent);
442
338
  color: inherit;
443
339
  border-radius: 2px;
444
340
  }
445
341
  /* Search-all results need real contrast — the filter's 30% wash is
446
342
  invisible in a dense results pane on the dark background. */
447
343
  .search-hit mark {
448
- background: var(--yellow);
449
- color: #0d1117;
344
+ background: var(--warn);
345
+ color: var(--bg);
450
346
  border-radius: 2px;
451
347
  font-weight: 600;
452
348
  padding: 0 1px;
@@ -464,14 +360,14 @@
464
360
  min-height: 0;
465
361
  }
466
362
  .log-output .log-cmd {
467
- color: var(--accent);
363
+ color: var(--accent-text);
468
364
  }
469
365
  .log-output .log-err {
470
- color: var(--red);
366
+ color: var(--bad);
471
367
  }
472
368
  /* An action an agent asked for (via the MCP server). */
473
369
  .log-output .log-agent {
474
- color: var(--yellow);
370
+ color: var(--warn);
475
371
  }
476
372
  .log-output .log-done {
477
373
  color: var(--muted);
@@ -484,12 +380,12 @@
484
380
  align-items: center;
485
381
  gap: 8px;
486
382
  padding: 8px 12px;
487
- background: color-mix(in srgb, var(--yellow) 12%, var(--bg));
488
- border-top: 1px solid color-mix(in srgb, var(--yellow) 40%, transparent);
383
+ background: color-mix(in srgb, var(--warn) 12%, var(--bg));
384
+ border-top: 1px solid color-mix(in srgb, var(--warn) 40%, transparent);
489
385
  }
490
386
  .aws-auth-msg {
491
387
  font-size: 12px;
492
- color: var(--yellow);
388
+ color: var(--warn);
493
389
  flex: 1;
494
390
  }
495
391
  .aws-auth-btn {
@@ -497,8 +393,8 @@
497
393
  padding: 3px 10px;
498
394
  border-radius: 4px;
499
395
  cursor: pointer;
500
- background: var(--yellow);
501
- color: #000;
396
+ background: var(--warn);
397
+ color: var(--bg);
502
398
  border: none;
503
399
  font-weight: 600;
504
400
  }
@@ -531,54 +427,22 @@
531
427
  </style>
532
428
  </head>
533
429
  <body>
534
- <header>
535
- <img
536
- src="/favicon.ico"
537
- width="16"
538
- height="16"
539
- alt=""
540
- style="flex-shrink: 0"
541
- />
542
- <h1>__DEN_CLI__</h1>
543
- <button
544
- class="v-pill"
545
- id="version-pill"
546
- onclick="openChangelog()"
547
- title="View changelog"
548
- >
549
- <span class="v-pip" id="v-pip"></span><span class="v-txt">v…</span>
550
- </button>
551
- <div class="health" id="health" title="Service health"></div>
552
- <div class="h-spacer"></div>
553
- <nav>
554
- <a href="/">Local Dev</a>
555
- <a href="/services">Services</a>
556
- <a href="/environments">Environments</a>
557
- <a href="/release">Releases</a>
558
- <a href="/logs" class="active"
559
- >Logs <span class="badge visible" id="log-badge"></span
560
- ></a>
561
- <a href="/tests">Tests</a>
562
- </nav>
563
- </header>
564
-
565
- <div class="subbar">
566
- <div class="env-badge">
567
- <select id="env-sel" title="Environment">
568
- <option value="local">local</option>
569
- </select>
570
- </div>
571
- <div class="spacer"></div>
430
+ <!-- The shell: top bar and rail, the same on every page (shell.js). -->
431
+ <div id="den-shell" data-page="logs"></div>
432
+ <script src="/shell.js"></script>
433
+
434
+ <!-- The page's one main landmark: everything after the shell. -->
435
+ <main id="den-content">
436
+ <!-- One bar: the streams, the history search and the layout toggle
437
+ (which had a near-empty band of its own). -->
438
+ <div class="logs-toolbar" id="logs-toolbar">
439
+ <span class="logs-label">streams</span>
572
440
  <div class="seg" id="log-dir-seg">
573
441
  <button data-dir="cols" class="active" title="Side by side">
574
442
  &#9134; cols
575
443
  </button>
576
444
  <button data-dir="rows" title="Stacked">&#9135; rows</button>
577
445
  </div>
578
- </div>
579
-
580
- <div class="logs-toolbar" id="logs-toolbar">
581
- <span class="logs-label">streams</span>
582
446
  <span class="hist-search-wrap">
583
447
  <input
584
448
  id="hist-search"
@@ -609,6 +473,8 @@
609
473
  applyDenConfig,
610
474
  initChangelog,
611
475
  computePrimaryByRepo,
476
+ envReady,
477
+ initWorkspacePill,
612
478
  } from "/header.js";
613
479
 
614
480
  // ── Safe highlight rendering ──────────────────────────────────────────────
@@ -707,15 +573,9 @@
707
573
 
708
574
  // ── Init ──────────────────────────────────────────────────────────────────
709
575
  async function init() {
710
- const envs = await fetch("/api/environments")
711
- .then((r) => r.json())
712
- .catch(() => ["local"]);
576
+ // The shell's env picker (header.js), one for every page.
577
+ await envReady();
713
578
  const envSel = document.getElementById("env-sel");
714
- envSel.innerHTML = envs
715
- .map((e) => `<option value="${e}">${e}</option>`)
716
- .join("");
717
- const savedEnv = localStorage.getItem("__DEN_CLI__-env");
718
- if (savedEnv && envs.includes(savedEnv)) envSel.value = savedEnv;
719
579
 
720
580
  const savedDir = localStorage.getItem("__DEN_CLI__-log-dir") || "cols";
721
581
  applyLogDir(savedDir);
@@ -724,6 +584,7 @@
724
584
  loadLogToolbar(),
725
585
  loadLogSettings(),
726
586
  initHealth(),
587
+ initWorkspacePill(),
727
588
  applyDenConfig(),
728
589
  initChangelog(),
729
590
  ]);
@@ -782,7 +643,6 @@
782
643
  // ── Env ───────────────────────────────────────────────────────────────────
783
644
  document.getElementById("env-sel").addEventListener("change", () => {
784
645
  const env = document.getElementById("env-sel").value;
785
- localStorage.setItem("__DEN_CLI__-env", env);
786
646
  // Close all open streams (they're env-specific) — except __DEN_CLI__/server
787
647
  // which is env-agnostic and should persist across env switches.
788
648
  for (const [key] of [...logStreams]) {
@@ -824,6 +684,8 @@
824
684
  services = await fetch(`/api/services?env=${env}`).then((r) =>
825
685
  r.json(),
826
686
  );
687
+ // An error answer (an invalid config: 422) lists nothing.
688
+ if (!Array.isArray(services)) services = [];
827
689
  } catch {
828
690
  services = [];
829
691
  }
@@ -1391,10 +1253,10 @@
1391
1253
  env,
1392
1254
  targets: targets.join(","),
1393
1255
  });
1394
- const data = await fetch(`/api/logs/search?${params}`).then((r) => {
1395
- if (!r.ok) throw new Error(`search failed (${r.status})`);
1396
- return r.json();
1397
- });
1256
+ const data = await window.denShell.api(
1257
+ "GET",
1258
+ `/api/logs/search?${params}`,
1259
+ );
1398
1260
  let total = 0;
1399
1261
  for (const result of data.results) {
1400
1262
  if (result.error) {
@@ -1492,5 +1354,6 @@
1492
1354
 
1493
1355
  init();
1494
1356
  </script>
1357
+ </main>
1495
1358
  </body>
1496
1359
  </html>
@@ -294,6 +294,18 @@ export const ACTIONS = [
294
294
  },
295
295
  ];
296
296
 
297
+ /**
298
+ * Pin or unpin to the rail (R14): the shell's Pinned group. A preference
299
+ * only (/api/settings `shell.pins`). Absent outside the shell.
300
+ */
301
+ function pinButton(id) {
302
+ const sh = typeof window !== "undefined" ? window.denShell : null;
303
+ if (!sh) return "";
304
+ const on = sh.isPinned(id);
305
+ const label = `${on ? "Unpin" : "Pin"} ${id}`;
306
+ return `<button class="dg-ib" data-pin-toggle="${esc(id)}" aria-pressed="${on}" title="${esc(label)}" aria-label="${esc(label)}">${on ? "★" : "☆"}</button>`;
307
+ }
308
+
297
309
  /** The manifest's own commands (setup, seed, reset-db, …) become actions too. */
298
310
  function commandActions(s) {
299
311
  if (!runsHere(s)) return [];
@@ -660,9 +672,9 @@ export function renderPanel(el, sel) {
660
672
  ? "bad"
661
673
  : "";
662
674
  el.innerHTML = `${st.popped ? "" : '<div class="dg-resize" data-resize title="Drag to resize"></div>'}<div class="dg-dh">
663
- <nav class="dg-crumbs" aria-label="Location"><span style="color:${esc(org.color)}">${esc(org.label)}</span> › <span>${esc(s0.app)}</span>${kind === "service" && m.byRepo.get(x.repo).length > 1 ? ` › <button data-goto-repo-panel="${esc(x.repo)}">${esc(x.repo)}</button>` : ""}</nav>
675
+ <nav class="dg-crumbs" aria-label="Location"><span class="dg-crumb-org"><i style="background:${esc(org.color)}"></i>${esc(org.label)}</span> › <span>${esc(s0.app)}</span>${kind === "service" && m.byRepo.get(x.repo).length > 1 ? ` › <button data-goto-repo-panel="${esc(x.repo)}">${esc(x.repo)}</button>` : ""}</nav>
664
676
  <div class="t"><h2>${esc(kind === "repo" ? x.repo : x.name)}</h2><span class="dg-pill ${pillCls}">${esc(word)}</span>${place !== "local" || m.mixed ? `<span class="dg-badge ${place}">${esc(placeLabel)}</span>` : ""}
665
- <span class="dg-hbtns"><button class="dg-ib" data-popout title="${st.popped ? "Dock as side panel" : "Expand to pop-up"}" aria-label="${st.popped ? "Dock as side panel" : "Expand to pop-up"}">${st.popped ? "⇲" : "⤢"}</button><button class="dg-ib" data-close aria-label="Close details">×</button></span></div>
677
+ <span class="dg-hbtns">${pinButton(kind === "repo" ? x.repo : x.id)}<button class="dg-ib" data-popout title="${st.popped ? "Dock as side panel" : "Expand to pop-up"}" aria-label="${st.popped ? "Dock as side panel" : "Expand to pop-up"}">${st.popped ? "⇲" : "⤢"}</button><button class="dg-ib" data-close aria-label="Close details">×</button></span></div>
666
678
  <div class="sub">${esc(kind === "repo" ? `${x.services.length} services · ${s0.path}` : x.id)}</div>
667
679
  ${actionBar(x)}
668
680
  <div class="dg-tabs" role="tablist">${tabs.map((t) => `<button role="tab" aria-selected="${t.id === st.tab}" data-tab="${t.id}">${esc(t.label)}</button>`).join("")}</div>