@monster-orange/den 0.2.7 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1655 -0
- package/README.md +4 -2
- package/bin/den +2 -1
- package/docs/distributions.md +619 -3
- package/make/den.mk +19 -4
- package/make/journeys.mk +2 -1
- package/make/local_dev.mk +6 -13
- package/make/secrets-1password.mk +18 -0
- package/make/test.mk +1 -1
- package/package.json +9 -2
- package/schemas/den.plugin.v1.json +156 -0
- package/schemas/den.theme.v1.json +269 -0
- package/scripts/auth-configure.ts +16 -8
- package/scripts/heal-config.ts +0 -5
- package/scripts/setup-1password.ts +41 -19
- package/scripts/setup-secrets.ts +67 -101
- package/src/pillars/api/global-setup.ts +1 -1
- package/src/pillars/journeys/sandbox.ts +23 -18
- package/src/pillars/journeys/tools/browser-session.ts +2 -2
- package/src/scripts/setup-default-workspace.ts +2 -2
- package/src/shared/agent-actor.ts +38 -3
- package/src/shared/auth/headless-login.ts +1 -1
- package/src/shared/auth/sudo-vault.ts +93 -31
- package/src/shared/auth/token-providers/index.ts +7 -3
- package/src/shared/brand.ts +7 -0
- package/src/shared/config-error.ts +63 -0
- package/src/shared/constants.ts +3 -24
- package/src/shared/den-config.ts +185 -7
- package/src/shared/edge/config.ts +147 -21
- package/src/shared/edge/exec.ts +31 -0
- package/src/shared/edge/included.ts +169 -0
- package/src/shared/edge/render.ts +69 -14
- package/src/shared/edge/runtime.ts +128 -121
- package/src/shared/edge/status.ts +68 -8
- package/src/shared/edge/tls.ts +328 -0
- package/src/shared/edge/types.ts +25 -9
- package/src/shared/environment-manager/catalog.ts +52 -1
- package/src/shared/environment-manager/default-workspace.ts +26 -3
- package/src/shared/environment-manager/git-remote.ts +49 -0
- package/src/shared/environment-manager/health.ts +73 -38
- package/src/shared/environment-manager/scope.ts +168 -0
- package/src/shared/environment-manager/service-runner.ts +789 -43
- package/src/shared/environment-manager/up-targets.ts +132 -0
- package/src/shared/environment-manager/workspace-manager.ts +67 -25
- package/src/shared/environment-manager/workspace-org.ts +26 -11
- package/src/shared/health/rollup.ts +48 -0
- package/src/shared/manifest/service-links.ts +20 -8
- package/src/shared/open-url.ts +21 -2
- package/src/shared/org-sources.ts +328 -0
- package/src/shared/org.ts +53 -5
- package/src/shared/plugins/manifest.ts +406 -0
- package/src/shared/plugins/store.ts +358 -0
- package/src/shared/process/legacy-adopt.ts +285 -0
- package/src/shared/process/process-group.ts +202 -0
- package/src/shared/process/spawn-streaming.ts +7 -0
- package/src/shared/secrets/doppler.ts +1 -1
- package/src/shared/secrets/index.ts +279 -50
- package/src/shared/secrets/none.ts +56 -0
- package/src/shared/secrets/strategies.ts +202 -0
- package/src/shared/secrets/types.ts +1 -1
- package/src/shared/smoke/git-remote.ts +40 -0
- package/src/shared/stacks/manifest-commands.ts +1 -1
- package/src/shared/stacks/namespace.ts +178 -0
- package/src/shared/stacks/owners.ts +303 -0
- package/src/shared/stacks/render.ts +8 -3
- package/src/shared/stacks/runtime.ts +100 -16
- package/src/shared/stacks/status.ts +63 -12
- package/src/shared/strategies/registry.ts +89 -0
- package/src/shared/theme/contract.ts +538 -0
- package/src/shared/types/index.ts +24 -5
- package/src/ui/cli/clone-plan.ts +154 -23
- package/src/ui/cli/create.ts +24 -7
- package/src/ui/cli/doctor-command.ts +131 -0
- package/src/ui/cli/edge-command.ts +56 -10
- package/src/ui/cli/flag-parser.ts +62 -3
- package/src/ui/cli/health-display.ts +25 -0
- package/src/ui/cli/index.ts +649 -72
- package/src/ui/cli/notice.ts +20 -0
- package/src/ui/cli/org-command.ts +12 -2
- package/src/ui/cli/plugin-command.ts +849 -0
- package/src/ui/cli/stack-command.ts +63 -2
- package/src/ui/cli/test-runner.ts +11 -9
- package/src/ui/cli/theme-command.ts +136 -0
- package/src/ui/cli/up-args.ts +15 -0
- package/src/ui/cli/web-command.ts +47 -23
- package/src/ui/mcp/api.ts +28 -2
- package/src/ui/mcp/scope.ts +298 -0
- package/src/ui/mcp/server.ts +70 -8
- package/src/ui/mcp/tools.ts +151 -24
- package/src/ui/server/appearance-prefs.ts +95 -0
- package/src/ui/server/audit.ts +90 -3
- package/src/ui/server/daemon-reaper.ts +29 -1
- package/src/ui/server/engine-stamp.ts +83 -0
- package/src/ui/server/github-service.ts +29 -45
- package/src/ui/server/graph-page.ts +20 -0
- package/src/ui/server/graph-prefs.ts +44 -1
- package/src/ui/server/http-errors.ts +293 -0
- package/src/ui/server/http-helpers.ts +22 -3
- package/src/ui/server/index.ts +590 -45
- package/src/ui/server/public/appearance.js +531 -0
- package/src/ui/server/public/edge-page.js +248 -0
- package/src/ui/server/public/edge.html +251 -0
- package/src/ui/server/public/environments.html +65 -83
- package/src/ui/server/public/error-page.js +247 -0
- package/src/ui/server/public/error.html +260 -0
- package/src/ui/server/public/graph-model.js +332 -14
- package/src/ui/server/public/graph.css +141 -47
- package/src/ui/server/public/graph.js +223 -44
- package/src/ui/server/public/header.js +396 -600
- package/src/ui/server/public/health-fit.js +248 -0
- package/src/ui/server/public/health-rollup.js +128 -0
- package/src/ui/server/public/i18n/es.js +287 -0
- package/src/ui/server/public/i18n/ja.js +276 -0
- package/src/ui/server/public/index.html +139 -438
- package/src/ui/server/public/local-dev.html +3468 -4044
- package/src/ui/server/public/logs.html +71 -208
- package/src/ui/server/public/panel.js +15 -3
- package/src/ui/server/public/release.html +175 -389
- package/src/ui/server/public/services.html +596 -770
- package/src/ui/server/public/settings-page.js +759 -0
- package/src/ui/server/public/settings-registry.js +521 -0
- package/src/ui/server/public/settings.html +590 -0
- package/src/ui/server/public/shell.js +3869 -0
- package/src/ui/server/public/topbar.js +1413 -0
- package/src/ui/server/public/version-label.js +12 -0
- package/src/ui/server/public/workspace-picker.js +74 -34
- package/src/ui/server/readonly-routes.ts +24 -4
- package/src/ui/server/release-service.ts +25 -3
- package/src/ui/server/services-list.ts +94 -17
- package/src/ui/server/settings-distribution.ts +74 -0
- package/src/ui/server/shell-prefs.ts +192 -0
- package/src/ui/server/theme-routes.ts +292 -0
- package/src/ui/server/theme-runtime.ts +407 -0
- package/src/ui/server/ui-assets.ts +16 -1
- package/src/ui/server/ui-language.ts +50 -0
- package/src/ui/server/workspace-routes.ts +55 -9
- package/themes/den/den-theme.json +72 -0
- package/themes/den/favicon.ico +0 -0
- package/themes/den/mark.svg +6 -0
|
@@ -3,6 +3,29 @@
|
|
|
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__ · Releases</title>
|
|
7
30
|
<style>
|
|
8
31
|
*,
|
|
@@ -13,25 +36,12 @@
|
|
|
13
36
|
padding: 0;
|
|
14
37
|
}
|
|
15
38
|
|
|
16
|
-
:root {
|
|
17
|
-
--bg: #0d1117;
|
|
18
|
-
--surface: #161b22;
|
|
19
|
-
--border: #30363d;
|
|
20
|
-
--text: #c9d1d9;
|
|
21
|
-
--muted: #8b949e;
|
|
22
|
-
--accent: #58a6ff;
|
|
23
|
-
--green: #3fb950;
|
|
24
|
-
--red: #f85149;
|
|
25
|
-
--yellow: #d29922;
|
|
26
|
-
--mono: "SF Mono", "Cascadia Code", "Fira Code", "Menlo", monospace;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
39
|
html,
|
|
30
40
|
body {
|
|
31
41
|
height: 100%;
|
|
32
42
|
background: var(--bg);
|
|
33
43
|
color: var(--text);
|
|
34
|
-
font-family:
|
|
44
|
+
font-family: var(--sans);
|
|
35
45
|
font-size: 13px;
|
|
36
46
|
}
|
|
37
47
|
body {
|
|
@@ -41,69 +51,11 @@
|
|
|
41
51
|
}
|
|
42
52
|
|
|
43
53
|
/* ── Header ───────────────────────────────────────────────────────────── */
|
|
44
|
-
header {
|
|
45
|
-
display: flex;
|
|
46
|
-
align-items: center;
|
|
47
|
-
gap: 10px;
|
|
48
|
-
padding: 7px 14px;
|
|
49
|
-
background: var(--surface);
|
|
50
|
-
border-bottom: 1px solid var(--border);
|
|
51
|
-
flex-shrink: 0;
|
|
52
|
-
}
|
|
53
|
-
header h1 {
|
|
54
|
-
font-family: var(--mono);
|
|
55
|
-
font-size: 14px;
|
|
56
|
-
font-weight: 600;
|
|
57
|
-
letter-spacing: -0.02em;
|
|
58
|
-
}
|
|
59
54
|
.h-spacer {
|
|
60
55
|
flex: 1;
|
|
61
56
|
}
|
|
62
57
|
|
|
63
|
-
.v-pill {
|
|
64
|
-
font-family: var(--mono);
|
|
65
|
-
font-size: 10px;
|
|
66
|
-
padding: 2px 8px;
|
|
67
|
-
border-radius: 10px;
|
|
68
|
-
border: 1px solid var(--border);
|
|
69
|
-
color: var(--muted);
|
|
70
|
-
background: none;
|
|
71
|
-
cursor: pointer;
|
|
72
|
-
user-select: none;
|
|
73
|
-
position: relative;
|
|
74
|
-
}
|
|
75
|
-
.v-pill:hover {
|
|
76
|
-
color: var(--accent);
|
|
77
|
-
border-color: var(--accent);
|
|
78
|
-
}
|
|
79
|
-
.v-pip {
|
|
80
|
-
position: absolute;
|
|
81
|
-
top: -3px;
|
|
82
|
-
right: -3px;
|
|
83
|
-
width: 7px;
|
|
84
|
-
height: 7px;
|
|
85
|
-
border-radius: 50%;
|
|
86
|
-
background: var(--accent);
|
|
87
|
-
border: 1.5px solid var(--bg);
|
|
88
|
-
display: none;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
58
|
/* ── Health dots ──────────────────────────────────────────────────────── */
|
|
92
|
-
.health {
|
|
93
|
-
display: flex;
|
|
94
|
-
align-items: center;
|
|
95
|
-
gap: 10px;
|
|
96
|
-
padding: 0 4px;
|
|
97
|
-
flex-wrap: wrap;
|
|
98
|
-
}
|
|
99
|
-
.health-item,
|
|
100
|
-
.health-group {
|
|
101
|
-
display: flex;
|
|
102
|
-
align-items: center;
|
|
103
|
-
gap: 5px;
|
|
104
|
-
font-size: 11px;
|
|
105
|
-
color: var(--muted);
|
|
106
|
-
}
|
|
107
59
|
.dot {
|
|
108
60
|
width: 7px;
|
|
109
61
|
height: 7px;
|
|
@@ -113,38 +65,17 @@
|
|
|
113
65
|
display: inline-block;
|
|
114
66
|
}
|
|
115
67
|
.dot.ok {
|
|
116
|
-
background: var(--
|
|
68
|
+
background: var(--ok);
|
|
117
69
|
}
|
|
118
70
|
.dot.err {
|
|
119
|
-
background: var(--
|
|
71
|
+
background: var(--bad);
|
|
120
72
|
}
|
|
121
73
|
.dot.running {
|
|
122
|
-
background: var(--
|
|
74
|
+
background: var(--warn);
|
|
123
75
|
animation: pulse 1s ease-in-out infinite;
|
|
124
76
|
}
|
|
125
77
|
|
|
126
78
|
/* ── Nav ──────────────────────────────────────────────────────────────── */
|
|
127
|
-
nav {
|
|
128
|
-
display: flex;
|
|
129
|
-
gap: 2px;
|
|
130
|
-
}
|
|
131
|
-
nav a {
|
|
132
|
-
color: var(--muted);
|
|
133
|
-
text-decoration: none;
|
|
134
|
-
padding: 3px 10px;
|
|
135
|
-
border-radius: 4px;
|
|
136
|
-
font-size: 12px;
|
|
137
|
-
font-weight: 500;
|
|
138
|
-
transition: color 0.1s;
|
|
139
|
-
}
|
|
140
|
-
nav a:hover {
|
|
141
|
-
color: var(--text);
|
|
142
|
-
background: var(--border);
|
|
143
|
-
}
|
|
144
|
-
nav a.active {
|
|
145
|
-
color: var(--text);
|
|
146
|
-
background: var(--surface);
|
|
147
|
-
}
|
|
148
79
|
|
|
149
80
|
/* ── Controls bar ─────────────────────────────────────────────────────── */
|
|
150
81
|
.controls {
|
|
@@ -179,25 +110,25 @@
|
|
|
179
110
|
}
|
|
180
111
|
.btn:hover:not(:disabled) {
|
|
181
112
|
border-color: var(--accent);
|
|
182
|
-
color: var(--accent);
|
|
113
|
+
color: var(--accent-text);
|
|
183
114
|
}
|
|
184
115
|
.btn:disabled {
|
|
185
116
|
opacity: 0.45;
|
|
186
117
|
cursor: not-allowed;
|
|
187
118
|
}
|
|
188
119
|
.btn.primary {
|
|
189
|
-
background: var(--accent);
|
|
190
|
-
color:
|
|
191
|
-
border-color: var(--accent);
|
|
120
|
+
background: var(--accent-fill);
|
|
121
|
+
color: var(--on-accent);
|
|
122
|
+
border-color: var(--accent-fill);
|
|
192
123
|
font-weight: 600;
|
|
193
124
|
}
|
|
194
125
|
.btn.primary:hover:not(:disabled) {
|
|
195
126
|
opacity: 0.88;
|
|
196
|
-
color:
|
|
127
|
+
color: var(--on-accent);
|
|
197
128
|
}
|
|
198
129
|
.btn.danger:hover:not(:disabled) {
|
|
199
|
-
border-color: var(--
|
|
200
|
-
color: var(--
|
|
130
|
+
border-color: var(--bad);
|
|
131
|
+
color: var(--bad);
|
|
201
132
|
}
|
|
202
133
|
.env-label {
|
|
203
134
|
font-size: 11px;
|
|
@@ -209,7 +140,7 @@
|
|
|
209
140
|
}
|
|
210
141
|
|
|
211
142
|
/* ── Main ─────────────────────────────────────────────────────────────── */
|
|
212
|
-
main {
|
|
143
|
+
.page-main {
|
|
213
144
|
flex: 1;
|
|
214
145
|
display: flex;
|
|
215
146
|
flex-direction: column;
|
|
@@ -251,7 +182,7 @@
|
|
|
251
182
|
color: var(--text);
|
|
252
183
|
}
|
|
253
184
|
th .arrow {
|
|
254
|
-
color: var(--accent);
|
|
185
|
+
color: var(--accent-text);
|
|
255
186
|
font-size: 9px;
|
|
256
187
|
margin-left: 3px;
|
|
257
188
|
}
|
|
@@ -260,51 +191,20 @@
|
|
|
260
191
|
border-bottom: 1px solid var(--border);
|
|
261
192
|
vertical-align: middle;
|
|
262
193
|
}
|
|
263
|
-
/* Row hover. The previous
|
|
264
|
-
|
|
265
|
-
|
|
194
|
+
/* Row hover. The previous white wash at 2.4% resolved to about 1.05:1
|
|
195
|
+
against the background, effectively invisible. An accent tint at 20%
|
|
196
|
+
lands near 1.39:1 (in the original dark palette), and the 3px accent
|
|
266
197
|
rule carries the rest of the signal. For scale, --surface itself is
|
|
267
198
|
only 1.09:1 against the background, so this is a deliberate step past
|
|
268
199
|
the page's usual contrast rather than a mistake.
|
|
269
200
|
Applied to the cells, not the row: the table is border-collapse:
|
|
270
201
|
collapse, where box-shadow on a <tr> is unreliable across browsers. */
|
|
271
202
|
tbody tr:hover td {
|
|
272
|
-
background:
|
|
203
|
+
background: color-mix(in srgb, var(--accent) 20%, transparent);
|
|
273
204
|
}
|
|
274
205
|
tbody tr:hover td:first-child {
|
|
275
206
|
box-shadow: inset 3px 0 0 var(--accent);
|
|
276
207
|
}
|
|
277
|
-
.session-chips {
|
|
278
|
-
display: flex;
|
|
279
|
-
flex-wrap: wrap;
|
|
280
|
-
gap: 6px;
|
|
281
|
-
padding: 6px 10px;
|
|
282
|
-
border-bottom: 1px solid var(--border);
|
|
283
|
-
background: var(--surface);
|
|
284
|
-
}
|
|
285
|
-
.session-chip {
|
|
286
|
-
font-size: 11px;
|
|
287
|
-
font-family: var(--mono);
|
|
288
|
-
padding: 2px 8px;
|
|
289
|
-
border-radius: 10px;
|
|
290
|
-
border: 1px solid var(--border);
|
|
291
|
-
background: transparent;
|
|
292
|
-
color: var(--muted);
|
|
293
|
-
cursor: pointer;
|
|
294
|
-
}
|
|
295
|
-
.session-chip:hover {
|
|
296
|
-
color: var(--text);
|
|
297
|
-
}
|
|
298
|
-
.session-chip.active {
|
|
299
|
-
border-color: var(--accent);
|
|
300
|
-
color: var(--accent);
|
|
301
|
-
}
|
|
302
|
-
.session-chip.running {
|
|
303
|
-
border-style: dashed;
|
|
304
|
-
}
|
|
305
|
-
.session-chip.fail {
|
|
306
|
-
border-color: var(--red);
|
|
307
|
-
}
|
|
308
208
|
.repo-cell {
|
|
309
209
|
font-weight: 600;
|
|
310
210
|
}
|
|
@@ -315,7 +215,7 @@
|
|
|
315
215
|
text-decoration: none;
|
|
316
216
|
}
|
|
317
217
|
.repo-link:hover {
|
|
318
|
-
color: var(--accent);
|
|
218
|
+
color: var(--accent-text);
|
|
319
219
|
text-decoration: underline;
|
|
320
220
|
}
|
|
321
221
|
.wave-tag {
|
|
@@ -345,8 +245,7 @@
|
|
|
345
245
|
.deployer {
|
|
346
246
|
font-family: var(--mono);
|
|
347
247
|
font-size: 11px;
|
|
348
|
-
color: var(--accent);
|
|
349
|
-
text-decoration: none;
|
|
248
|
+
color: var(--accent-text);
|
|
350
249
|
white-space: nowrap;
|
|
351
250
|
}
|
|
352
251
|
a.deployer:hover {
|
|
@@ -370,13 +269,13 @@
|
|
|
370
269
|
font-weight: 500;
|
|
371
270
|
}
|
|
372
271
|
.st-up {
|
|
373
|
-
color: var(--
|
|
272
|
+
color: var(--ok);
|
|
374
273
|
}
|
|
375
274
|
.st-warn {
|
|
376
|
-
color: var(--
|
|
275
|
+
color: var(--warn);
|
|
377
276
|
}
|
|
378
277
|
.st-bad {
|
|
379
|
-
color: var(--
|
|
278
|
+
color: var(--bad);
|
|
380
279
|
}
|
|
381
280
|
.st-none {
|
|
382
281
|
color: var(--muted);
|
|
@@ -401,7 +300,7 @@
|
|
|
401
300
|
#read-only-tag {
|
|
402
301
|
display: none;
|
|
403
302
|
font-size: 12px;
|
|
404
|
-
color: var(--muted
|
|
303
|
+
color: var(--muted);
|
|
405
304
|
border: 1px solid currentColor;
|
|
406
305
|
border-radius: 4px;
|
|
407
306
|
padding: 1px 6px;
|
|
@@ -423,8 +322,8 @@
|
|
|
423
322
|
color 0.1s;
|
|
424
323
|
}
|
|
425
324
|
.dep-btn:hover:not(:disabled) {
|
|
426
|
-
border-color: var(--
|
|
427
|
-
color: var(--
|
|
325
|
+
border-color: var(--ok);
|
|
326
|
+
color: var(--ok);
|
|
428
327
|
}
|
|
429
328
|
.dep-btn:disabled {
|
|
430
329
|
opacity: 0.4;
|
|
@@ -452,22 +351,22 @@
|
|
|
452
351
|
flex-shrink: 0;
|
|
453
352
|
}
|
|
454
353
|
.run-pill.busy .dot {
|
|
455
|
-
background: var(--
|
|
354
|
+
background: var(--warn);
|
|
456
355
|
animation: pulse 1s ease-in-out infinite;
|
|
457
356
|
}
|
|
458
357
|
.run-pill.ok {
|
|
459
|
-
color: var(--
|
|
460
|
-
border-color:
|
|
358
|
+
color: var(--ok);
|
|
359
|
+
border-color: color-mix(in srgb, var(--ok) 27%, transparent);
|
|
461
360
|
}
|
|
462
361
|
.run-pill.ok .dot {
|
|
463
|
-
background: var(--
|
|
362
|
+
background: var(--ok);
|
|
464
363
|
}
|
|
465
364
|
.run-pill.err {
|
|
466
|
-
color: var(--
|
|
467
|
-
border-color:
|
|
365
|
+
color: var(--bad);
|
|
366
|
+
border-color: color-mix(in srgb, var(--bad) 27%, transparent);
|
|
468
367
|
}
|
|
469
368
|
.run-pill.err .dot {
|
|
470
|
-
background: var(--
|
|
369
|
+
background: var(--bad);
|
|
471
370
|
}
|
|
472
371
|
@keyframes pulse {
|
|
473
372
|
0%,
|
|
@@ -526,9 +425,9 @@
|
|
|
526
425
|
border-radius: 3px;
|
|
527
426
|
background: linear-gradient(
|
|
528
427
|
90deg,
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
428
|
+
color-mix(in srgb, var(--text) 5%, transparent) 25%,
|
|
429
|
+
color-mix(in srgb, var(--text) 12%, transparent) 50%,
|
|
430
|
+
color-mix(in srgb, var(--text) 5%, transparent) 75%
|
|
532
431
|
);
|
|
533
432
|
background-size: 600px 100%;
|
|
534
433
|
animation: rel-shimmer 1.5s linear infinite;
|
|
@@ -569,7 +468,7 @@
|
|
|
569
468
|
animation: none;
|
|
570
469
|
}
|
|
571
470
|
.skel {
|
|
572
|
-
background:
|
|
471
|
+
background: color-mix(in srgb, var(--text) 8%, transparent);
|
|
573
472
|
}
|
|
574
473
|
}
|
|
575
474
|
|
|
@@ -604,43 +503,6 @@
|
|
|
604
503
|
color: var(--text);
|
|
605
504
|
background: var(--border);
|
|
606
505
|
}
|
|
607
|
-
.output-pane {
|
|
608
|
-
height: 34%;
|
|
609
|
-
display: flex;
|
|
610
|
-
flex-direction: column;
|
|
611
|
-
min-height: 0;
|
|
612
|
-
}
|
|
613
|
-
.output-pane.hidden {
|
|
614
|
-
display: none;
|
|
615
|
-
}
|
|
616
|
-
#output {
|
|
617
|
-
flex: 1;
|
|
618
|
-
overflow-y: auto;
|
|
619
|
-
padding: 10px 14px;
|
|
620
|
-
font-family: var(--mono);
|
|
621
|
-
font-size: 11.5px;
|
|
622
|
-
line-height: 1.6;
|
|
623
|
-
white-space: pre-wrap;
|
|
624
|
-
word-break: break-all;
|
|
625
|
-
min-height: 0;
|
|
626
|
-
}
|
|
627
|
-
#output .cmd {
|
|
628
|
-
color: var(--accent);
|
|
629
|
-
}
|
|
630
|
-
#output .phase {
|
|
631
|
-
color: var(--text);
|
|
632
|
-
font-weight: 600;
|
|
633
|
-
}
|
|
634
|
-
#output .warn {
|
|
635
|
-
color: var(--yellow);
|
|
636
|
-
}
|
|
637
|
-
#output .err {
|
|
638
|
-
color: var(--red);
|
|
639
|
-
}
|
|
640
|
-
#output .ok {
|
|
641
|
-
color: var(--green);
|
|
642
|
-
}
|
|
643
|
-
|
|
644
506
|
/* ── Modal ────────────────────────────────────────────────────────────── */
|
|
645
507
|
.overlay {
|
|
646
508
|
position: fixed;
|
|
@@ -663,7 +525,7 @@
|
|
|
663
525
|
display: flex;
|
|
664
526
|
flex-direction: column;
|
|
665
527
|
overflow: hidden;
|
|
666
|
-
box-shadow:
|
|
528
|
+
box-shadow: var(--shadow);
|
|
667
529
|
}
|
|
668
530
|
.modal-hdr {
|
|
669
531
|
display: flex;
|
|
@@ -727,14 +589,14 @@
|
|
|
727
589
|
margin-bottom: 6px;
|
|
728
590
|
}
|
|
729
591
|
.banner.hotfix {
|
|
730
|
-
border: 1px solid var(--
|
|
731
|
-
background:
|
|
732
|
-
color: var(--
|
|
592
|
+
border: 1px solid var(--warn);
|
|
593
|
+
background: color-mix(in srgb, var(--warn) 8%, transparent);
|
|
594
|
+
color: var(--warn);
|
|
733
595
|
}
|
|
734
596
|
.banner.prod {
|
|
735
|
-
border: 1px solid var(--
|
|
736
|
-
background:
|
|
737
|
-
color:
|
|
597
|
+
border: 1px solid var(--bad);
|
|
598
|
+
background: color-mix(in srgb, var(--bad) 8%, transparent);
|
|
599
|
+
color: var(--bad);
|
|
738
600
|
}
|
|
739
601
|
.plan-wave {
|
|
740
602
|
margin: 8px 0;
|
|
@@ -753,7 +615,7 @@
|
|
|
753
615
|
padding: 2px 0 2px 12px;
|
|
754
616
|
}
|
|
755
617
|
.plan-repo .off {
|
|
756
|
-
color: var(--
|
|
618
|
+
color: var(--warn);
|
|
757
619
|
}
|
|
758
620
|
|
|
759
621
|
/* wave editor */
|
|
@@ -798,80 +660,21 @@
|
|
|
798
660
|
line-height: 1.5;
|
|
799
661
|
}
|
|
800
662
|
.err-text {
|
|
801
|
-
color: var(--
|
|
663
|
+
color: var(--bad);
|
|
802
664
|
font-size: 11px;
|
|
803
665
|
}
|
|
804
666
|
</style>
|
|
805
667
|
</head>
|
|
806
668
|
<body>
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
width="16"
|
|
811
|
-
height="16"
|
|
812
|
-
alt=""
|
|
813
|
-
style="flex-shrink: 0"
|
|
814
|
-
/>
|
|
815
|
-
<h1>__DEN_CLI__</h1>
|
|
816
|
-
<button
|
|
817
|
-
class="v-pill"
|
|
818
|
-
id="version-pill"
|
|
819
|
-
onclick="openChangelog()"
|
|
820
|
-
title="View changelog"
|
|
821
|
-
>
|
|
822
|
-
<span class="v-pip" id="v-pip"></span><span class="v-txt">v…</span>
|
|
823
|
-
</button>
|
|
824
|
-
<div class="health" id="health" title="Local service health"></div>
|
|
825
|
-
<!-- header.js polls /api/health with whatever #env-sel holds. This page's
|
|
826
|
-
VISIBLE env picker is the deploy target, so it must not use that id —
|
|
827
|
-
the header would then ask "are my local services healthy in sandbox?"
|
|
828
|
-
and light up entirely red.
|
|
829
|
-
|
|
830
|
-
This pinned-to-local select is what header.js reads. Deployed-env
|
|
831
|
-
health is not shown here because it is not observable from a laptop:
|
|
832
|
-
those checks target *.internal.<env>.example.dev (private, VPN-only) and
|
|
833
|
-
`aws rds describe-db-instances` (needs credentials), so off-VPN every
|
|
834
|
-
one fails and the row is a wall of red that means nothing. The
|
|
835
|
-
deployed-env signal this page actually needs is the Status column,
|
|
836
|
-
which compares main vs deployed SHA over the GitHub API and needs no
|
|
837
|
-
network path to the services. -->
|
|
838
|
-
<select
|
|
839
|
-
id="env-sel"
|
|
840
|
-
style="display: none"
|
|
841
|
-
aria-hidden="true"
|
|
842
|
-
tabindex="-1"
|
|
843
|
-
>
|
|
844
|
-
<option value="local" selected>local</option>
|
|
845
|
-
</select>
|
|
846
|
-
<div class="h-spacer"></div>
|
|
847
|
-
<nav>
|
|
848
|
-
<a href="/">Local Dev</a>
|
|
849
|
-
<a href="/services">Services</a>
|
|
850
|
-
<a href="/environments">Environments</a>
|
|
851
|
-
<a href="/release" class="active">Releases</a>
|
|
852
|
-
<a href="/logs"
|
|
853
|
-
>Logs
|
|
854
|
-
<span
|
|
855
|
-
id="log-badge"
|
|
856
|
-
style="
|
|
857
|
-
background: var(--yellow);
|
|
858
|
-
color: #0d1117;
|
|
859
|
-
font-size: 10px;
|
|
860
|
-
font-weight: 700;
|
|
861
|
-
padding: 1px 5px;
|
|
862
|
-
border-radius: 8px;
|
|
863
|
-
line-height: 1.4;
|
|
864
|
-
display: none;
|
|
865
|
-
"
|
|
866
|
-
></span
|
|
867
|
-
></a>
|
|
868
|
-
<a href="/tests">Tests</a>
|
|
869
|
-
</nav>
|
|
870
|
-
</header>
|
|
669
|
+
<!-- The shell: top bar and rail, the same on every page (shell.js). -->
|
|
670
|
+
<div id="den-shell" data-page="release" data-health-env="local"></div>
|
|
671
|
+
<script src="/shell.js"></script>
|
|
871
672
|
|
|
673
|
+
<!-- The page's one main landmark: everything after the shell. -->
|
|
674
|
+
<main id="den-content">
|
|
872
675
|
<div class="controls">
|
|
873
|
-
<
|
|
874
|
-
<select id="rel-env" title="
|
|
676
|
+
<label class="env-label" for="rel-env">deploy to:</label>
|
|
677
|
+
<select id="rel-env" title="Deploy target"></select>
|
|
875
678
|
<button class="btn" id="refresh-btn" title="Re-check deployed state">
|
|
876
679
|
⟳ Refresh
|
|
877
680
|
</button>
|
|
@@ -888,7 +691,7 @@
|
|
|
888
691
|
>
|
|
889
692
|
</div>
|
|
890
693
|
|
|
891
|
-
<main>
|
|
694
|
+
<div class="page-main">
|
|
892
695
|
<div class="table-pane">
|
|
893
696
|
<table id="rel-table">
|
|
894
697
|
<thead>
|
|
@@ -908,25 +711,9 @@
|
|
|
908
711
|
</table>
|
|
909
712
|
</div>
|
|
910
713
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
<button onclick="window.__rel.clearOutput()">Clear</button>
|
|
915
|
-
<button
|
|
916
|
-
onclick="window.__rel.closeDeployLog()"
|
|
917
|
-
title="Close the deploy log"
|
|
918
|
-
>
|
|
919
|
-
✕ Close
|
|
920
|
-
</button>
|
|
921
|
-
</span>
|
|
922
|
-
</div>
|
|
923
|
-
<!-- Only shown when more than one deploy session exists; switches which
|
|
924
|
-
one the single log pane below is attached to. -->
|
|
925
|
-
<div id="session-chips" class="session-chips" style="display: none"></div>
|
|
926
|
-
<div class="output-pane hidden" id="output-pane">
|
|
927
|
-
<pre id="output"></pre>
|
|
928
|
-
</div>
|
|
929
|
-
</main>
|
|
714
|
+
<!-- Each deploy session is a tab in the shell's runs dock (R16):
|
|
715
|
+
selecting one attaches its log, its × closes the session. -->
|
|
716
|
+
</div>
|
|
930
717
|
|
|
931
718
|
<!-- Generic modal -->
|
|
932
719
|
<div class="overlay" id="overlay">
|
|
@@ -947,11 +734,13 @@
|
|
|
947
734
|
initLogBadge,
|
|
948
735
|
initHealth,
|
|
949
736
|
refreshHealth,
|
|
737
|
+
initWorkspacePill,
|
|
950
738
|
} from "/header.js";
|
|
951
739
|
|
|
952
|
-
// The header's dots report LOCAL service health
|
|
953
|
-
//
|
|
954
|
-
//
|
|
740
|
+
// The header's dots report LOCAL service health here: #den-shell pins
|
|
741
|
+
// data-health-env="local" (shell.js). The env picker is the shell's
|
|
742
|
+
// view; #rel-env below is the deploy target. Changing the deploy
|
|
743
|
+
// target must NOT re-point the dots — that was the bug where
|
|
955
744
|
// selecting sandbox made the header ask /api/health?env=sandbox and show
|
|
956
745
|
// every system red while docker was perfectly healthy.
|
|
957
746
|
const HEADER_HEALTH_ENV = "local";
|
|
@@ -1092,7 +881,11 @@
|
|
|
1092
881
|
});
|
|
1093
882
|
|
|
1094
883
|
renderHead();
|
|
1095
|
-
await Promise.all([
|
|
884
|
+
await Promise.all([
|
|
885
|
+
applyDenConfig(),
|
|
886
|
+
initChangelog(),
|
|
887
|
+
initWorkspacePill(),
|
|
888
|
+
]);
|
|
1096
889
|
initLogBadge();
|
|
1097
890
|
initHealth();
|
|
1098
891
|
loadStatus();
|
|
@@ -1251,7 +1044,7 @@
|
|
|
1251
1044
|
const mainCell = r.mainShort
|
|
1252
1045
|
? r.deployedShort && r.deployedShort !== r.mainShort
|
|
1253
1046
|
? `${srcTag}<span class="sha">${esc(r.mainShort)}</span>`
|
|
1254
|
-
: `${srcTag}<span class="sha" style="color:var(--
|
|
1047
|
+
: `${srcTag}<span class="sha" style="color:var(--ok)">${esc(r.mainShort)}</span>`
|
|
1255
1048
|
: '<span class="sha">—</span>';
|
|
1256
1049
|
// The GitHub Environment checked, when it isn't the env's own name
|
|
1257
1050
|
// (mapped CD environments, one row per tenant cell).
|
|
@@ -1595,14 +1388,12 @@
|
|
|
1595
1388
|
});
|
|
1596
1389
|
});
|
|
1597
1390
|
try {
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
return r.json();
|
|
1605
|
-
});
|
|
1391
|
+
// R26c: the server's message on a failure, not "save failed".
|
|
1392
|
+
const updated = await window.denShell.api(
|
|
1393
|
+
"PUT",
|
|
1394
|
+
"/api/release/config",
|
|
1395
|
+
{ repos },
|
|
1396
|
+
);
|
|
1606
1397
|
state.config = updated;
|
|
1607
1398
|
closeModal();
|
|
1608
1399
|
renderTable();
|
|
@@ -1616,46 +1407,42 @@
|
|
|
1616
1407
|
// deploy decoupled from this page), then we attach to stream its log. The
|
|
1617
1408
|
// session id is remembered so the log survives navigation / reload.
|
|
1618
1409
|
async function startDeploy(params) {
|
|
1619
|
-
showOutput(true);
|
|
1620
|
-
clearOutput();
|
|
1621
1410
|
const label =
|
|
1622
1411
|
params.mode === "all"
|
|
1623
1412
|
? `all → ${state.env}`
|
|
1624
1413
|
: `${params.repo} → ${state.env}`;
|
|
1625
|
-
|
|
1414
|
+
// Until the server names the session, the request has its own tab.
|
|
1415
|
+
curRun = runs().start(REQUEST_RUN, `deploy ${label}`, {
|
|
1416
|
+
clear: true,
|
|
1417
|
+
doneLine: false,
|
|
1418
|
+
});
|
|
1626
1419
|
appendLine(`▶ requesting deploy ${label}`, "cmd");
|
|
1627
1420
|
setStatus(`starting ${label}…`);
|
|
1628
1421
|
|
|
1629
1422
|
let session;
|
|
1630
1423
|
try {
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
confirm: params.confirm,
|
|
1640
|
-
}),
|
|
1424
|
+
// R26c: a refusal (a 409 names the repos already in flight) is the
|
|
1425
|
+
// server's message in the run's output; a lost server is said too.
|
|
1426
|
+
session = await window.denShell.api("POST", "/api/release/deploy", {
|
|
1427
|
+
env: state.env,
|
|
1428
|
+
mode: params.mode,
|
|
1429
|
+
repo: params.repo,
|
|
1430
|
+
ref: params.ref,
|
|
1431
|
+
confirm: params.confirm,
|
|
1641
1432
|
});
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
/* not JSON — show as-is */
|
|
1650
|
-
}
|
|
1651
|
-
appendLine(text, "err");
|
|
1433
|
+
} catch (e) {
|
|
1434
|
+
if (e.denApi && !e.network) {
|
|
1435
|
+
appendLine(
|
|
1436
|
+
e.message + (e.requestId ? ` (request ${e.requestId})` : ""),
|
|
1437
|
+
"err",
|
|
1438
|
+
);
|
|
1439
|
+
runs().finish(REQUEST_RUN, { ok: false });
|
|
1652
1440
|
setStatus("deploy not started");
|
|
1653
1441
|
await refreshSessions();
|
|
1654
1442
|
return;
|
|
1655
1443
|
}
|
|
1656
|
-
session = await res.json();
|
|
1657
|
-
} catch (e) {
|
|
1658
1444
|
appendLine("Failed to start deploy: " + e.message, "err");
|
|
1445
|
+
runs().finish(REQUEST_RUN, { ok: false });
|
|
1659
1446
|
setStatus("deploy not started");
|
|
1660
1447
|
await refreshSessions();
|
|
1661
1448
|
return;
|
|
@@ -1665,6 +1452,7 @@
|
|
|
1665
1452
|
LS_DEPLOY,
|
|
1666
1453
|
JSON.stringify({ sessionId: session.id }),
|
|
1667
1454
|
);
|
|
1455
|
+
runs().remove(REQUEST_RUN);
|
|
1668
1456
|
// Show the new session immediately, then reconcile against the server.
|
|
1669
1457
|
state.sessions = [
|
|
1670
1458
|
session,
|
|
@@ -1693,10 +1481,12 @@
|
|
|
1693
1481
|
state.deploySession = sessionId;
|
|
1694
1482
|
state.deploying = running;
|
|
1695
1483
|
clearRunsForSession(sessionId);
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1484
|
+
// Its tab, cleared: the replay rebuilds the full log from scratch.
|
|
1485
|
+
curRun = runs().start(
|
|
1486
|
+
runKey(sessionId),
|
|
1487
|
+
`deploy ${label || sessionId}`,
|
|
1488
|
+
{ ...sessionTabOpts(sessionId), clear: true, running },
|
|
1489
|
+
);
|
|
1700
1490
|
setStatus(running ? "attaching to deploy…" : "deploy finished");
|
|
1701
1491
|
// Button state comes from the full session list, not from whichever log
|
|
1702
1492
|
// happens to be on screen — switching logs must not free a busy repo.
|
|
@@ -1736,6 +1526,7 @@
|
|
|
1736
1526
|
`\n${msg.ok ? "✓" : "✗"} ${msg.summary}`,
|
|
1737
1527
|
msg.ok ? "ok" : "err",
|
|
1738
1528
|
);
|
|
1529
|
+
runs().finish(runKey(sessionId), { ok: !!msg.ok });
|
|
1739
1530
|
finishDeploy();
|
|
1740
1531
|
}
|
|
1741
1532
|
};
|
|
@@ -1764,10 +1555,6 @@
|
|
|
1764
1555
|
// server rather than assuming everything is now free.
|
|
1765
1556
|
refreshSessions();
|
|
1766
1557
|
setStatus("deploy finished — re-checking…");
|
|
1767
|
-
const t = $("output-title");
|
|
1768
|
-
if (t && t.textContent.startsWith("Deploying "))
|
|
1769
|
-
t.textContent =
|
|
1770
|
-
"Deploy log — " + t.textContent.slice("Deploying ".length);
|
|
1771
1558
|
renderTable();
|
|
1772
1559
|
setTimeout(() => loadStatus(true), 2500);
|
|
1773
1560
|
}
|
|
@@ -1808,11 +1595,21 @@
|
|
|
1808
1595
|
});
|
|
1809
1596
|
}
|
|
1810
1597
|
|
|
1811
|
-
// User closed the log: dismiss ONLY
|
|
1812
|
-
// keep running, so if one
|
|
1813
|
-
// hiding the log entirely.
|
|
1814
|
-
async function closeDeployLog() {
|
|
1598
|
+
// User closed the log (its tab's ×): dismiss ONLY that session. Other
|
|
1599
|
+
// deploys keep running, so if the one on screen closes and one is left,
|
|
1600
|
+
// switch to it instead of hiding the log entirely.
|
|
1601
|
+
async function closeDeployLog(id = state.deploySession) {
|
|
1602
|
+
if (id && id !== state.deploySession) {
|
|
1603
|
+
fetch(`/api/release/deploy?session=${encodeURIComponent(id)}`, {
|
|
1604
|
+
method: "DELETE",
|
|
1605
|
+
}).catch(() => {});
|
|
1606
|
+
state.sessions = state.sessions.filter((s) => s.id !== id);
|
|
1607
|
+
runs().remove(runKey(id));
|
|
1608
|
+
recomputeDeployingRepos();
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1815
1611
|
const closing = state.deploySession;
|
|
1612
|
+
if (closing) runs().remove(runKey(closing));
|
|
1816
1613
|
if (closing) {
|
|
1817
1614
|
fetch(`/api/release/deploy?session=${encodeURIComponent(closing)}`, {
|
|
1818
1615
|
method: "DELETE",
|
|
@@ -1841,8 +1638,6 @@
|
|
|
1841
1638
|
|
|
1842
1639
|
localStorage.removeItem(LS_DEPLOY);
|
|
1843
1640
|
recomputeDeployingRepos();
|
|
1844
|
-
showOutput(false);
|
|
1845
|
-
clearOutput();
|
|
1846
1641
|
setStatus("idle");
|
|
1847
1642
|
renderTable();
|
|
1848
1643
|
}
|
|
@@ -1925,7 +1720,7 @@
|
|
|
1925
1720
|
}
|
|
1926
1721
|
state.deployingRepos = live;
|
|
1927
1722
|
syncButtons();
|
|
1928
|
-
|
|
1723
|
+
syncSessionTabs();
|
|
1929
1724
|
}
|
|
1930
1725
|
|
|
1931
1726
|
// ── Live CD run poll ──────────────────────────────────────────────────────
|
|
@@ -2052,60 +1847,51 @@
|
|
|
2052
1847
|
recomputeDeployingRepos();
|
|
2053
1848
|
}
|
|
2054
1849
|
|
|
2055
|
-
//
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
"session-chip",
|
|
2069
|
-
s.id === state.deploySession ? "active" : "",
|
|
2070
|
-
s.status === "running" ? "running" : s.ok ? "ok" : "fail",
|
|
2071
|
-
]
|
|
2072
|
-
.filter(Boolean)
|
|
2073
|
-
.join(" ");
|
|
2074
|
-
return `<button class="${cls}" data-session="${esc(s.id)}" title="${esc(s.label)}">${
|
|
2075
|
-
s.status === "running" ? "● " : ""
|
|
2076
|
-
}${esc(s.label)}</button>`;
|
|
2077
|
-
})
|
|
2078
|
-
.join("");
|
|
2079
|
-
bar.querySelectorAll(".session-chip").forEach((c) =>
|
|
2080
|
-
c.addEventListener("click", () => {
|
|
2081
|
-
const s = state.sessions.find((x) => x.id === c.dataset.session);
|
|
2082
|
-
if (s && s.id !== state.deploySession) {
|
|
1850
|
+
// ── The runs dock (R16): one tab per deploy session ──────────────────────
|
|
1851
|
+
const runs = () => window.denShell.runs;
|
|
1852
|
+
const runKey = (id) => `deploy:${id}`;
|
|
1853
|
+
const REQUEST_RUN = "deploy:request";
|
|
1854
|
+
let curRun = null; // the tab appendLine writes to
|
|
1855
|
+
function sessionTabOpts(id) {
|
|
1856
|
+
return {
|
|
1857
|
+
doneLine: false,
|
|
1858
|
+
onClose: () => closeDeployLog(id),
|
|
1859
|
+
// Selecting a session's tab attaches its log (the server replays it).
|
|
1860
|
+
onSelect: () => {
|
|
1861
|
+
const s = state.sessions.find((x) => x.id === id);
|
|
1862
|
+
if (s && s.id !== state.deploySession)
|
|
2083
1863
|
attachDeploy(s.id, { status: s.status, label: s.label });
|
|
1864
|
+
},
|
|
1865
|
+
};
|
|
2084
1866
|
}
|
|
2085
|
-
|
|
2086
|
-
|
|
1867
|
+
// N sessions, N tabs: each names its session and pulses while it runs.
|
|
1868
|
+
function syncSessionTabs() {
|
|
1869
|
+
const ids = new Set(state.sessions.map((s) => runKey(s.id)));
|
|
1870
|
+
for (const s of state.sessions)
|
|
1871
|
+
runs().add(runKey(s.id), `deploy ${s.label || s.id}`, {
|
|
1872
|
+
...sessionTabOpts(s.id),
|
|
1873
|
+
running: s.status === "running",
|
|
1874
|
+
...(s.status === "running" ? {} : { error: s.ok === false }),
|
|
1875
|
+
});
|
|
1876
|
+
for (const r of runs().list())
|
|
1877
|
+
if (
|
|
1878
|
+
r.id.startsWith("deploy:") &&
|
|
1879
|
+
r.id !== REQUEST_RUN &&
|
|
1880
|
+
!ids.has(r.id) &&
|
|
1881
|
+
r.id !== runKey(state.deploySession)
|
|
1882
|
+
)
|
|
1883
|
+
runs().remove(r.id);
|
|
2087
1884
|
}
|
|
2088
1885
|
|
|
2089
1886
|
// ── Output helpers ──────────────────────────────────────────────────────────
|
|
2090
|
-
function showOutput(show) {
|
|
2091
|
-
$("output-bar").style.display = show ? "" : "none";
|
|
2092
|
-
$("output-pane").classList.toggle("hidden", !show);
|
|
2093
|
-
}
|
|
2094
1887
|
function appendLine(text, cls) {
|
|
2095
|
-
|
|
2096
|
-
const span = document.createElement("span");
|
|
2097
|
-
if (cls) span.className = cls;
|
|
2098
|
-
span.textContent = text + "\n";
|
|
2099
|
-
out.appendChild(span);
|
|
2100
|
-
out.scrollTop = out.scrollHeight;
|
|
2101
|
-
}
|
|
2102
|
-
function clearOutput() {
|
|
2103
|
-
$("output").innerHTML = "";
|
|
1888
|
+
if (curRun) runs().append(curRun, text, cls);
|
|
2104
1889
|
}
|
|
2105
1890
|
|
|
2106
|
-
window.__rel = {
|
|
1891
|
+
window.__rel = { closeDeployLog };
|
|
2107
1892
|
|
|
2108
1893
|
init();
|
|
2109
1894
|
</script>
|
|
1895
|
+
</main>
|
|
2110
1896
|
</body>
|
|
2111
1897
|
</html>
|