mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-10-22 07:09:11 +02:00
feat: update this installation's own source in one action
An operator running a fork has no honest way to upgrade: upgrade.sh downloads the Release tarball of one hard-coded third-party repository and rsyncs it over the tree, so "upgrade" means "overwrite yourself with somebody else's code", and the safe alternative is to pull by hand and remember the build command. internal/update drives the remote the directory already tracks: fetch, fast-forward, go build into .update-staging, atomic rename of the binary (the previous one kept as cyberstrike-ai.prev), and a .update-state.json so a rollback has a target that is not guessed at. Refusals are the point - local source edits and a diverged branch stop with the file names and commit counts in the error rather than being reset to match, and operator-owned content (roles/skills/tools/agents/data/config.yaml) is put aside before the merge and restored over it, with what was kept listed in keptContent instead of quietly dropped. Consumers: GET/POST /api/system/update* behind update:read / update:apply (mutations are process-global, so they need a global-scope session); a console page at #system-update with an apply button that names why it is blocked, a job polled from the server and a rollback control; the same three actions as -check-update / -update / -update-rollback on the binary; OpenAPI entries for all five endpoints. Permission gating no longer re-enables a control a page disabled for a reason of its own (data-state-disabled), which every state-disabled, permission-gated button in the console was subject to. upgrade.sh becomes a thin shell over the same implementation and only falls back to the tarball path for installs that are not git work trees. Docs and the zh/EN/RU dictionaries are updated. The package runs 13 tests against real temporary git repositories; the HTTP layer, RBAC and the console page each carry their own contract tests.
This commit is contained in:
1 parent
d2084a9481
commit
cb8bc6b824
34 files changed
+5225
-112
No files matched your search
@@ -46715,3 +46715,293 @@ html[data-theme="dark"] { --tool-blocked-color: #f4bc62; }
|
||||
white-space: nowrap;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
一键更新控制台(system-update):这里说的每一句状态都要能被眼睛分区——
|
||||
本机事实、远端比较、能不能点、正在跑的进度、以及跑完的结论。
|
||||
ok/warn/danger 三色是给"说谎"留的对照组:查不了必须是 warn/danger,不能长得像 ok。
|
||||
--------------------------------------------------------------------------- */
|
||||
.update-console {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.update-intro {
|
||||
color: var(--text-secondary);
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
|
||||
.update-section-title {
|
||||
font-size: 15px;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
.update-sub-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
margin: 14px 0 6px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.update-facts {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 20px;
|
||||
padding: 12px 16px;
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.update-fact {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.update-fact code {
|
||||
color: var(--text-primary);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
.update-marks {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.update-chip {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.update-chip-ok {
|
||||
background: rgba(37, 168, 112, 0.14);
|
||||
border-color: rgba(37, 168, 112, 0.4);
|
||||
color: #1f8a5b;
|
||||
}
|
||||
|
||||
.update-chip-warn {
|
||||
background: rgba(217, 130, 43, 0.14);
|
||||
border-color: rgba(217, 130, 43, 0.4);
|
||||
color: #b06a17;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.update-chip-danger {
|
||||
background: rgba(192, 57, 43, 0.14);
|
||||
border-color: rgba(192, 57, 43, 0.4);
|
||||
color: var(--danger-color, #c0392b);
|
||||
}
|
||||
|
||||
.update-chip-running {
|
||||
background: rgba(58, 110, 197, 0.14);
|
||||
border-color: rgba(58, 110, 197, 0.4);
|
||||
color: #2d5bb0;
|
||||
}
|
||||
|
||||
.update-ok {
|
||||
color: #1f8a5b;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.update-behind {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.update-hint {
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
.update-warn {
|
||||
background: rgba(217, 130, 43, 0.12);
|
||||
border: 1px solid rgba(217, 130, 43, 0.35);
|
||||
border-radius: 8px;
|
||||
padding: 8px 12px;
|
||||
font-size: 13px;
|
||||
color: #b06a17;
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
.update-error {
|
||||
background: rgba(192, 57, 43, 0.1);
|
||||
border: 1px solid rgba(192, 57, 43, 0.32);
|
||||
border-radius: 8px;
|
||||
padding: 8px 12px;
|
||||
color: var(--danger-color, #c0392b);
|
||||
font-size: 13px;
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
/* 「检查失败」那一行是这一页最不该被看漏的字,所以它比别的错误横幅再重一档。 */
|
||||
.update-check-failed {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 这三个按钮的名字既是样式钩子也是测试钩子:禁用态要看得出是禁用,而不是"点了没反应"。 */
|
||||
.update-apply-btn:disabled,
|
||||
.update-check-btn:disabled,
|
||||
.update-rollback-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
.update-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.update-restart-choice {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.update-restart-choice input {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.update-restart-preview {
|
||||
border-left: 3px solid var(--border-color);
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.update-blocker-list {
|
||||
margin: 6px 0 0;
|
||||
padding-left: 18px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.update-commit-list,
|
||||
.update-kept-list,
|
||||
.update-item-list {
|
||||
margin: 0;
|
||||
padding-left: 18px;
|
||||
font-size: 13px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.update-commit-list code,
|
||||
.update-kept-list code,
|
||||
.update-item-list code {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.update-commit-subject {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.update-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.update-table th,
|
||||
.update-table td {
|
||||
text-align: left;
|
||||
padding: 6px 8px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.update-row-blocking td {
|
||||
background: rgba(192, 57, 43, 0.06);
|
||||
}
|
||||
|
||||
.update-row-kept td {
|
||||
background: rgba(37, 168, 112, 0.06);
|
||||
}
|
||||
|
||||
.update-job-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.update-job-time {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.update-step-list {
|
||||
margin: 10px 0 0;
|
||||
padding: 10px 12px;
|
||||
list-style: decimal inside;
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 10px;
|
||||
font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
||||
font-size: 12.5px;
|
||||
}
|
||||
|
||||
.update-step {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
padding: 3px 0;
|
||||
border-bottom: 1px dashed var(--border-color);
|
||||
}
|
||||
|
||||
.update-step:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.update-step-phase {
|
||||
color: #2d5bb0;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.update-step-message {
|
||||
color: var(--text-primary);
|
||||
flex: 1 1 320px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.update-step-at {
|
||||
color: var(--text-secondary);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.update-result {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.update-failure-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.update-failure-message {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
Reference in new issue
Block a user