zen-gitsync 2.18.27 → 2.19.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/package.json +6 -2
- package/src/cli/ai/tools.js +71 -8
- package/src/cli/ai/webSearch.js +108 -21
- package/src/cli/peer.js +272 -0
- package/src/config.js +14 -4
- package/src/gitCommit.js +34 -1
- package/src/paths.js +6 -0
- package/src/peer/client.js +165 -0
- package/src/peer/localAsk.js +130 -0
- package/src/peer/server.js +183 -0
- package/src/peer/store.js +292 -0
- package/src/ui/public/assets/{AgentView-DunbYHMn.js → AgentView-BtnAYtte.js} +1 -1
- package/src/ui/public/assets/AppVersionBadge-BcXo-yuu.js +2 -0
- package/src/ui/public/assets/{BranchSelector-BKA3fL9E.js → BranchSelector-5t-T3FB6.js} +1 -1
- package/src/ui/public/assets/{CommitForm-CVPGpci8.js → CommitForm-BMfp3Eqh.js} +1 -1
- package/src/ui/public/assets/{CommonDialog-ZwJlTF5A.js → CommonDialog-BiF2fMgr.js} +1 -1
- package/src/ui/public/assets/{EditorView-DDzACKq1.js → EditorView-CtmZ7RZp.js} +1 -1
- package/src/ui/public/assets/{FlowExecutionViewer-BJOiCa6S.js → FlowExecutionViewer-D2lchSo7.js} +1 -1
- package/src/ui/public/assets/{FlowOrchestrationWorkspace-BDnAdRr9.js → FlowOrchestrationWorkspace-BY2zLwWB.js} +1 -1
- package/src/ui/public/assets/{LogList-DbGz7_dy.js → LogList-DTfAGnlt.js} +1 -1
- package/src/ui/public/assets/{MindmapView-mbTFeVj8.js → MindmapView-DArneR4u.js} +1 -1
- package/src/ui/public/assets/{MonitorView-O7I40RiB.js → MonitorView-BQvMyjEj.js} +1 -1
- package/src/ui/public/assets/{ProjectManagerView-D1NVbIOW.js → ProjectManagerView-BRUyrHfg.js} +1 -1
- package/src/ui/public/assets/{ProjectStartupButton-C21zy3Bg.js → ProjectStartupButton-D0f8OAb-.js} +1 -1
- package/src/ui/public/assets/{RemoteManagerDialog-CpSrItn2.js → RemoteManagerDialog-DXDXCfyD.js} +1 -1
- package/src/ui/public/assets/{RemoteRepoCard-CXDXPJK2.js → RemoteRepoCard-BRnjC6vm.js} +1 -1
- package/src/ui/public/assets/{SourceMapView-C9qTeaOC.js → SourceMapView-B6xgG5mi.js} +1 -1
- package/src/ui/public/assets/{SvgIcon-BmXhx-vA.js → SvgIcon-GLj_3KcS.js} +1 -1
- package/src/ui/public/assets/{UserInputNode-i7ER8ZCn.js → UserInputNode-C6Z6Kys_.js} +1 -1
- package/src/ui/public/assets/{WorkbenchView-pZz9ykZT.js → WorkbenchView-B2EUXleS.js} +1 -1
- package/src/ui/public/assets/_plugin-vue_export-helper-DaO48JOV.js +19 -0
- package/src/ui/public/assets/configStore-B_2ifbl_.js +1 -0
- package/src/ui/public/assets/{index-DW-2cjNW.js → index-DUzH5036.js} +63 -60
- package/src/ui/public/assets/index-zdGFM_7W.css +1 -0
- package/src/ui/public/assets/{relativeTime-DsG6UERK.js → relativeTime-DzYQUHQo.js} +1 -1
- package/src/ui/public/assets/{taskExecutionExport-C3dV5cuE.js → taskExecutionExport-C3vFixdJ.js} +1 -1
- package/src/ui/public/assets/{useWorkbenchProjects-B6k5mmVC.js → useWorkbenchProjects-0fWZsDRX.js} +1 -1
- package/src/ui/public/index.html +6 -6
- package/src/ui/server/index.js +130 -10
- package/src/ui/server/routes/config.js +1317 -1316
- package/src/ui/server/routes/peerSettings.js +164 -0
- package/src/ui/server/routes/workbench/agentChat.js +811 -811
- package/src/ui/server/routes/workbench/agentRoutes.js +9 -0
- package/src/ui/server/utils/ensureHub.js +101 -0
- package/src/ui/server/utils/instanceRegistry.js +8 -3
- package/src/ui/server/utils/peerRuntime.js +77 -0
- package/src/ui/server/utils/startServerOnAvailablePort.js +6 -0
- package/src/ui/public/assets/AppVersionBadge-9B4b0hbP.js +0 -2
- package/src/ui/public/assets/_plugin-vue_export-helper-D6ACRIgd.js +0 -19
- package/src/ui/public/assets/configStore-BQoByr2v.js +0 -1
- package/src/ui/public/assets/index-B9mqQJGC.css +0 -1
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// Copyright 2026 xz333221
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
//
|
|
15
|
+
// 「跨机器」设置面板的后端:本机身份 + 对端清单的读写与探活。
|
|
16
|
+
//
|
|
17
|
+
// 挂在**主服务**上(127.0.0.1 + Origin 守卫),不是 peer 端口 —— peer 端口是给
|
|
18
|
+
// 别的机器用的(要令牌),这组接口是给本机界面用的(不需要令牌,也拿不到对方机器的
|
|
19
|
+
// 东西)。两套路由同名会混淆,所以这里统一用 /api/peer-settings 前缀。
|
|
20
|
+
//
|
|
21
|
+
// 唯一会返回令牌明文的地方就是 GET /api/peer-settings:配对时用户必须能复制它到
|
|
22
|
+
// 另一台机器。它只在 127.0.0.1 上、只给本机界面看。
|
|
23
|
+
|
|
24
|
+
import { asyncRoute, HttpError } from '../utils/asyncRoute.js';
|
|
25
|
+
import logger from '../utils/logger.js';
|
|
26
|
+
import {
|
|
27
|
+
ensureSelf,
|
|
28
|
+
readState,
|
|
29
|
+
updateSelf,
|
|
30
|
+
upsertPeer,
|
|
31
|
+
removePeer,
|
|
32
|
+
rotateSelfToken,
|
|
33
|
+
PEER_DEFAULT_PORT
|
|
34
|
+
} from '../../../peer/store.js';
|
|
35
|
+
import { fetchPeerInfo } from '../../../peer/client.js';
|
|
36
|
+
import { applyPeerRuntime, isPeerRunning } from '../utils/peerRuntime.js';
|
|
37
|
+
|
|
38
|
+
const PROBE_TIMEOUT_MS = 2500;
|
|
39
|
+
|
|
40
|
+
/** 对端在线探测:失败不抛,只标记 offline(面板要能一眼看出"连不上")。 */
|
|
41
|
+
async function probe(peer) {
|
|
42
|
+
try {
|
|
43
|
+
const info = await fetchPeerInfo(peer, { timeoutMs: PROBE_TIMEOUT_MS });
|
|
44
|
+
return { online: true, remoteName: info.name || '', version: info.version || '', remoteId: info.id || '' };
|
|
45
|
+
} catch (err) {
|
|
46
|
+
return { online: false, error: String(err?.message || err) };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function registerPeerSettingsRoutes({ app }) {
|
|
51
|
+
/**
|
|
52
|
+
* GET /api/peer-settings
|
|
53
|
+
* 本机身份 + 对端清单(附带一次并发探活)。
|
|
54
|
+
*/
|
|
55
|
+
app.get('/api/peer-settings', asyncRoute(async (req, res) => {
|
|
56
|
+
const state = await ensureSelf();
|
|
57
|
+
const peers = state.peers || [];
|
|
58
|
+
const probed = await Promise.all(peers.map(async (p) => ({ ...p, ...(await probe(p)) })));
|
|
59
|
+
|
|
60
|
+
// 昵称**以对方自报为准**(对方在它自己的设置里改了就跟着变)。
|
|
61
|
+
// 变了顺手落盘:否则对方改名后、下次它离线时又显示回旧名字。
|
|
62
|
+
const rows = await Promise.all(probed.map(async (p) => {
|
|
63
|
+
const remoteName = p.online ? String(p.remoteName || '').trim() : '';
|
|
64
|
+
if (remoteName && remoteName !== p.name) {
|
|
65
|
+
await upsertPeer({ id: p.id, name: remoteName, url: p.url, token: p.token }).catch(() => {});
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
id: p.id,
|
|
69
|
+
name: remoteName || p.name,
|
|
70
|
+
url: p.url,
|
|
71
|
+
online: p.online,
|
|
72
|
+
version: p.version || '',
|
|
73
|
+
error: p.error || ''
|
|
74
|
+
};
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
res.json({ success: true, running: isPeerRunning(), self: state.self, peers: rows });
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
/** POST /api/peer-settings/self —— 改昵称 / 头像 / 端口 / 开关。 */
|
|
81
|
+
app.post('/api/peer-settings/self', asyncRoute(async (req, res) => {
|
|
82
|
+
const body = req.body || {};
|
|
83
|
+
if (body.port !== undefined) {
|
|
84
|
+
const port = Number(body.port);
|
|
85
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
86
|
+
throw new HttpError(400, '端口必须是 1-65535 的整数');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const state = await updateSelf({
|
|
90
|
+
name: typeof body.name === 'string' ? body.name : undefined,
|
|
91
|
+
avatar: typeof body.avatar === 'string' ? body.avatar.slice(0, 200) : undefined,
|
|
92
|
+
port: body.port !== undefined ? Number(body.port) : undefined,
|
|
93
|
+
enabled: typeof body.enabled === 'boolean' ? body.enabled : undefined
|
|
94
|
+
});
|
|
95
|
+
// 立刻生效:开/关/换端口都当场应用,设置面板里的开关不该要求重启进程。
|
|
96
|
+
try {
|
|
97
|
+
await applyPeerRuntime(state.self);
|
|
98
|
+
} catch (err) {
|
|
99
|
+
// 起不来就别假装开着:回滚开关,把真实原因交给用户
|
|
100
|
+
if (state.self.enabled) await updateSelf({ enabled: false });
|
|
101
|
+
throw new HttpError(400, `peer 端口 ${state.self.port} 起不来:${err?.message || err}`);
|
|
102
|
+
}
|
|
103
|
+
res.json({ success: true, running: isPeerRunning(), self: state.self });
|
|
104
|
+
}));
|
|
105
|
+
|
|
106
|
+
/** POST /api/peer-settings/token —— 重新生成令牌(旧的立即失效)。 */
|
|
107
|
+
app.post('/api/peer-settings/token', asyncRoute(async (req, res) => {
|
|
108
|
+
const state = await rotateSelfToken();
|
|
109
|
+
logger.info('[peer-settings] 令牌已重置');
|
|
110
|
+
// 必须重启监听:运行中的实例持有的是**旧令牌**的闭包,不换就不生效
|
|
111
|
+
try {
|
|
112
|
+
await applyPeerRuntime(state.self);
|
|
113
|
+
} catch (err) {
|
|
114
|
+
logger.warn(`[peer-settings] 令牌已换但监听未重启: ${err?.message || err}`);
|
|
115
|
+
}
|
|
116
|
+
res.json({ success: true, running: isPeerRunning(), self: state.self });
|
|
117
|
+
}));
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* POST /api/peer-settings/peers —— 添加(或覆盖)一台对端。
|
|
121
|
+
* 先真连一次:连不上/令牌错当场报错,别让用户以为加好了。
|
|
122
|
+
*/
|
|
123
|
+
app.post('/api/peer-settings/peers', asyncRoute(async (req, res) => {
|
|
124
|
+
const rawUrl = String(req.body?.url || '').trim();
|
|
125
|
+
const token = String(req.body?.token || '').trim();
|
|
126
|
+
if (!rawUrl) throw new HttpError(400, '请填对端地址');
|
|
127
|
+
if (!token) throw new HttpError(400, '请填对端令牌');
|
|
128
|
+
|
|
129
|
+
const url = (rawUrl.startsWith('http') ? rawUrl : `http://${rawUrl}`).replace(/\/+$/, '');
|
|
130
|
+
let info;
|
|
131
|
+
try {
|
|
132
|
+
info = await fetchPeerInfo({ url, token }, { timeoutMs: 5000 });
|
|
133
|
+
} catch (err) {
|
|
134
|
+
throw new HttpError(400, `连不上对端:${err?.message || err}`);
|
|
135
|
+
}
|
|
136
|
+
// 昵称由**对方**决定(它在自己的设置里写什么就叫什么),本机只在对方没设时退回地址。
|
|
137
|
+
// 这里允许显式传 name,是给将来"本地备注"留的口子;界面不再问用户填。
|
|
138
|
+
const name = String(req.body?.name || '').trim() || String(info.name || '').trim() || url;
|
|
139
|
+
const state = await upsertPeer({ id: info.id || '', name, url, token });
|
|
140
|
+
res.json({ success: true, peer: { id: info.id || '', name, url, online: true, version: info.version || '' }, count: state.peers.length });
|
|
141
|
+
}));
|
|
142
|
+
|
|
143
|
+
/** DELETE /api/peer-settings/peers/:ref —— 移除(ref 可以是 id 或昵称)。 */
|
|
144
|
+
app.delete('/api/peer-settings/peers/:ref', asyncRoute(async (req, res) => {
|
|
145
|
+
const { removed, peer } = await removePeer(decodeURIComponent(req.params.ref));
|
|
146
|
+
if (!removed) throw new HttpError(404, '没有这台对端');
|
|
147
|
+
res.json({ success: true, peer: { id: peer.id, name: peer.name } });
|
|
148
|
+
}));
|
|
149
|
+
|
|
150
|
+
/** POST /api/peer-settings/peers/:ref/test —— 单独重测一台。 */
|
|
151
|
+
app.post('/api/peer-settings/peers/:ref/test', asyncRoute(async (req, res) => {
|
|
152
|
+
const ref = decodeURIComponent(req.params.ref).toLowerCase();
|
|
153
|
+
const state = await readState();
|
|
154
|
+
const peer = (state.peers || []).find((p) => p.id.toLowerCase() === ref || p.name.toLowerCase() === ref || p.url.toLowerCase() === ref);
|
|
155
|
+
if (!peer) throw new HttpError(404, '没有这台对端');
|
|
156
|
+
const result = await probe(peer);
|
|
157
|
+
res.json({ success: true, ...result });
|
|
158
|
+
}));
|
|
159
|
+
|
|
160
|
+
/** GET /api/peer-settings/defaults —— 面板第一次打开时给默认值(端口号)。 */
|
|
161
|
+
app.get('/api/peer-settings/defaults', (req, res) => {
|
|
162
|
+
res.json({ success: true, port: PEER_DEFAULT_PORT });
|
|
163
|
+
});
|
|
164
|
+
}
|