@aiyiran/myclaw 1.1.88 → 1.1.89
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/assets/myclaw-artifacts.js +3 -4
- package/package.json +1 -1
- package/server/sync_workspace.py +62 -25
|
@@ -2355,9 +2355,8 @@
|
|
|
2355
2355
|
|
|
2356
2356
|
var deployBtn = document.createElement('button');
|
|
2357
2357
|
deployBtn.textContent = '⬇创建 -> 训练场';
|
|
2358
|
-
deployBtn.
|
|
2359
|
-
deployBtn.
|
|
2360
|
-
deployBtn.onmouseleave = function () { deployBtn.style.background = 'rgba(167,139,250,0.12)'; };
|
|
2358
|
+
deployBtn.disabled = true;
|
|
2359
|
+
deployBtn.style.cssText = _btnBase + 'background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);color:rgba(205,214,244,0.3);cursor:not-allowed;';
|
|
2361
2360
|
|
|
2362
2361
|
var copyLocalBtn = document.createElement('button');
|
|
2363
2362
|
copyLocalBtn.textContent = '⬇创建 -> 新伙伴';
|
|
@@ -2382,9 +2381,9 @@
|
|
|
2382
2381
|
var footerStatus = document.createElement('span');
|
|
2383
2382
|
footerStatus.style.cssText = 'font-size:11px;color:rgba(205,214,244,0.45);flex:1;';
|
|
2384
2383
|
|
|
2385
|
-
rightFooter.appendChild(deployBtn);
|
|
2386
2384
|
rightFooter.appendChild(copyLocalBtn);
|
|
2387
2385
|
rightFooter.appendChild(promptBtn);
|
|
2386
|
+
rightFooter.appendChild(deployBtn);
|
|
2388
2387
|
rightFooter.appendChild(footerStatus);
|
|
2389
2388
|
|
|
2390
2389
|
// currentTpl 在 setActive 时更新,按钮 onclick 通过闭包读取
|
package/package.json
CHANGED
package/server/sync_workspace.py
CHANGED
|
@@ -65,6 +65,9 @@ HISTORY_FILENAME = "history.json"
|
|
|
65
65
|
_rollback_suppressed: dict = {}
|
|
66
66
|
_ROLLBACK_SUPPRESS_TTL = 3.0 # 秒
|
|
67
67
|
|
|
68
|
+
# 时间窗口合并:同一文件在此时间窗口内的多次变更只保留最新快照(覆盖,不新增版本)
|
|
69
|
+
_HISTORY_MERGE_WINDOW = 20.0 # 秒
|
|
70
|
+
|
|
68
71
|
|
|
69
72
|
def _is_rollback_suppressed(abs_path: str) -> bool:
|
|
70
73
|
ts = _rollback_suppressed.get(abs_path)
|
|
@@ -94,36 +97,70 @@ def save_history_version(workspace_id: str, relative_path: str, file_path: str):
|
|
|
94
97
|
|
|
95
98
|
history_data = _load_history_json(history_json_path, workspace_id)
|
|
96
99
|
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
# 检查该文件最后一次快照是否在合并窗口内
|
|
101
|
+
now_ts = time.time()
|
|
102
|
+
records = history_data.get("records", [])
|
|
103
|
+
last_record = next(
|
|
104
|
+
(r for r in reversed(records) if r.get("path") == relative_path),
|
|
105
|
+
None
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if last_record:
|
|
109
|
+
try:
|
|
110
|
+
last_ts = datetime.fromisoformat(last_record["snapshot_at"]).timestamp()
|
|
111
|
+
except Exception:
|
|
112
|
+
last_ts = 0
|
|
113
|
+
within_window = (now_ts - last_ts) < _HISTORY_MERGE_WINDOW
|
|
114
|
+
else:
|
|
115
|
+
within_window = False
|
|
99
116
|
|
|
100
|
-
# 复制当前文件内容到 __history__/vN/<relative_path>
|
|
101
117
|
rel_parts = relative_path.replace("\\", "/").split("/")
|
|
102
|
-
dest_path = os.path.join(history_base, ver_dir_name, *rel_parts)
|
|
103
|
-
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
|
104
|
-
try:
|
|
105
|
-
with open(file_path, "rb") as src, open(dest_path, "wb") as dst:
|
|
106
|
-
dst.write(src.read())
|
|
107
|
-
except Exception as e:
|
|
108
|
-
print(f"[history] 快照写入失败: {e}")
|
|
109
|
-
return
|
|
110
118
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
if within_window:
|
|
120
|
+
# 覆盖最后一次快照,不新增版本
|
|
121
|
+
ver_dir_name = last_record["version_dir"]
|
|
122
|
+
dest_path = os.path.join(history_base, ver_dir_name, *rel_parts)
|
|
123
|
+
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
|
124
|
+
try:
|
|
125
|
+
with open(file_path, "rb") as src, open(dest_path, "wb") as dst:
|
|
126
|
+
dst.write(src.read())
|
|
127
|
+
except Exception as e:
|
|
128
|
+
print(f"[history] 快照覆盖失败: {e}")
|
|
129
|
+
return
|
|
130
|
+
# 更新该 record 的时间戳
|
|
131
|
+
last_record["snapshot_at"] = now_iso()
|
|
132
|
+
with open(history_json_path, "w", encoding="utf-8") as f:
|
|
133
|
+
json.dump(history_data, f, ensure_ascii=False, indent=2)
|
|
134
|
+
print(f"[history] 合并覆盖快照 {ver_dir_name}: {relative_path}")
|
|
135
|
+
else:
|
|
136
|
+
# 超出时间窗口,新增版本
|
|
137
|
+
next_ver = history_data.get("next_version", 1)
|
|
138
|
+
ver_dir_name = f"v{next_ver}"
|
|
122
139
|
|
|
123
|
-
|
|
124
|
-
|
|
140
|
+
dest_path = os.path.join(history_base, ver_dir_name, *rel_parts)
|
|
141
|
+
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
|
142
|
+
try:
|
|
143
|
+
with open(file_path, "rb") as src, open(dest_path, "wb") as dst:
|
|
144
|
+
dst.write(src.read())
|
|
145
|
+
except Exception as e:
|
|
146
|
+
print(f"[history] 快照写入失败: {e}")
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
history_data["next_version"] = next_ver + 1
|
|
150
|
+
history_data["records"].append({
|
|
151
|
+
"version": next_ver,
|
|
152
|
+
"version_dir": ver_dir_name,
|
|
153
|
+
"path": relative_path,
|
|
154
|
+
"snapshot_at": now_iso()
|
|
155
|
+
})
|
|
156
|
+
if "current_versions" not in history_data:
|
|
157
|
+
history_data["current_versions"] = {}
|
|
158
|
+
history_data["current_versions"][relative_path] = next_ver
|
|
159
|
+
|
|
160
|
+
with open(history_json_path, "w", encoding="utf-8") as f:
|
|
161
|
+
json.dump(history_data, f, ensure_ascii=False, indent=2)
|
|
125
162
|
|
|
126
|
-
|
|
163
|
+
print(f"[history] 已保存快照 {ver_dir_name}: {relative_path}")
|
|
127
164
|
|
|
128
165
|
|
|
129
166
|
def rollback_to_version(workspace_id: str, relative_path: str, version_dir: str):
|