openpalm 0.9.8 → 0.9.10
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/package.json +4 -2
- package/playwright.config.ts +16 -0
- package/src/commands/install-file.test.ts +306 -0
- package/src/commands/install-services.test.ts +12 -7
- package/src/commands/install-services.ts +1 -1
- package/src/commands/install.ts +142 -34
- package/src/commands/restart.ts +2 -35
- package/src/commands/service.ts +0 -17
- package/src/commands/start.ts +5 -43
- package/src/commands/status.ts +0 -9
- package/src/commands/stop.ts +4 -36
- package/src/commands/uninstall.ts +23 -14
- package/src/commands/update.ts +0 -9
- package/src/lib/docker.ts +25 -7
- package/src/lib/env.ts +13 -60
- package/src/lib/paths.ts +11 -1
- package/src/lib/staging.ts +3 -3
- package/src/main.test.ts +118 -80
- package/src/setup-wizard/index.html +114 -180
- package/src/setup-wizard/server-errors.test.ts +429 -0
- package/src/setup-wizard/server-integration.test.ts +511 -0
- package/src/setup-wizard/server.test.ts +6 -6
- package/src/setup-wizard/server.ts +17 -5
- package/src/setup-wizard/standalone.ts +166 -0
- package/src/setup-wizard/wizard.css +892 -299
- package/src/setup-wizard/wizard.js +1172 -559
- package/src/lib/admin.ts +0 -107
|
@@ -12,227 +12,149 @@
|
|
|
12
12
|
|
|
13
13
|
<!-- ── Header ─────────────────────────────────────────────── -->
|
|
14
14
|
<div class="wizard-header">
|
|
15
|
-
<
|
|
16
|
-
<
|
|
15
|
+
<div class="hdr-logo">OP</div>
|
|
16
|
+
<h1>OpenPalm <span class="hdr-suffix">Setup</span></h1>
|
|
17
17
|
</div>
|
|
18
18
|
|
|
19
19
|
<div class="wizard-body">
|
|
20
20
|
|
|
21
|
-
<!-- ──
|
|
22
|
-
<nav class="
|
|
23
|
-
<
|
|
24
|
-
<
|
|
25
|
-
<button class="step-dot" data-step="1" aria-label="Step 2: Connections" disabled>2</button>
|
|
26
|
-
<span class="step-line" data-line="1"></span>
|
|
27
|
-
<button class="step-dot" data-step="2" aria-label="Step 3: Models" disabled>3</button>
|
|
28
|
-
<span class="step-line" data-line="2"></span>
|
|
29
|
-
<button class="step-dot" data-step="3" aria-label="Step 4: Options" disabled>4</button>
|
|
30
|
-
<span class="step-line" data-line="3"></span>
|
|
31
|
-
<button class="step-dot" data-step="4" aria-label="Step 5: Review" disabled>5</button>
|
|
21
|
+
<!-- ── Progress Bar ────────────────────────────────────── -->
|
|
22
|
+
<nav class="prog-bar" aria-label="Wizard steps" id="step-indicators">
|
|
23
|
+
<div class="prog-segments" id="prog-segments"></div>
|
|
24
|
+
<div class="prog-labels" id="prog-labels"></div>
|
|
32
25
|
</nav>
|
|
33
26
|
|
|
34
27
|
<!-- ═══════════════════════════════════════════════════════════
|
|
35
|
-
Step 0: Welcome &
|
|
28
|
+
Step 0: Welcome & Identity
|
|
36
29
|
═══════════════════════════════════════════════════════════ -->
|
|
37
30
|
<section class="step-content" id="step-0" data-testid="step-welcome">
|
|
38
|
-
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
<
|
|
44
|
-
|
|
31
|
+
<!-- Welcome Hero -->
|
|
32
|
+
<div class="welcome-hero" id="welcome-hero">
|
|
33
|
+
<div class="welcome-icon">👋</div>
|
|
34
|
+
<h2>Welcome to OpenPalm</h2>
|
|
35
|
+
<p class="welcome-subtitle">Your self-hosted AI assistant. Pick your providers, choose models, and you're up and running.</p>
|
|
36
|
+
<div class="welcome-pills">
|
|
37
|
+
<span class="pill">Cloud or local</span>
|
|
38
|
+
<span class="pill">Smart defaults</span>
|
|
39
|
+
<span class="pill">Privacy first</span>
|
|
40
|
+
</div>
|
|
41
|
+
<button class="btn btn-primary-lg" id="btn-get-started">Get Started</button>
|
|
45
42
|
</div>
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<
|
|
50
|
-
|
|
44
|
+
<!-- Identity Form (hidden until Get Started) -->
|
|
45
|
+
<div class="identity-form hidden" id="identity-form">
|
|
46
|
+
<h2>About You</h2>
|
|
47
|
+
<p class="step-description">Set up admin credentials and optional identity details.</p>
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
<div class="field-group">
|
|
50
|
+
<label for="admin-token">Admin Token</label>
|
|
51
|
+
<input id="admin-token" type="text" autocomplete="off" placeholder="Min 8 characters">
|
|
52
|
+
<p class="field-hint">Protects the admin console. A random token has been generated for you.</p>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div class="field-group">
|
|
56
|
+
<label for="owner-name">Your Name <span style="color:var(--color-text-tertiary);font-weight:normal">(optional)</span></label>
|
|
57
|
+
<input id="owner-name" type="text" placeholder="Jane Doe" autocomplete="name">
|
|
58
|
+
</div>
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
<div class="field-group">
|
|
61
|
+
<label for="owner-email">Email <span style="color:var(--color-text-tertiary);font-weight:normal">(optional)</span></label>
|
|
62
|
+
<input id="owner-email" type="email" placeholder="jane@example.com" autocomplete="email">
|
|
63
|
+
</div>
|
|
58
64
|
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
<div id="step0-error" class="field-error hidden" role="alert"></div>
|
|
66
|
+
|
|
67
|
+
<div class="step-actions">
|
|
68
|
+
<button class="btn btn-primary" id="btn-step0-next">Set Up Providers</button>
|
|
69
|
+
</div>
|
|
61
70
|
</div>
|
|
62
71
|
</section>
|
|
63
72
|
|
|
64
73
|
<!-- ═══════════════════════════════════════════════════════════
|
|
65
|
-
Step 1:
|
|
74
|
+
Step 1: Providers (Card Grid)
|
|
66
75
|
═══════════════════════════════════════════════════════════ -->
|
|
67
76
|
<section class="step-content hidden" id="step-1" data-testid="step-connections">
|
|
68
|
-
<h2>
|
|
69
|
-
<p class="step-description">
|
|
70
|
-
|
|
71
|
-
<!-- Existing connections list -->
|
|
72
|
-
<ul class="hub-list hidden" id="conn-hub-list" aria-label="Connections"></ul>
|
|
73
|
-
|
|
74
|
-
<!-- Empty state -->
|
|
75
|
-
<div class="hub-empty" id="conn-hub-empty">
|
|
76
|
-
<p class="hub-empty-headline">No connections yet</p>
|
|
77
|
-
<p class="hub-empty-body">Add a cloud API provider or a local model server to get started.</p>
|
|
78
|
-
</div>
|
|
77
|
+
<h2>Where should your models run?</h2>
|
|
78
|
+
<p class="step-description">Select one or more providers. Click a card to configure it.</p>
|
|
79
79
|
|
|
80
80
|
<!-- Loading state for provider detection -->
|
|
81
81
|
<div class="loading-state hidden" id="conn-detecting">
|
|
82
82
|
<span class="spinner"></span> Detecting local providers...
|
|
83
83
|
</div>
|
|
84
84
|
|
|
85
|
-
<!--
|
|
86
|
-
<div
|
|
87
|
-
<button class="conn-card" type="button" id="btn-add-cloud">
|
|
88
|
-
<div class="conn-icon conn-icon--cloud" aria-hidden="true">
|
|
89
|
-
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"/></svg>
|
|
90
|
-
</div>
|
|
91
|
-
<div class="conn-body">
|
|
92
|
-
<span class="conn-label">Cloud Provider <span class="conn-badge">Hosted</span></span>
|
|
93
|
-
<span class="conn-desc">OpenAI, Anthropic, Groq, Together, Mistral, DeepSeek, xAI</span>
|
|
94
|
-
</div>
|
|
95
|
-
<div class="conn-arrow" aria-hidden="true">
|
|
96
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>
|
|
97
|
-
</div>
|
|
98
|
-
</button>
|
|
99
|
-
<button class="conn-card" type="button" id="btn-add-local">
|
|
100
|
-
<div class="conn-icon conn-icon--local" aria-hidden="true">
|
|
101
|
-
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/><circle cx="6" cy="6" r="1" fill="currentColor" stroke="none"/><circle cx="6" cy="18" r="1" fill="currentColor" stroke="none"/></svg>
|
|
102
|
-
</div>
|
|
103
|
-
<div class="conn-body">
|
|
104
|
-
<span class="conn-label">Local Provider <span class="conn-badge conn-badge--local">On-Device</span></span>
|
|
105
|
-
<span class="conn-desc">Ollama, Docker Model Runner, LM Studio running locally</span>
|
|
106
|
-
</div>
|
|
107
|
-
<div class="conn-arrow" aria-hidden="true">
|
|
108
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>
|
|
109
|
-
</div>
|
|
110
|
-
</button>
|
|
111
|
-
</div>
|
|
112
|
-
|
|
113
|
-
<!-- Connection detail form (shared for cloud and local) -->
|
|
114
|
-
<div id="conn-detail-form" class="hidden">
|
|
115
|
-
<div class="connection-mode-card" id="conn-mode-card">
|
|
116
|
-
<div class="connection-mode-header">
|
|
117
|
-
<span class="connection-mode-badge" id="conn-mode-badge"></span>
|
|
118
|
-
<h3 id="conn-mode-title"></h3>
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
|
|
122
|
-
<!-- Cloud: provider quick-picks -->
|
|
123
|
-
<div class="provider-quick-picks hidden" id="cloud-provider-picks"></div>
|
|
124
|
-
|
|
125
|
-
<!-- Local: detected providers -->
|
|
126
|
-
<div id="local-provider-list" class="hidden"></div>
|
|
127
|
-
|
|
128
|
-
<div class="field-group">
|
|
129
|
-
<label for="conn-name">Connection Name</label>
|
|
130
|
-
<input id="conn-name" type="text" placeholder='e.g. "My Ollama", "Work OpenAI"'>
|
|
131
|
-
</div>
|
|
132
|
-
|
|
133
|
-
<div class="field-group">
|
|
134
|
-
<label for="conn-base-url">Base URL</label>
|
|
135
|
-
<input id="conn-base-url" type="url" placeholder="https://api.openai.com">
|
|
136
|
-
<p class="field-hint">The provider endpoint. Do not include a trailing /v1.</p>
|
|
137
|
-
</div>
|
|
138
|
-
|
|
139
|
-
<div class="field-group hidden" id="conn-apikey-group">
|
|
140
|
-
<label for="conn-api-key">API Key</label>
|
|
141
|
-
<input id="conn-api-key" type="password" placeholder="sk-..." autocomplete="new-password">
|
|
142
|
-
<p class="field-hint">Stored server-side only.</p>
|
|
143
|
-
</div>
|
|
144
|
-
|
|
145
|
-
<div id="conn-detail-error" class="field-error hidden" role="alert"></div>
|
|
146
|
-
|
|
147
|
-
<div class="connection-success hidden" id="conn-test-success" role="status">
|
|
148
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--color-success)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
|
|
149
|
-
<span id="conn-test-msg">Connected</span>
|
|
150
|
-
</div>
|
|
151
|
-
|
|
152
|
-
<div class="step-actions">
|
|
153
|
-
<button class="btn btn-secondary" id="btn-conn-cancel">Cancel</button>
|
|
154
|
-
<button class="btn btn-outline" id="btn-conn-test">Test</button>
|
|
155
|
-
<button class="btn btn-primary" id="btn-conn-save">Save Connection</button>
|
|
156
|
-
</div>
|
|
157
|
-
</div>
|
|
85
|
+
<!-- Provider card grid -->
|
|
86
|
+
<div class="provider-grid" id="provider-grid"></div>
|
|
158
87
|
|
|
159
|
-
<!--
|
|
88
|
+
<!-- Provider count info + nav -->
|
|
160
89
|
<div class="step-actions" id="step1-actions">
|
|
161
90
|
<button class="btn btn-secondary" id="btn-step1-back">Back</button>
|
|
162
|
-
<
|
|
163
|
-
<button class="btn btn-primary" id="btn-step1-next" disabled>
|
|
91
|
+
<span class="nav-info" id="provider-count-info"></span>
|
|
92
|
+
<button class="btn btn-primary" id="btn-step1-next" disabled>Choose Models</button>
|
|
164
93
|
</div>
|
|
165
94
|
</section>
|
|
166
95
|
|
|
167
96
|
<!-- ═══════════════════════════════════════════════════════════
|
|
168
|
-
Step 2: Model Assignment
|
|
97
|
+
Step 2: Model Assignment (Radio Options)
|
|
169
98
|
═══════════════════════════════════════════════════════════ -->
|
|
170
99
|
<section class="step-content hidden" id="step-2" data-testid="step-models">
|
|
171
|
-
<h2>
|
|
172
|
-
<p class="step-description">
|
|
173
|
-
|
|
174
|
-
<!-- LLM Card -->
|
|
175
|
-
<div class="model-card">
|
|
176
|
-
<div class="model-card-header">
|
|
177
|
-
<span class="model-card-title">Chat Model (LLM)</span>
|
|
178
|
-
<span class="model-card-help">Used for conversations, tool calls, and reasoning.</span>
|
|
179
|
-
</div>
|
|
180
|
-
<div class="field-group">
|
|
181
|
-
<label for="llm-connection">Connection</label>
|
|
182
|
-
<select id="llm-connection">
|
|
183
|
-
<option value="" disabled selected>Select a connection</option>
|
|
184
|
-
</select>
|
|
185
|
-
</div>
|
|
186
|
-
<div class="field-group">
|
|
187
|
-
<label for="llm-model">Model</label>
|
|
188
|
-
<select id="llm-model"><option value="">Loading...</option></select>
|
|
189
|
-
</div>
|
|
190
|
-
<div class="field-group">
|
|
191
|
-
<label for="llm-small-model">Small Model <span style="color:var(--color-text-tertiary);font-weight:normal">(optional)</span></label>
|
|
192
|
-
<select id="llm-small-model"><option value="">(same as chat model)</option></select>
|
|
193
|
-
<p class="field-hint">Used for lightweight tasks like memory extraction.</p>
|
|
194
|
-
</div>
|
|
195
|
-
</div>
|
|
100
|
+
<h2>Choose Your Models</h2>
|
|
101
|
+
<p class="step-description">Pre-selected from your providers. Adjust if needed.</p>
|
|
196
102
|
|
|
197
|
-
<!--
|
|
198
|
-
<div
|
|
199
|
-
<div class="model-card-header">
|
|
200
|
-
<span class="model-card-title">Embeddings</span>
|
|
201
|
-
<span class="model-card-help">Used for vector search and memory.</span>
|
|
202
|
-
</div>
|
|
203
|
-
<div class="field-group">
|
|
204
|
-
<label for="emb-connection">Connection</label>
|
|
205
|
-
<select id="emb-connection">
|
|
206
|
-
<option value="" disabled selected>Select a connection</option>
|
|
207
|
-
</select>
|
|
208
|
-
</div>
|
|
209
|
-
<div class="field-group">
|
|
210
|
-
<label for="emb-model">Embedding Model</label>
|
|
211
|
-
<select id="emb-model"><option value="">Loading...</option></select>
|
|
212
|
-
</div>
|
|
213
|
-
<div class="field-group">
|
|
214
|
-
<label for="emb-dims">Embedding Dimensions</label>
|
|
215
|
-
<input id="emb-dims" type="number" value="1536" min="1" step="1">
|
|
216
|
-
<p class="field-hint">Set automatically for known models. Override only if needed.</p>
|
|
217
|
-
</div>
|
|
218
|
-
</div>
|
|
103
|
+
<!-- Model groups rendered dynamically -->
|
|
104
|
+
<div id="model-groups"></div>
|
|
219
105
|
|
|
220
|
-
|
|
106
|
+
<!-- Hidden fields for backward compatibility with API payload -->
|
|
107
|
+
<input type="hidden" id="llm-connection" value="">
|
|
108
|
+
<input type="hidden" id="llm-model" value="">
|
|
109
|
+
<input type="hidden" id="llm-small-model" value="">
|
|
110
|
+
<input type="hidden" id="emb-connection" value="">
|
|
111
|
+
<input type="hidden" id="emb-model" value="">
|
|
112
|
+
<input type="hidden" id="emb-dims" value="1536">
|
|
221
113
|
|
|
222
114
|
<div id="step2-error" class="field-error hidden" role="alert"></div>
|
|
223
115
|
|
|
224
116
|
<div class="step-actions">
|
|
225
117
|
<button class="btn btn-secondary" id="btn-step2-back">Back</button>
|
|
226
|
-
<button class="btn btn-primary" id="btn-step2-next">
|
|
118
|
+
<button class="btn btn-primary" id="btn-step2-next">Voice Setup</button>
|
|
227
119
|
</div>
|
|
228
120
|
</section>
|
|
229
121
|
|
|
230
122
|
<!-- ═══════════════════════════════════════════════════════════
|
|
231
|
-
Step 3:
|
|
123
|
+
Step 3: Voice (TTS / STT)
|
|
232
124
|
═══════════════════════════════════════════════════════════ -->
|
|
233
|
-
<section class="step-content hidden" id="step-3" data-testid="step-
|
|
125
|
+
<section class="step-content hidden" id="step-3" data-testid="step-voice">
|
|
126
|
+
<h2>Voice Capabilities</h2>
|
|
127
|
+
<p class="step-description">Choose how your assistant speaks and listens.</p>
|
|
128
|
+
|
|
129
|
+
<!-- Voice groups rendered dynamically -->
|
|
130
|
+
<div id="voice-groups"></div>
|
|
131
|
+
|
|
132
|
+
<div class="step-actions">
|
|
133
|
+
<button class="btn btn-secondary" id="btn-step3-back">Back</button>
|
|
134
|
+
<button class="btn btn-primary" id="btn-step3-next">Options</button>
|
|
135
|
+
</div>
|
|
136
|
+
</section>
|
|
137
|
+
|
|
138
|
+
<!-- ═══════════════════════════════════════════════════════════
|
|
139
|
+
Step 4: Options (Channels + Services + Memory)
|
|
140
|
+
═══════════════════════════════════════════════════════════ -->
|
|
141
|
+
<section class="step-content hidden" id="step-4" data-testid="step-options">
|
|
234
142
|
<h2>Options</h2>
|
|
235
|
-
<p class="step-description">
|
|
143
|
+
<p class="step-description">Choose channels, services, and tweak settings before review.</p>
|
|
144
|
+
|
|
145
|
+
<!-- Channels -->
|
|
146
|
+
<div class="options-section">
|
|
147
|
+
<h3 class="options-section-title">Channels</h3>
|
|
148
|
+
<p class="options-section-desc">How you talk to your assistant. Web Chat is always on.</p>
|
|
149
|
+
<div class="toggle-grid" id="channels-grid"></div>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<!-- Services -->
|
|
153
|
+
<div class="options-section">
|
|
154
|
+
<h3 class="options-section-title">Services</h3>
|
|
155
|
+
<p class="options-section-desc">Extra capabilities for your stack.</p>
|
|
156
|
+
<div class="toggle-grid" id="services-grid"></div>
|
|
157
|
+
</div>
|
|
236
158
|
|
|
237
159
|
<!-- Ollama in-stack toggle (only shown when relevant) -->
|
|
238
160
|
<div class="addon-row hidden" id="ollama-addon">
|
|
@@ -251,25 +173,37 @@
|
|
|
251
173
|
<p class="field-hint">Identifies the memory owner. Defaults to your email or "default_user".</p>
|
|
252
174
|
</div>
|
|
253
175
|
|
|
176
|
+
<div id="step4-error" class="field-error hidden" role="alert"></div>
|
|
177
|
+
|
|
254
178
|
<div class="step-actions">
|
|
255
|
-
<button class="btn btn-secondary" id="btn-
|
|
256
|
-
<button class="btn btn-primary" id="btn-
|
|
179
|
+
<button class="btn btn-secondary" id="btn-step4-back">Back</button>
|
|
180
|
+
<button class="btn btn-primary" id="btn-step4-next">Review</button>
|
|
257
181
|
</div>
|
|
258
182
|
</section>
|
|
259
183
|
|
|
260
184
|
<!-- ═══════════════════════════════════════════════════════════
|
|
261
|
-
Step
|
|
185
|
+
Step 5: Review & Install
|
|
262
186
|
═══════════════════════════════════════════════════════════ -->
|
|
263
|
-
<section class="step-content hidden" id="step-
|
|
187
|
+
<section class="step-content hidden" id="step-5" data-testid="step-review">
|
|
264
188
|
<h2>Review & Install</h2>
|
|
265
189
|
<p class="step-description">Confirm your settings, then install.</p>
|
|
266
190
|
|
|
267
|
-
<div
|
|
191
|
+
<div id="review-summary"></div>
|
|
192
|
+
|
|
193
|
+
<div class="review-json-toggle" id="review-json-toggle">
|
|
194
|
+
<button class="btn-json-toggle" id="btn-toggle-json" type="button">Show Setup JSON</button>
|
|
195
|
+
</div>
|
|
196
|
+
<div class="review-json hidden" id="review-json">
|
|
197
|
+
<pre id="review-json-pre"></pre>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<!-- Keep old review-grid for backward compat (hidden, populated for tests) -->
|
|
201
|
+
<div class="hidden" id="review-grid"></div>
|
|
268
202
|
|
|
269
203
|
<div id="install-error" class="install-error hidden" role="alert"></div>
|
|
270
204
|
|
|
271
205
|
<div class="step-actions" id="review-actions">
|
|
272
|
-
<button class="btn btn-secondary" id="btn-
|
|
206
|
+
<button class="btn btn-secondary" id="btn-step5-back">Back</button>
|
|
273
207
|
<button class="btn btn-primary" id="btn-install">Install</button>
|
|
274
208
|
</div>
|
|
275
209
|
</section>
|
|
@@ -330,7 +264,7 @@
|
|
|
330
264
|
<h2>Setup Complete</h2>
|
|
331
265
|
<p class="done-subtitle">Your OpenPalm stack is up and running.</p>
|
|
332
266
|
<ul class="service-list" id="deploy-service-list"></ul>
|
|
333
|
-
<a href="
|
|
267
|
+
<a href="http://localhost:4096" class="btn btn-primary">Open Console</a>
|
|
334
268
|
</div>
|
|
335
269
|
|
|
336
270
|
<!-- Deploy error actions -->
|