dsh-miloco-bridge 0.1.0 → 0.1.1
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/README.md +18 -10
- package/cordis.patch.yml +1 -3
- package/{doc → docs}/ARCHITECTURE.md +2 -2
- package/{doc → docs}/INSTALL.md +8 -5
- package/{doc → docs}/SKILL-REWRITES.md +3 -3
- package/{doc → docs}/TOOLS.md +13 -10
- package/{doc → docs}/VERIFICATION.md +22 -6
- package/docs/images/agent-presets.png +0 -0
- package/docs/images/miloco-settings.png +0 -0
- package/lib/client.js +672 -119
- package/lib/settings-api.js +86 -1
- package/lib/tools.js +307 -40
- package/package.json +2 -2
- package/skills/miloco-create-task/SKILL.md +63 -14
- package/doc/images/agent-presets.png +0 -0
- package/doc/images/miloco-settings.png +0 -0
package/lib/client.js
CHANGED
|
@@ -40,55 +40,202 @@ window.__ModuleLoader__.load({
|
|
|
40
40
|
const style = document.createElement('style')
|
|
41
41
|
style.id = STYLE_ID
|
|
42
42
|
style.textContent = `
|
|
43
|
-
.dsh-miloco-bridge-settings {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
.dsh-miloco-bridge-settings {
|
|
44
|
+
--mc-border: var(--dsw-alias-border-l2, rgba(128,128,128,.24));
|
|
45
|
+
--mc-border-soft: var(--dsw-alias-border-l1, rgba(128,128,128,.14));
|
|
46
|
+
--mc-bg: var(--dsw-alias-bg-l1, rgba(128,128,128,.035));
|
|
47
|
+
--mc-bg-soft: var(--dsw-alias-bg-l2, rgba(128,128,128,.07));
|
|
48
|
+
--mc-text: var(--dsw-alias-label-primary, inherit);
|
|
49
|
+
--mc-text-2: var(--dsw-alias-label-secondary, rgba(128,128,128,.95));
|
|
50
|
+
--mc-text-3: var(--dsw-alias-label-tertiary, rgba(128,128,128,.72));
|
|
51
|
+
--mc-accent: var(--dsw-alias-brand-primary, #4a7edc);
|
|
52
|
+
--mc-success: var(--dsw-alias-state-success-primary, #3c9a5a);
|
|
53
|
+
--mc-error: var(--dsw-alias-state-error-primary, #c85a50);
|
|
54
|
+
font-size: 13px; line-height: 1.65; max-width: 820px; color: var(--mc-text);
|
|
55
|
+
}
|
|
56
|
+
.dsh-miloco-bridge-settings h3 { font-size: 14px; margin: 0 0 10px; font-weight: 600; letter-spacing: .01em; }
|
|
57
|
+
.dsh-miloco-bridge-settings .cardHead { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 2px; }
|
|
58
|
+
.dsh-miloco-bridge-settings .cardHead h3 { margin: 0; }
|
|
59
|
+
.dsh-miloco-bridge-settings .iconBtn {
|
|
60
|
+
flex: none; display: inline-flex; align-items: center; justify-content: center;
|
|
61
|
+
width: 26px; height: 26px; padding: 0; border: 0; border-radius: 6px;
|
|
62
|
+
background: transparent; color: var(--mc-text-3);
|
|
63
|
+
}
|
|
64
|
+
.dsh-miloco-bridge-settings .iconBtn:hover:not(:disabled) {
|
|
65
|
+
background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); border-color: transparent;
|
|
66
|
+
}
|
|
67
|
+
.dsh-miloco-bridge-settings .iconBtn.spinning svg { animation: dsh-miloco-spin .9s linear infinite; }
|
|
68
|
+
@keyframes dsh-miloco-spin { to { transform: rotate(360deg); } }
|
|
69
|
+
.dsh-miloco-bridge-settings .card {
|
|
70
|
+
border: 1px solid var(--mc-border); border-radius: 10px; padding: 15px 16px;
|
|
71
|
+
margin-bottom: 12px; background: transparent;
|
|
72
|
+
}
|
|
73
|
+
.dsh-miloco-bridge-settings .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; margin-top: 10px; }
|
|
74
|
+
.dsh-miloco-bridge-settings .stat {
|
|
75
|
+
display: flex; flex-direction: column; padding: 9px 11px; border-radius: 8px;
|
|
76
|
+
background: transparent; border: 1px solid var(--mc-border-soft);
|
|
77
|
+
}
|
|
78
|
+
.dsh-miloco-bridge-settings .stat .k { color: var(--mc-text-3); font-size: 11px; }
|
|
79
|
+
.dsh-miloco-bridge-settings .stat .v { font-size: 17px; font-weight: 600; line-height: 1.35; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
50
80
|
.dsh-miloco-bridge-settings .row { display: flex; align-items: center; gap: 8px; margin: 8px 0; flex-wrap: wrap; }
|
|
51
|
-
.dsh-miloco-bridge-settings .row > label { min-width: 96px;
|
|
52
|
-
.dsh-miloco-bridge-settings
|
|
53
|
-
|
|
54
|
-
|
|
81
|
+
.dsh-miloco-bridge-settings .row > label { min-width: 96px; color: var(--mc-text-2); }
|
|
82
|
+
.dsh-miloco-bridge-settings .field { display: flex; flex-direction: column; gap: 4px; margin: 10px 0; }
|
|
83
|
+
.dsh-miloco-bridge-settings .formGrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 4px 14px; margin: 10px 0; }
|
|
84
|
+
.dsh-miloco-bridge-settings .formGrid .field { margin: 0; }
|
|
85
|
+
.dsh-miloco-bridge-settings .field > label { color: var(--mc-text-2); font-size: 12px; }
|
|
86
|
+
.dsh-miloco-bridge-settings .fieldRow { display: flex; align-items: center; gap: 8px; }
|
|
87
|
+
.dsh-miloco-bridge-settings .fieldRow input:not([type="checkbox"]):not([type="radio"]) { flex: 1 1 auto; width: auto; min-width: 0; }
|
|
88
|
+
.dsh-miloco-bridge-settings .row > select,
|
|
89
|
+
.dsh-miloco-bridge-settings .row > input:not([type="checkbox"]):not([type="radio"]) { flex: 1 1 220px; width: auto; }
|
|
90
|
+
.dsh-miloco-bridge-settings .statusRow {
|
|
91
|
+
display: grid; grid-template-columns: 96px minmax(0, 1fr) auto; align-items: center;
|
|
92
|
+
gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--mc-border-soft);
|
|
93
|
+
}
|
|
94
|
+
.dsh-miloco-bridge-settings .statusRow:last-of-type { border-bottom: 0; }
|
|
95
|
+
.dsh-miloco-bridge-settings .statusLabel { color: var(--mc-text-2); }
|
|
96
|
+
.dsh-miloco-bridge-settings .statusValue { min-width: 0; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
97
|
+
.dsh-miloco-bridge-settings .statusCheck { justify-self: end; color: var(--mc-success); font-weight: 700; font-size: 14px; line-height: 1; }
|
|
98
|
+
.dsh-miloco-bridge-settings .statusEnd { justify-self: end; }
|
|
99
|
+
.dsh-miloco-bridge-settings .statusValue > a, .dsh-miloco-bridge-settings .statusValue > code {
|
|
100
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
|
|
101
|
+
}
|
|
102
|
+
.dsh-miloco-bridge-settings select,
|
|
103
|
+
.dsh-miloco-bridge-settings input:not([type="checkbox"]):not([type="radio"]) {
|
|
104
|
+
flex: 0 0 auto; width: 100%; height: 34px; box-sizing: border-box;
|
|
105
|
+
padding: 6px 9px; border-radius: 7px; border: 1px solid var(--mc-border);
|
|
106
|
+
background: var(--dsw-alias-bg-base, transparent); color: inherit; font: inherit; outline: none;
|
|
107
|
+
}
|
|
108
|
+
.dsh-miloco-bridge-settings select { cursor: pointer; }
|
|
109
|
+
.dsh-miloco-bridge-settings input[type="checkbox"] {
|
|
110
|
+
flex: 0 0 auto; width: auto; min-width: 0; height: auto; margin: 2px 0 0;
|
|
111
|
+
accent-color: var(--mc-accent);
|
|
112
|
+
}
|
|
113
|
+
.dsh-miloco-bridge-settings select:focus, .dsh-miloco-bridge-settings input:focus {
|
|
114
|
+
border-color: var(--mc-accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--mc-accent) 18%, transparent);
|
|
55
115
|
}
|
|
56
116
|
.dsh-miloco-bridge-settings button {
|
|
57
|
-
padding:
|
|
58
|
-
background:
|
|
117
|
+
padding: 6px 12px; border-radius: 7px; border: 1px solid var(--mc-border);
|
|
118
|
+
background: var(--mc-bg-soft); color: inherit; font: inherit; cursor: pointer;
|
|
119
|
+
transition: background .15s ease, border-color .15s ease, opacity .15s ease;
|
|
59
120
|
}
|
|
60
|
-
.dsh-miloco-bridge-settings button
|
|
61
|
-
.dsh-miloco-bridge-settings button
|
|
121
|
+
.dsh-miloco-bridge-settings button:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.14)); border-color: var(--mc-text-3); }
|
|
122
|
+
.dsh-miloco-bridge-settings button.primary { background: var(--mc-accent); border-color: transparent; color: #fff; }
|
|
123
|
+
.dsh-miloco-bridge-settings button.primary:hover:not(:disabled) { opacity: .9; }
|
|
124
|
+
.dsh-miloco-bridge-settings button.dangerBtn { color: var(--mc-error); border-color: var(--mc-error); background: transparent; }
|
|
125
|
+
.dsh-miloco-bridge-settings button:disabled { opacity: .45; cursor: default; }
|
|
62
126
|
.dsh-miloco-bridge-settings .pill {
|
|
63
|
-
display: inline-
|
|
64
|
-
border: 1px solid
|
|
127
|
+
display: inline-flex; align-items: center; padding: 1px 8px; border-radius: 999px;
|
|
128
|
+
font-size: 11.5px; line-height: 1.6; border: 1px solid var(--mc-border); color: var(--mc-text-2);
|
|
129
|
+
white-space: nowrap;
|
|
130
|
+
}
|
|
131
|
+
.dsh-miloco-bridge-settings .pill.ok { border-color: var(--mc-success); color: var(--mc-success); }
|
|
132
|
+
.dsh-miloco-bridge-settings .pill.bad { border-color: var(--mc-error); color: var(--mc-error); }
|
|
133
|
+
.dsh-miloco-bridge-settings .pill.warn { border-color: #b8860b; color: #b8860b; }
|
|
134
|
+
.dsh-miloco-bridge-settings .muted { color: var(--mc-text-3); font-size: 12px; }
|
|
135
|
+
.dsh-miloco-bridge-settings .modeList { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 8px; margin: 12px 0 8px; }
|
|
136
|
+
.dsh-miloco-bridge-settings .modeItem {
|
|
137
|
+
display: flex; align-items: flex-start; gap: 9px; margin: 0; padding: 10px 12px;
|
|
138
|
+
border: 1px solid var(--mc-border); border-radius: 9px; background: transparent;
|
|
139
|
+
cursor: pointer; transition: border-color .15s ease, background .15s ease;
|
|
65
140
|
}
|
|
66
|
-
.dsh-miloco-bridge-settings .
|
|
67
|
-
.dsh-miloco-bridge-settings .
|
|
68
|
-
.dsh-miloco-bridge-settings .pill.warn { border-color: rgba(210,150,50,.6); color: rgba(210,150,50,1); }
|
|
69
|
-
.dsh-miloco-bridge-settings .muted { opacity: .68; font-size: 12px; }
|
|
70
|
-
.dsh-miloco-bridge-settings .modeList { display: flex; flex-direction: column; gap: 4px; margin: 10px 0 6px; }
|
|
71
|
-
.dsh-miloco-bridge-settings .modeItem { display: flex; align-items: flex-start; gap: 8px; margin: 0; padding: 6px 8px; border-radius: 6px; }
|
|
72
|
-
.dsh-miloco-bridge-settings .modeItem:hover { background: rgba(128,128,128,.08); }
|
|
141
|
+
.dsh-miloco-bridge-settings .modeItem:hover { border-color: var(--mc-text-3); }
|
|
142
|
+
.dsh-miloco-bridge-settings .modeItem.selected { border-color: var(--mc-accent); background: var(--mc-bg-soft); }
|
|
73
143
|
.dsh-miloco-bridge-settings .modeItem input[type="radio"] {
|
|
74
144
|
flex: 0 0 auto; min-width: 0; width: auto; margin: 2px 0 0; padding: 0;
|
|
75
|
-
border: 0; background: transparent; accent-color:
|
|
145
|
+
border: 0; background: transparent; accent-color: var(--mc-accent);
|
|
76
146
|
}
|
|
77
|
-
.dsh-miloco-bridge-settings .modeText { display: flex; flex: 1 1 auto; flex-direction: column; gap:
|
|
147
|
+
.dsh-miloco-bridge-settings .modeText { display: flex; flex: 1 1 auto; flex-direction: column; gap: 3px; min-width: 0; text-align: left; }
|
|
148
|
+
.dsh-miloco-bridge-settings .modeText strong { font-size: 13px; }
|
|
78
149
|
.dsh-miloco-bridge-settings .modeText .muted { margin: 0; }
|
|
79
|
-
.dsh-miloco-bridge-settings a { color:
|
|
150
|
+
.dsh-miloco-bridge-settings a { color: var(--mc-accent); text-decoration: none; }
|
|
80
151
|
.dsh-miloco-bridge-settings a:hover { text-decoration: underline; }
|
|
81
|
-
.dsh-miloco-bridge-settings .danger { border: 1px solid
|
|
82
|
-
.dsh-miloco-bridge-settings .danger h4 { margin: 0 0 6px; font-size: 13px; color:
|
|
152
|
+
.dsh-miloco-bridge-settings .danger { border: 1px solid var(--mc-error); border-radius: 8px; padding: 10px 12px; margin-top: 10px; background: transparent; }
|
|
153
|
+
.dsh-miloco-bridge-settings .danger h4 { margin: 0 0 6px; font-size: 13px; color: var(--mc-error); }
|
|
83
154
|
.dsh-miloco-bridge-settings .danger label { display: flex; gap: 6px; align-items: flex-start; margin: 6px 0; font-size: 12px; }
|
|
84
|
-
.dsh-miloco-bridge-settings .guide { margin: 8px 0 0; padding: 10px 12px; border-radius:
|
|
155
|
+
.dsh-miloco-bridge-settings .guide { margin: 8px 0 0; padding: 10px 12px; border-radius: 7px; border: 1px solid var(--mc-border-soft); background: transparent; }
|
|
85
156
|
.dsh-miloco-bridge-settings .guide h4 { margin: 8px 0 4px; font-size: 13px; }
|
|
86
157
|
.dsh-miloco-bridge-settings .guide ul { margin: 4px 0; padding-left: 18px; }
|
|
87
158
|
.dsh-miloco-bridge-settings .guide li { margin: 2px 0; }
|
|
159
|
+
.dsh-miloco-bridge-settings .versionSub {
|
|
160
|
+
display: flex; flex-wrap: wrap; align-items: center; flex-basis: 100%;
|
|
161
|
+
min-width: 0; color: var(--mc-text-3); font-size: 12px; line-height: 1.6;
|
|
162
|
+
}
|
|
163
|
+
.dsh-miloco-bridge-settings .versionSub > * + *::before {
|
|
164
|
+
content: '·'; margin: 0 6px; color: var(--mc-text-3);
|
|
165
|
+
}
|
|
166
|
+
.dsh-miloco-bridge-settings .versionSub .versionLink {
|
|
167
|
+
border: 0; background: transparent; padding: 0; border-radius: 0;
|
|
168
|
+
color: var(--mc-accent); font: inherit; line-height: inherit; cursor: pointer;
|
|
169
|
+
}
|
|
170
|
+
.dsh-miloco-bridge-settings .versionSub .versionLink:hover:not(:disabled) {
|
|
171
|
+
background: transparent; text-decoration: underline;
|
|
172
|
+
}
|
|
173
|
+
.dsh-miloco-bridge-settings .versionDirty { color: #b8860b; }
|
|
174
|
+
.dsh-miloco-bridge-settings .versionNote { margin-top: 6px; }
|
|
175
|
+
.dsh-miloco-bridge-settings .versionManualPanel { margin-top: 8px; }
|
|
176
|
+
.dsh-miloco-bridge-settings .versionCommand {
|
|
177
|
+
margin: 0; padding: 8px 10px; border-radius: 6px; max-height: 120px; overflow: auto;
|
|
178
|
+
border: 1px solid var(--mc-border-soft); background: var(--dsw-alias-bg-base, transparent);
|
|
179
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
180
|
+
font-size: 11.5px; line-height: 1.5; white-space: pre-wrap; word-break: break-word;
|
|
181
|
+
}
|
|
88
182
|
.dsh-miloco-bridge-settings code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
|
|
183
|
+
.dsh-miloco-preset-helpbar { display: inline-flex; align-items: center; gap: 2px; margin-right: auto; }
|
|
184
|
+
.dsh-miloco-preset-helpbtn {
|
|
185
|
+
border: 0; background: transparent; color: var(--dsw-alias-label-secondary, rgba(128,128,128,.95));
|
|
186
|
+
font: inherit; font-size: 12px; line-height: 1.5; padding: 2px 6px; border-radius: 5px; cursor: pointer;
|
|
187
|
+
}
|
|
188
|
+
.dsh-miloco-preset-helpbtn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); }
|
|
189
|
+
.dsh-miloco-guide-backdrop {
|
|
190
|
+
position: fixed; inset: 0; z-index: 10000; display: flex; align-items: center; justify-content: center;
|
|
191
|
+
padding: 24px; background: rgba(0,0,0,.24);
|
|
192
|
+
}
|
|
193
|
+
.dsh-miloco-guide-modal {
|
|
194
|
+
display: flex; flex-direction: column; width: min(640px, 100%); height: min(600px, 100%);
|
|
195
|
+
max-height: 100%; overflow: hidden; padding: 0; border-radius: 16px; border: 0;
|
|
196
|
+
background: var(--dsw-alias-bg-base, #fff); color: var(--dsw-alias-label-primary, #111);
|
|
197
|
+
box-shadow: 0 18px 56px rgba(0,0,0,.22);
|
|
198
|
+
}
|
|
199
|
+
.dsh-miloco-guide-header { flex: none; padding: 24px 24px 20px 28px; }
|
|
200
|
+
.dsh-miloco-guide-title-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
|
|
201
|
+
.dsh-miloco-guide-title { margin: 0; font-size: 20px; font-weight: 600; line-height: 28px; }
|
|
202
|
+
.dsh-miloco-guide-intro { margin: 8px 0 0; color: var(--dsw-alias-label-secondary, rgba(128,128,128,.95)); font-size: 14px; line-height: 22px; }
|
|
203
|
+
.dsh-miloco-guide-close {
|
|
204
|
+
flex: none; width: 32px; height: 32px; border: 0; border-radius: 8px; padding: 0;
|
|
205
|
+
background: transparent; color: var(--dsw-alias-label-tertiary, rgba(128,128,128,.8));
|
|
206
|
+
font: inherit; font-size: 22px; line-height: 1; cursor: pointer;
|
|
207
|
+
}
|
|
208
|
+
.dsh-miloco-guide-close:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); }
|
|
209
|
+
.dsh-miloco-guide-tabs {
|
|
210
|
+
flex: none; display: grid; grid-template-columns: 1fr 1fr; gap: 4px;
|
|
211
|
+
margin: 0 28px 22px; padding: 4px; border-radius: 10px;
|
|
212
|
+
background: var(--dsw-alias-bg-l2, #f5f5f5);
|
|
213
|
+
}
|
|
214
|
+
.dsh-miloco-guide-tab {
|
|
215
|
+
border: 1px solid transparent; border-radius: 8px; padding: 7px 10px;
|
|
216
|
+
background: transparent; color: var(--dsw-alias-label-secondary, rgba(128,128,128,.95));
|
|
217
|
+
font: inherit; font-size: 14px; line-height: 20px; cursor: pointer;
|
|
218
|
+
}
|
|
219
|
+
.dsh-miloco-guide-tab.selected {
|
|
220
|
+
background: var(--dsw-alias-bg-base, #fff);
|
|
221
|
+
border-color: var(--dsw-alias-border-l2, rgba(128,128,128,.24));
|
|
222
|
+
color: var(--dsw-alias-label-primary, #111);
|
|
223
|
+
box-shadow: 0 1px 2px rgba(0,0,0,.04);
|
|
224
|
+
}
|
|
225
|
+
.dsh-miloco-guide-panel {
|
|
226
|
+
flex: 1; min-height: 0; overflow-y: auto; padding: 0 24px 24px 28px;
|
|
227
|
+
color: var(--dsw-alias-label-primary, #111); font-size: 16px; overflow-wrap: anywhere;
|
|
228
|
+
}
|
|
229
|
+
.dsh-miloco-guide-panel h3 { margin: 28px 0 10px; font-size: 16px; font-weight: 600; line-height: 24px; }
|
|
230
|
+
.dsh-miloco-guide-panel h3:first-child { margin-top: 0; }
|
|
231
|
+
.dsh-miloco-guide-panel p { margin: 0 0 16px; font-size: 16px; line-height: 1.625; }
|
|
232
|
+
.dsh-miloco-guide-panel ul { margin: 0 0 16px; padding-left: 22px; }
|
|
233
|
+
.dsh-miloco-guide-panel li { margin: 6px 0; font-size: 16px; line-height: 1.6; }
|
|
234
|
+
.dsh-miloco-guide-panel blockquote { margin: 10px 0 14px; padding: 12px 16px; border: 0; border-radius: 10px; background: var(--dsw-alias-bg-module-platform, var(--dsw-alias-bg-l2, #f5f5f5)); }
|
|
235
|
+
.dsh-miloco-guide-panel blockquote p { margin: 0; }
|
|
89
236
|
.dsh-miloco-bridge-settings pre {
|
|
90
237
|
margin: 8px 0 0; padding: 8px 10px; border-radius: 6px; max-height: 260px; overflow: auto;
|
|
91
|
-
border: 1px solid
|
|
238
|
+
border: 1px solid var(--mc-border-soft); font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
92
239
|
font-size: 11.5px; line-height: 1.5; white-space: pre-wrap;
|
|
93
240
|
}
|
|
94
241
|
`
|
|
@@ -169,6 +316,157 @@ window.__ModuleLoader__.load({
|
|
|
169
316
|
return options
|
|
170
317
|
}
|
|
171
318
|
|
|
319
|
+
/**
|
|
320
|
+
* DSH 0.2.1 only ships guide dialogs for built-in presets. Add equivalent
|
|
321
|
+
* explanation/usage buttons to the custom Miloco preset card so the same
|
|
322
|
+
* help is reachable from 「设置 → Agent 预设」. This is a defensive DOM
|
|
323
|
+
* enhancement: if DSH changes the card structure, it silently does nothing.
|
|
324
|
+
*/
|
|
325
|
+
const PRESET_GUIDE = {
|
|
326
|
+
title: 'Miloco 家庭助手',
|
|
327
|
+
intro: {
|
|
328
|
+
explanation: '只读和管家两种模式决定这个预设能做什么。',
|
|
329
|
+
usage: '新建任务时选择「Miloco 家庭助手」,说明要完成什么、涉及哪个房间或家人。',
|
|
330
|
+
},
|
|
331
|
+
content: {
|
|
332
|
+
explanation: [
|
|
333
|
+
'<h3>保留哪些能力</h3>',
|
|
334
|
+
'<p>保留 DSH 通用 Agent 的对话与工具调用;米家设备、摄像头、感知、规则/任务与家庭档案',
|
|
335
|
+
'是它独有的能力,且只在这个预设内出现。</p>',
|
|
336
|
+
'<h3>什么时候选</h3>',
|
|
337
|
+
'<p>想要联动家居设备时:只需要了解家里情况时选只读;需要操作设备或维护家庭信息时选管家。',
|
|
338
|
+
'涉及门锁、燃气、摄像头开关等动作时,技能层会要求二次确认。</p>',
|
|
339
|
+
'<p>模式说明与示例见「设置 → 插件 → Miloco」。</p>',
|
|
340
|
+
].join(''),
|
|
341
|
+
usage: [
|
|
342
|
+
'<h3>查看家里情况</h3>',
|
|
343
|
+
'<blockquote><p>客厅现在有人吗?刚才门口发生了什么?</p></blockquote>',
|
|
344
|
+
'<p>用摄像头画面和感知事件回答,不控制设备。</p>',
|
|
345
|
+
'<h3>操作设备或执行场景</h3>',
|
|
346
|
+
'<blockquote><p>把客厅灯打开;执行回家模式。</p></blockquote>',
|
|
347
|
+
'<p>需要管家模式;控制前会先确认设备与参数。</p>',
|
|
348
|
+
'<h3>维护家庭信息</h3>',
|
|
349
|
+
'<blockquote><p>记一下孩子每天喝几杯水;奶奶对什么过敏?</p></blockquote>',
|
|
350
|
+
'<p>用于家庭档案、成员与宠物登记,以及任务创建。</p>',
|
|
351
|
+
].join(''),
|
|
352
|
+
},
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Open a read-only help dialog matching DSH's native preset guide layout.
|
|
357
|
+
* @param {'explanation'|'usage'} kind - Initial guide page.
|
|
358
|
+
*/
|
|
359
|
+
function openPresetGuide(kind) {
|
|
360
|
+
if (typeof document === 'undefined' || !document.body) return
|
|
361
|
+
document.querySelector('.dsh-miloco-guide-backdrop')?.remove()
|
|
362
|
+
const backdrop = document.createElement('div')
|
|
363
|
+
backdrop.className = 'dsh-miloco-guide-backdrop'
|
|
364
|
+
const modal = document.createElement('div')
|
|
365
|
+
modal.className = 'dsh-miloco-guide-modal'
|
|
366
|
+
modal.setAttribute('role', 'dialog')
|
|
367
|
+
modal.setAttribute('aria-modal', 'true')
|
|
368
|
+
|
|
369
|
+
const header = document.createElement('div')
|
|
370
|
+
header.className = 'dsh-miloco-guide-header'
|
|
371
|
+
const titleRow = document.createElement('div')
|
|
372
|
+
titleRow.className = 'dsh-miloco-guide-title-row'
|
|
373
|
+
const title = document.createElement('h2')
|
|
374
|
+
title.className = 'dsh-miloco-guide-title'
|
|
375
|
+
title.textContent = PRESET_GUIDE.title
|
|
376
|
+
const close = document.createElement('button')
|
|
377
|
+
close.type = 'button'
|
|
378
|
+
close.className = 'dsh-miloco-guide-close'
|
|
379
|
+
close.textContent = '×'
|
|
380
|
+
close.setAttribute('aria-label', '关闭')
|
|
381
|
+
const intro = document.createElement('p')
|
|
382
|
+
intro.className = 'dsh-miloco-guide-intro'
|
|
383
|
+
titleRow.append(title, close)
|
|
384
|
+
header.append(titleRow, intro)
|
|
385
|
+
|
|
386
|
+
const tabs = document.createElement('div')
|
|
387
|
+
tabs.className = 'dsh-miloco-guide-tabs'
|
|
388
|
+
const tabExplanation = document.createElement('button')
|
|
389
|
+
tabExplanation.type = 'button'
|
|
390
|
+
tabExplanation.className = 'dsh-miloco-guide-tab'
|
|
391
|
+
tabExplanation.textContent = '模式说明'
|
|
392
|
+
const tabUsage = document.createElement('button')
|
|
393
|
+
tabUsage.type = 'button'
|
|
394
|
+
tabUsage.className = 'dsh-miloco-guide-tab'
|
|
395
|
+
tabUsage.textContent = '如何使用'
|
|
396
|
+
tabs.append(tabExplanation, tabUsage)
|
|
397
|
+
|
|
398
|
+
const panel = document.createElement('div')
|
|
399
|
+
panel.className = 'dsh-miloco-guide-panel'
|
|
400
|
+
|
|
401
|
+
const setPage = next => {
|
|
402
|
+
const active = next === 'usage' ? 'usage' : 'explanation'
|
|
403
|
+
panel.innerHTML = PRESET_GUIDE.content[active]
|
|
404
|
+
panel.dataset.guidePage = active
|
|
405
|
+
intro.textContent = PRESET_GUIDE.intro[active]
|
|
406
|
+
tabExplanation.classList.toggle('selected', active === 'explanation')
|
|
407
|
+
tabUsage.classList.toggle('selected', active === 'usage')
|
|
408
|
+
}
|
|
409
|
+
tabExplanation.addEventListener('click', () => setPage('explanation'))
|
|
410
|
+
tabUsage.addEventListener('click', () => setPage('usage'))
|
|
411
|
+
setPage(kind)
|
|
412
|
+
|
|
413
|
+
modal.append(header, tabs, panel)
|
|
414
|
+
backdrop.append(modal)
|
|
415
|
+
|
|
416
|
+
const dismiss = () => {
|
|
417
|
+
backdrop.remove()
|
|
418
|
+
document.removeEventListener('keydown', onKey)
|
|
419
|
+
}
|
|
420
|
+
const onKey = event => {
|
|
421
|
+
if (event.key === 'Escape') dismiss()
|
|
422
|
+
}
|
|
423
|
+
close.addEventListener('click', dismiss)
|
|
424
|
+
backdrop.addEventListener('click', event => {
|
|
425
|
+
if (event.target === backdrop) dismiss()
|
|
426
|
+
})
|
|
427
|
+
document.addEventListener('keydown', onKey)
|
|
428
|
+
document.body.append(backdrop)
|
|
429
|
+
close.focus()
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Add help buttons to the custom Miloco preset card when it appears.
|
|
434
|
+
* @param {any} ctx - Browser plugin context.
|
|
435
|
+
*/
|
|
436
|
+
function installPresetGuide(ctx) {
|
|
437
|
+
if (typeof document === 'undefined' || !document.body) return
|
|
438
|
+
const enhance = () => {
|
|
439
|
+
const card = document.querySelector('[data-agent-preset-id="miloco"]')
|
|
440
|
+
if (!card || card.dataset.dshMilocoGuide === '1') return
|
|
441
|
+
const foot = card.querySelector('[class*="cardFoot"]')
|
|
442
|
+
if (!foot || foot.querySelector('.dsh-miloco-preset-helpbar')) return
|
|
443
|
+
const bar = document.createElement('span')
|
|
444
|
+
bar.className = 'dsh-miloco-preset-helpbar'
|
|
445
|
+
const makeButton = (label, kind) => {
|
|
446
|
+
const button = document.createElement('button')
|
|
447
|
+
button.type = 'button'
|
|
448
|
+
button.className = 'dsh-miloco-preset-helpbtn'
|
|
449
|
+
button.textContent = label
|
|
450
|
+
button.setAttribute('aria-label', `${label}: Miloco 家庭助手`)
|
|
451
|
+
button.addEventListener('click', event => {
|
|
452
|
+
event.preventDefault()
|
|
453
|
+
event.stopPropagation()
|
|
454
|
+
openPresetGuide(kind)
|
|
455
|
+
})
|
|
456
|
+
return button
|
|
457
|
+
}
|
|
458
|
+
bar.append(makeButton('模式说明', 'explanation'), makeButton('如何使用', 'usage'))
|
|
459
|
+
foot.insertBefore(bar, foot.firstChild)
|
|
460
|
+
card.dataset.dshMilocoGuide = '1'
|
|
461
|
+
}
|
|
462
|
+
const observer = new MutationObserver(enhance)
|
|
463
|
+
observer.observe(document.body, { childList: true, subtree: true })
|
|
464
|
+
enhance()
|
|
465
|
+
try {
|
|
466
|
+
ctx.effect?.(() => () => observer.disconnect(), 'dsh-miloco: preset guide')
|
|
467
|
+
} catch { /* 客户端页面卸载时忽略 */ }
|
|
468
|
+
}
|
|
469
|
+
|
|
172
470
|
function MilocoSettingsTab() {
|
|
173
471
|
const [status, setStatus] = React.useState(null)
|
|
174
472
|
const [error, setError] = React.useState(null)
|
|
@@ -191,6 +489,14 @@ window.__ModuleLoader__.load({
|
|
|
191
489
|
const [confirmText, setConfirmText] = React.useState('')
|
|
192
490
|
const [uninstallBusy, setUninstallBusy] = React.useState(false)
|
|
193
491
|
const [uninstallResult, setUninstallResult] = React.useState(null)
|
|
492
|
+
const [versionInfo, setVersionInfo] = React.useState(null)
|
|
493
|
+
const [versionBusy, setVersionBusy] = React.useState(false)
|
|
494
|
+
const [versionError, setVersionError] = React.useState(null)
|
|
495
|
+
const [versionCommandOpen, setVersionCommandOpen] = React.useState(false)
|
|
496
|
+
const [upgradeStatus, setUpgradeStatus] = React.useState(null)
|
|
497
|
+
const [upgradeConfirmOpen, setUpgradeConfirmOpen] = React.useState(false)
|
|
498
|
+
const [upgradeBusy, setUpgradeBusy] = React.useState(false)
|
|
499
|
+
const [upgradePolling, setUpgradePolling] = React.useState(false)
|
|
194
500
|
|
|
195
501
|
const reload = React.useCallback(() => {
|
|
196
502
|
setError(null)
|
|
@@ -209,7 +515,74 @@ window.__ModuleLoader__.load({
|
|
|
209
515
|
.catch((cause) => setError(cause instanceof Error ? cause.message : String(cause)))
|
|
210
516
|
}, [])
|
|
211
517
|
|
|
212
|
-
React.
|
|
518
|
+
const loadVersion = React.useCallback((refresh = false) => {
|
|
519
|
+
setVersionBusy(true)
|
|
520
|
+
setVersionError(null)
|
|
521
|
+
call(`/version${refresh ? '?refresh=1' : ''}`)
|
|
522
|
+
.then((payload) => {
|
|
523
|
+
setVersionInfo(payload)
|
|
524
|
+
setUpgradeStatus(payload.upgradeStatus ?? null)
|
|
525
|
+
if (payload.error) setVersionError(payload.error)
|
|
526
|
+
})
|
|
527
|
+
.catch((cause) => setVersionError(cause instanceof Error ? cause.message : String(cause)))
|
|
528
|
+
.finally(() => setVersionBusy(false))
|
|
529
|
+
}, [])
|
|
530
|
+
|
|
531
|
+
const pollUpgrade = React.useCallback(() => {
|
|
532
|
+
setUpgradePolling(true)
|
|
533
|
+
const started = Date.now()
|
|
534
|
+
const tick = () => {
|
|
535
|
+
call('/upgrade/status')
|
|
536
|
+
.then((payload) => {
|
|
537
|
+
const status = payload.status
|
|
538
|
+
setUpgradeStatus(status)
|
|
539
|
+
if (status && ['done', 'failed', 'idle'].includes(status.phase)) {
|
|
540
|
+
setUpgradePolling(false)
|
|
541
|
+
loadVersion(true)
|
|
542
|
+
return
|
|
543
|
+
}
|
|
544
|
+
if (Date.now() - started > 5 * 60_000) {
|
|
545
|
+
setUpgradePolling(false)
|
|
546
|
+
loadVersion(true)
|
|
547
|
+
return
|
|
548
|
+
}
|
|
549
|
+
window.setTimeout(tick, 3000)
|
|
550
|
+
})
|
|
551
|
+
.catch(() => {
|
|
552
|
+
// The upgrade restarts the backend; treat this as the restart phase.
|
|
553
|
+
setUpgradeStatus({ phase: 'restarting' })
|
|
554
|
+
if (Date.now() - started > 5 * 60_000) {
|
|
555
|
+
setUpgradePolling(false)
|
|
556
|
+
loadVersion(true)
|
|
557
|
+
return
|
|
558
|
+
}
|
|
559
|
+
window.setTimeout(tick, 3000)
|
|
560
|
+
})
|
|
561
|
+
}
|
|
562
|
+
tick()
|
|
563
|
+
}, [loadVersion])
|
|
564
|
+
|
|
565
|
+
const runUpgrade = React.useCallback(() => {
|
|
566
|
+
setUpgradeBusy(true)
|
|
567
|
+
setVersionError(null)
|
|
568
|
+
call('/upgrade', {
|
|
569
|
+
method: 'POST',
|
|
570
|
+
headers: { 'content-type': 'application/json' },
|
|
571
|
+
body: JSON.stringify({}),
|
|
572
|
+
})
|
|
573
|
+
.then(() => {
|
|
574
|
+
setUpgradeConfirmOpen(false)
|
|
575
|
+
setUpgradeStatus({ phase: 'starting' })
|
|
576
|
+
pollUpgrade()
|
|
577
|
+
})
|
|
578
|
+
.catch((cause) => setVersionError(cause instanceof Error ? cause.message : String(cause)))
|
|
579
|
+
.finally(() => setUpgradeBusy(false))
|
|
580
|
+
}, [pollUpgrade])
|
|
581
|
+
|
|
582
|
+
React.useEffect(() => {
|
|
583
|
+
reload()
|
|
584
|
+
loadVersion(false)
|
|
585
|
+
}, [reload, loadVersion])
|
|
213
586
|
|
|
214
587
|
const refreshSessions = React.useCallback(() => {
|
|
215
588
|
call('/sessions')
|
|
@@ -371,7 +744,8 @@ window.__ModuleLoader__.load({
|
|
|
371
744
|
onChange: (event) => setConnectPushToken(event.target.value),
|
|
372
745
|
})),
|
|
373
746
|
h('div', { className: 'row' },
|
|
374
|
-
h('button', { type: 'button',
|
|
747
|
+
h('button', { type: 'button', onClick: () => setConnectOpen(false) }, '取消'),
|
|
748
|
+
h('button', { type: 'button', className: 'primary', disabled: busy || !connectUrl, onClick: connect }, '连接')))
|
|
375
749
|
: null
|
|
376
750
|
|
|
377
751
|
// ---------------------------------------------------------------- 部署 / 连接
|
|
@@ -384,8 +758,15 @@ window.__ModuleLoader__.load({
|
|
|
384
758
|
h('div', { className: 'row' },
|
|
385
759
|
h('button', { type: 'button', className: 'primary', disabled: busy, onClick: deploy },
|
|
386
760
|
busy ? '部署中…' : '一键部署 Miloco'),
|
|
387
|
-
h('button', {
|
|
388
|
-
|
|
761
|
+
h('button', {
|
|
762
|
+
type: 'button',
|
|
763
|
+
disabled: busy,
|
|
764
|
+
onClick: () => {
|
|
765
|
+
const opening = !connectOpen
|
|
766
|
+
setConnectOpen(opening)
|
|
767
|
+
if (opening) setUninstallOpen(false)
|
|
768
|
+
},
|
|
769
|
+
}, '连接已有的 Miloco')),
|
|
389
770
|
connectForm,
|
|
390
771
|
deployLog ? h('pre', null, deployLog) : null))
|
|
391
772
|
}
|
|
@@ -397,7 +778,7 @@ window.__ModuleLoader__.load({
|
|
|
397
778
|
h('div', { className: 'grid' },
|
|
398
779
|
h('div', { className: 'stat' },
|
|
399
780
|
h('span', { className: 'k' }, '米家账号'),
|
|
400
|
-
h('span', { className: 'v' }, overview.account?.nickname || (overview.accountBound ? '已绑定' : '未绑定'))),
|
|
781
|
+
h('span', { className: 'v', title: overview.account?.nickname || '' }, overview.account?.nickname || (overview.accountBound ? '已绑定' : '未绑定'))),
|
|
401
782
|
h('div', { className: 'stat' },
|
|
402
783
|
h('span', { className: 'k' }, '设备'),
|
|
403
784
|
h('span', { className: 'v' }, overview.deviceCount ?? '—')),
|
|
@@ -430,7 +811,7 @@ window.__ModuleLoader__.load({
|
|
|
430
811
|
h('div', { className: 'muted' }, '模式决定这个预设手里有哪些工具:'),
|
|
431
812
|
h('div', { className: 'modeList' }, modes.map(item => h('label', {
|
|
432
813
|
key: item.id,
|
|
433
|
-
className:
|
|
814
|
+
className: `modeItem${mode === item.id ? ' selected' : ''}`,
|
|
434
815
|
},
|
|
435
816
|
h('input', {
|
|
436
817
|
type: 'radio', name: 'dsh-miloco-bridge-mode', checked: mode === item.id,
|
|
@@ -478,60 +859,62 @@ window.__ModuleLoader__.load({
|
|
|
478
859
|
: ''
|
|
479
860
|
cards.push(h('div', { className: 'card', key: 'target' },
|
|
480
861
|
h('h3', null, '推送目标'),
|
|
481
|
-
h('div', { className: '
|
|
862
|
+
h('div', { className: 'field' },
|
|
482
863
|
h('label', null, '对话会话'),
|
|
483
|
-
h('
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
864
|
+
h('div', { className: 'fieldRow' },
|
|
865
|
+
h('input', {
|
|
866
|
+
type: 'text',
|
|
867
|
+
list: SESSION_LIST_ID,
|
|
868
|
+
value: sessionId,
|
|
869
|
+
placeholder: lastSession
|
|
870
|
+
? `留空 = 上一次触发 miloco 的会话(${lastSession})`
|
|
871
|
+
: '留空 = 上一次触发 miloco 的会话(还没有)',
|
|
872
|
+
onChange: (event) => setSessionId(event.target.value),
|
|
873
|
+
}),
|
|
874
|
+
h('datalist', { id: SESSION_LIST_ID }, sessionOptions(status)),
|
|
875
|
+
h('button', { type: 'button', disabled: busy, onClick: refreshSessions }, '刷新会话'))),
|
|
494
876
|
h('div', { className: 'muted' },
|
|
495
877
|
'留空 = 投到「上一次触发 miloco 的会话」,也就是最近一次调用过米家工具的对话。'
|
|
496
878
|
+ '也可以从下拉里选任意对话,或直接填会话 id。'),
|
|
497
|
-
h('div', { className: '
|
|
498
|
-
h('
|
|
499
|
-
|
|
500
|
-
value: modelValue,
|
|
501
|
-
onChange: (event) => {
|
|
502
|
-
setModelValue(event.target.value)
|
|
503
|
-
setReasoningEffort('')
|
|
504
|
-
},
|
|
505
|
-
}, modelOptions(status))),
|
|
506
|
-
selectedEfforts.length > 0
|
|
507
|
-
? h('div', { className: 'row' },
|
|
508
|
-
h('label', null, '推理强度'),
|
|
879
|
+
h('div', { className: 'formGrid' },
|
|
880
|
+
h('div', { className: 'field' },
|
|
881
|
+
h('label', null, '模型'),
|
|
509
882
|
h('select', {
|
|
510
|
-
value:
|
|
511
|
-
onChange: (event) =>
|
|
883
|
+
value: modelValue,
|
|
884
|
+
onChange: (event) => {
|
|
885
|
+
setModelValue(event.target.value)
|
|
886
|
+
setReasoningEffort('')
|
|
887
|
+
},
|
|
888
|
+
}, modelOptions(status))),
|
|
889
|
+
selectedEfforts.length > 0
|
|
890
|
+
? h('div', { className: 'field' },
|
|
891
|
+
h('label', null, '推理强度'),
|
|
892
|
+
h('select', {
|
|
893
|
+
value: effortValue,
|
|
894
|
+
onChange: (event) => setReasoningEffort(event.target.value),
|
|
895
|
+
}, [
|
|
896
|
+
h('option', { key: '__model_default__', value: '' }, '跟随模型默认'),
|
|
897
|
+
...selectedEfforts.map(item => h('option', {
|
|
898
|
+
key: item.id,
|
|
899
|
+
value: item.id,
|
|
900
|
+
}, item.name || item.id)),
|
|
901
|
+
]))
|
|
902
|
+
: null,
|
|
903
|
+
h('div', { className: 'field' },
|
|
904
|
+
h('label', null, '感知等级'),
|
|
905
|
+
h('select', {
|
|
906
|
+
value: perceptionLevel,
|
|
907
|
+
onChange: (event) => setPerceptionLevel(event.target.value),
|
|
512
908
|
}, [
|
|
513
|
-
h('option', { key: '
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
]))
|
|
519
|
-
: null,
|
|
520
|
-
h('div', { className: 'row' },
|
|
521
|
-
h('label', null, '感知等级'),
|
|
522
|
-
h('select', {
|
|
523
|
-
value: perceptionLevel,
|
|
524
|
-
onChange: (event) => setPerceptionLevel(event.target.value),
|
|
525
|
-
}, [
|
|
526
|
-
h('option', { key: 'all', value: 'all' }, '全部事件'),
|
|
527
|
-
h('option', { key: 'low', value: 'low' }, '低及以上'),
|
|
528
|
-
h('option', { key: 'medium', value: 'medium' }, '中及以上'),
|
|
529
|
-
h('option', { key: 'high', value: 'high' }, '仅高优先级'),
|
|
530
|
-
])),
|
|
909
|
+
h('option', { key: 'all', value: 'all' }, '全部事件'),
|
|
910
|
+
h('option', { key: 'low', value: 'low' }, '低及以上'),
|
|
911
|
+
h('option', { key: 'medium', value: 'medium' }, '中及以上'),
|
|
912
|
+
h('option', { key: 'high', value: 'high' }, '仅高优先级'),
|
|
913
|
+
]))),
|
|
531
914
|
h('div', { className: 'muted' },
|
|
532
915
|
'低于所选等级的感知建议不会投递到 DSH;规则、任务、onboarding 与普通对话事件不受影响。'),
|
|
533
916
|
h('div', { className: 'row' },
|
|
534
|
-
h('button', { type: 'button', disabled: busy, onClick: save }, busy ? '保存中…' : '保存'),
|
|
917
|
+
h('button', { type: 'button', className: 'primary', disabled: busy, onClick: save }, busy ? '保存中…' : '保存'),
|
|
535
918
|
saved ? h('span', { className: 'pill ok' }, saved) : null,
|
|
536
919
|
error ? h('span', { className: 'pill bad' }, error) : null),
|
|
537
920
|
h('div', { className: 'muted' },
|
|
@@ -539,47 +922,212 @@ window.__ModuleLoader__.load({
|
|
|
539
922
|
|
|
540
923
|
// ---------------------------------------------------------------- 运行状态
|
|
541
924
|
const deployState = status.deploy ?? {}
|
|
925
|
+
const versionData = versionInfo?.version ?? null
|
|
926
|
+
const upgradeInfo = versionInfo?.upgrade ?? null
|
|
927
|
+
const currentVersion = upgradeInfo?.current || versionData?.version || ''
|
|
928
|
+
const latestVersion = upgradeInfo?.latest || ''
|
|
929
|
+
const deployKind = upgradeInfo?.deploy_kind || ''
|
|
930
|
+
const hasUpdate = Boolean(upgradeInfo?.has_update)
|
|
931
|
+
const isDevDeployment = deployKind === 'dev'
|
|
932
|
+
const releaseUrl = upgradeInfo?.release_url || ''
|
|
933
|
+
const sourceRoot = versionInfo?.sourceRoot || deployState.sourceDir || ''
|
|
934
|
+
const gitDirty = Boolean(versionData?.git?.dirty)
|
|
935
|
+
const commitShort = versionData?.git?.commit_short || ''
|
|
936
|
+
const upgradePhase = upgradeStatus?.phase || ''
|
|
937
|
+
const upgradePhaseLabels = {
|
|
938
|
+
idle: '空闲',
|
|
939
|
+
starting: '准备中',
|
|
940
|
+
downloading: '下载中',
|
|
941
|
+
installing: '安装中',
|
|
942
|
+
restarting: '服务重启中',
|
|
943
|
+
done: '已完成',
|
|
944
|
+
failed: '失败',
|
|
945
|
+
}
|
|
946
|
+
const devUpdateCommand = sourceRoot
|
|
947
|
+
? `cd ${sourceRoot} && git pull --ff-only && bash scripts/install.sh --dev --skip-openclaw && systemctl --user restart miloco.service`
|
|
948
|
+
: ''
|
|
949
|
+
const versionPill = versionInfo?.reachable === false
|
|
950
|
+
? ['bad', '不可用']
|
|
951
|
+
: !versionInfo
|
|
952
|
+
? ['', versionBusy ? '读取中' : '未知']
|
|
953
|
+
: isDevDeployment
|
|
954
|
+
? ['', '源码部署']
|
|
955
|
+
: hasUpdate
|
|
956
|
+
? ['warn', '有新版本']
|
|
957
|
+
: ['ok', '已是最新']
|
|
958
|
+
|
|
542
959
|
cards.push(h('div', { className: 'card', key: 'runtime' },
|
|
543
|
-
h('
|
|
544
|
-
|
|
545
|
-
h('
|
|
546
|
-
|
|
547
|
-
?
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
h('
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
h('
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
960
|
+
h('div', { className: 'cardHead' },
|
|
961
|
+
h('h3', null, '运行状态'),
|
|
962
|
+
h('button', {
|
|
963
|
+
type: 'button',
|
|
964
|
+
className: `iconBtn${busy || versionBusy ? ' spinning' : ''}`,
|
|
965
|
+
title: '刷新运行状态与版本信息',
|
|
966
|
+
'aria-label': '刷新运行状态与版本信息',
|
|
967
|
+
disabled: busy,
|
|
968
|
+
onClick: () => {
|
|
969
|
+
reload()
|
|
970
|
+
loadVersion(true)
|
|
971
|
+
},
|
|
972
|
+
}, h('svg', {
|
|
973
|
+
viewBox: '0 0 24 24',
|
|
974
|
+
width: 16,
|
|
975
|
+
height: 16,
|
|
976
|
+
fill: 'none',
|
|
977
|
+
stroke: 'currentColor',
|
|
978
|
+
strokeWidth: 1.8,
|
|
979
|
+
strokeLinecap: 'round',
|
|
980
|
+
strokeLinejoin: 'round',
|
|
981
|
+
'aria-hidden': 'true',
|
|
982
|
+
},
|
|
983
|
+
h('path', { d: 'M20 11a8.1 8.1 0 0 0-15.5-2m-.5-4v4h4' }),
|
|
984
|
+
h('path', { d: 'M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4' })))),
|
|
985
|
+
h('div', { className: 'statusRow' },
|
|
986
|
+
h('span', { className: 'statusLabel' }, '后端'),
|
|
987
|
+
h('div', { className: 'statusValue' },
|
|
988
|
+
connection.baseUrl
|
|
989
|
+
? h('a', {
|
|
990
|
+
href: connection.baseUrl,
|
|
991
|
+
target: '_blank',
|
|
992
|
+
rel: 'noopener noreferrer',
|
|
993
|
+
title: '在新标签页打开 Miloco 面板',
|
|
994
|
+
}, connection.baseUrl)
|
|
995
|
+
: h('code', null, '—'),
|
|
996
|
+
h('span', { className: 'muted' }, `来源:${connection.source ?? '—'}`)),
|
|
997
|
+
reachable
|
|
998
|
+
? h('span', { className: 'statusCheck', title: '已连通', 'aria-label': '已连通' }, '✓')
|
|
999
|
+
: h('span', { className: 'statusEnd pill bad' }, '连不上')),
|
|
1000
|
+
h('div', { className: 'statusRow' },
|
|
1001
|
+
h('span', { className: 'statusLabel' }, '推送入口'),
|
|
1002
|
+
h('div', { className: 'statusValue' },
|
|
1003
|
+
h('code', null, push.path || '—')),
|
|
1004
|
+
push.tokenSet
|
|
1005
|
+
? h('span', { className: 'statusCheck', title: '令牌已配置', 'aria-label': '令牌已配置' }, '✓')
|
|
1006
|
+
: h('span', { className: 'statusEnd pill bad' }, '未配置')),
|
|
1007
|
+
h('div', { className: 'statusRow' },
|
|
1008
|
+
h('span', { className: 'statusLabel' }, 'Miloco adapter'),
|
|
1009
|
+
h('div', { className: 'statusValue' },
|
|
1010
|
+
h('span', { className: 'muted' },
|
|
1011
|
+
adapter.installed
|
|
1012
|
+
? `agent.platform=${adapter.agentPlatform || '未设置'}`
|
|
1013
|
+
: '安装 Miloco 后可一键写入 adapter 与令牌')),
|
|
1014
|
+
adapter.active
|
|
1015
|
+
? h('span', { className: 'statusCheck', title: '已启用', 'aria-label': '已启用' }, '✓')
|
|
1016
|
+
: h('span', { className: `statusEnd pill ${adapter.installed ? 'warn' : 'bad'}` },
|
|
1017
|
+
adapter.installed ? '未启用' : '未安装')),
|
|
1018
|
+
h('div', { className: 'statusRow' },
|
|
1019
|
+
h('span', { className: 'statusLabel' }, 'Miloco 版本'),
|
|
1020
|
+
h('div', { className: 'statusValue' },
|
|
1021
|
+
h('code', {
|
|
1022
|
+
title: currentVersion || '未知',
|
|
1023
|
+
}, currentVersion ? currentVersion.split('+')[0] : '—'),
|
|
1024
|
+
commitShort ? h('span', { className: 'muted' }, commitShort) : null,
|
|
1025
|
+
h('span', { className: `pill ${versionPill[0]}` }, versionPill[1]),
|
|
1026
|
+
h('div', { className: 'versionSub' },
|
|
1027
|
+
latestVersion
|
|
1028
|
+
? h('span', null, `最新正式版 ${latestVersion}`)
|
|
1029
|
+
: null,
|
|
1030
|
+
releaseUrl
|
|
1031
|
+
? h('a', {
|
|
1032
|
+
href: releaseUrl,
|
|
1033
|
+
target: '_blank',
|
|
1034
|
+
rel: 'noopener noreferrer',
|
|
1035
|
+
title: '在 GitHub 查看发布说明',
|
|
1036
|
+
}, '发布页')
|
|
1037
|
+
: null,
|
|
1038
|
+
isDevDeployment && devUpdateCommand
|
|
1039
|
+
? h('button', {
|
|
1040
|
+
type: 'button',
|
|
1041
|
+
className: 'versionLink',
|
|
1042
|
+
title: '查看源码部署的更新命令',
|
|
1043
|
+
onClick: () => setVersionCommandOpen(!versionCommandOpen),
|
|
1044
|
+
}, versionCommandOpen ? '收起命令' : '手动更新')
|
|
1045
|
+
: null,
|
|
1046
|
+
h('button', {
|
|
1047
|
+
type: 'button',
|
|
1048
|
+
className: 'versionLink',
|
|
1049
|
+
title: '重新读取 Miloco 版本与更新状态',
|
|
1050
|
+
disabled: versionBusy,
|
|
1051
|
+
onClick: () => loadVersion(true),
|
|
1052
|
+
}, versionBusy ? '检查中…' : '检查更新'),
|
|
1053
|
+
isDevDeployment && gitDirty
|
|
1054
|
+
? h('span', {
|
|
1055
|
+
className: 'versionDirty',
|
|
1056
|
+
title: '源码工作区有本地改动,更新前请先处理或提交',
|
|
1057
|
+
}, '工作区有改动')
|
|
1058
|
+
: null,
|
|
1059
|
+
versionError
|
|
1060
|
+
? h('span', { className: 'versionDirty', title: versionError }, '版本读取失败')
|
|
1061
|
+
: null,
|
|
1062
|
+
upgradePhase && !['idle', 'done', 'failed'].includes(upgradePhase)
|
|
1063
|
+
? h('span', null, `升级中:${upgradePhaseLabels[upgradePhase] || upgradePhase}`)
|
|
1064
|
+
: null)),
|
|
1065
|
+
hasUpdate && !isDevDeployment
|
|
1066
|
+
? h('div', { className: 'statusEnd' },
|
|
1067
|
+
h('button', {
|
|
1068
|
+
type: 'button',
|
|
1069
|
+
className: 'primary',
|
|
1070
|
+
disabled: upgradeBusy,
|
|
1071
|
+
onClick: () => setUpgradeConfirmOpen(!upgradeConfirmOpen),
|
|
1072
|
+
}, upgradeConfirmOpen ? '收起' : '更新'))
|
|
1073
|
+
: null),
|
|
1074
|
+
isDevDeployment && devUpdateCommand && versionCommandOpen
|
|
1075
|
+
? h('div', { className: 'versionManualPanel' },
|
|
1076
|
+
h('code', { className: 'versionCommand' }, devUpdateCommand),
|
|
1077
|
+
h('div', { className: 'row' },
|
|
1078
|
+
h('button', {
|
|
1079
|
+
type: 'button',
|
|
1080
|
+
onClick: () => {
|
|
1081
|
+
try { void navigator.clipboard?.writeText(devUpdateCommand) } catch { /* ignore */ }
|
|
1082
|
+
},
|
|
1083
|
+
}, '复制命令')))
|
|
1084
|
+
: null,
|
|
1085
|
+
upgradePhase === 'failed'
|
|
1086
|
+
? h('div', { className: 'muted versionNote' }, '上次升级失败,可查看 Miloco 的 upgrade.log 后重试。')
|
|
1087
|
+
: null,
|
|
1088
|
+
upgradeConfirmOpen
|
|
1089
|
+
? h('div', { className: 'danger' },
|
|
1090
|
+
h('h4', null, '更新 Miloco'),
|
|
1091
|
+
h('div', { className: 'muted' },
|
|
1092
|
+
'升级会短暂中断感知与自动化,完成后 Miloco 会自动重启。'),
|
|
1093
|
+
h('div', { className: 'row' },
|
|
1094
|
+
h('button', { type: 'button', onClick: () => setUpgradeConfirmOpen(false) }, '取消'),
|
|
1095
|
+
h('button', {
|
|
1096
|
+
type: 'button',
|
|
1097
|
+
className: 'primary',
|
|
1098
|
+
disabled: upgradeBusy,
|
|
1099
|
+
onClick: runUpgrade,
|
|
1100
|
+
}, upgradeBusy ? '启动中…' : '确认更新')))
|
|
1101
|
+
: null,
|
|
1102
|
+
upgradePolling
|
|
1103
|
+
? h('div', { className: 'muted versionNote' }, '正在等待升级完成…')
|
|
1104
|
+
: null,
|
|
1105
|
+
h('div', { className: 'statusRow' },
|
|
1106
|
+
h('span', { className: 'statusLabel' }, '配置位置'),
|
|
1107
|
+
h('div', { className: 'statusValue' },
|
|
1108
|
+
h('span', { className: 'muted' },
|
|
1109
|
+
`插件默认:${status.configLocation?.bundle ?? ''};`
|
|
1110
|
+
+ `profile 覆盖:${status.configLocation?.profile ?? ''}`))),
|
|
574
1111
|
h('div', { className: 'row' },
|
|
575
|
-
h('button', { type: 'button', disabled: busy, onClick: reload }, '刷新'),
|
|
576
|
-
h('button', { type: 'button', disabled: busy, onClick: () => setConnectOpen(!connectOpen) },
|
|
577
|
-
connectOpen ? '收起连接设置' : '修改连接'),
|
|
578
1112
|
h('button', {
|
|
579
1113
|
type: 'button',
|
|
1114
|
+
disabled: busy,
|
|
1115
|
+
onClick: () => {
|
|
1116
|
+
const opening = !connectOpen
|
|
1117
|
+
setConnectOpen(opening)
|
|
1118
|
+
if (opening) setUninstallOpen(false)
|
|
1119
|
+
},
|
|
1120
|
+
}, '修改连接'),
|
|
1121
|
+
h('button', {
|
|
1122
|
+
type: 'button',
|
|
1123
|
+
className: 'dangerBtn',
|
|
580
1124
|
disabled: uninstallBusy,
|
|
581
|
-
onClick: () =>
|
|
582
|
-
|
|
1125
|
+
onClick: () => {
|
|
1126
|
+
const opening = !uninstallOpen
|
|
1127
|
+
setUninstallOpen(opening)
|
|
1128
|
+
if (opening) setConnectOpen(false)
|
|
1129
|
+
},
|
|
1130
|
+
}, '卸载 Miloco')),
|
|
583
1131
|
reachable ? connectForm : null,
|
|
584
1132
|
uninstallResult ? h('div', { className: 'muted' }, uninstallResult) : null,
|
|
585
1133
|
uninstallOpen
|
|
@@ -608,7 +1156,11 @@ window.__ModuleLoader__.load({
|
|
|
608
1156
|
}),
|
|
609
1157
|
h('button', {
|
|
610
1158
|
type: 'button',
|
|
611
|
-
|
|
1159
|
+
onClick: () => { setUninstallOpen(false); setConfirmText('') },
|
|
1160
|
+
}, '取消'),
|
|
1161
|
+
h('button', {
|
|
1162
|
+
type: 'button',
|
|
1163
|
+
className: 'dangerBtn',
|
|
612
1164
|
disabled: uninstallBusy || confirmText !== 'UNINSTALL',
|
|
613
1165
|
onClick: uninstall,
|
|
614
1166
|
}, uninstallBusy ? '卸载中…' : '确认卸载')))
|
|
@@ -623,6 +1175,7 @@ window.__ModuleLoader__.load({
|
|
|
623
1175
|
|
|
624
1176
|
function apply(ctx) {
|
|
625
1177
|
ensureStyles()
|
|
1178
|
+
installPresetGuide(ctx)
|
|
626
1179
|
ctx.slots.inject('settings.plugins.tab', () => ctx.slots.register({
|
|
627
1180
|
name: 'settings.plugins.tab',
|
|
628
1181
|
id: TAB_ID,
|