zcode-pro 0.16.0__py3-none-any.whl
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.
- zcode_pro/__init__.py +3 -0
- zcode_pro/__main__.py +4 -0
- zcode_pro/app/cli.mjs +9 -0
- zcode_pro/app/dist/inject.js +5821 -0
- zcode_pro/app/src/host/cdp.mjs +126 -0
- zcode_pro/app/src/host/helper.mjs +2134 -0
- zcode_pro/app/src/host/main.mjs +262 -0
- zcode_pro/app/src/host/paths.mjs +73 -0
- zcode_pro/app/src/host/pickFolder.mjs +152 -0
- zcode_pro/app/src/host/settings.mjs +61 -0
- zcode_pro/app/src/host/taskIndex.mjs +255 -0
- zcode_pro/app/src/host/taskOrder.mjs +117 -0
- zcode_pro/app/src/host/webdav.mjs +105 -0
- zcode_pro/app/src/inject/core.js +751 -0
- zcode_pro/app/src/inject/features/alias.js +211 -0
- zcode_pro/app/src/inject/features/copy-path.js +78 -0
- zcode_pro/app/src/inject/features/file-menu.js +147 -0
- zcode_pro/app/src/inject/features/idle-reclaim.js +179 -0
- zcode_pro/app/src/inject/features/image-menu.js +215 -0
- zcode_pro/app/src/inject/features/open-folder.js +43 -0
- zcode_pro/app/src/inject/features/pinned-collapse.js +132 -0
- zcode_pro/app/src/inject/features/pinned-expand.js +142 -0
- zcode_pro/app/src/inject/features/plugin-updates.js +28 -0
- zcode_pro/app/src/inject/features/project-menu.js +58 -0
- zcode_pro/app/src/inject/features/relocate-dialog.js +156 -0
- zcode_pro/app/src/inject/features/session-switch.js +244 -0
- zcode_pro/app/src/inject/features/settings-dialog.js +1503 -0
- zcode_pro/app/src/inject/features/settings-entry.js +21 -0
- zcode_pro/app/src/inject/features/styles.js +311 -0
- zcode_pro/app/src/inject/features/task-order.js +78 -0
- zcode_pro/app/src/inject/features/title-lock.js +242 -0
- zcode_pro/app/src/inject/features/toolbar-icons.js +243 -0
- zcode_pro/app/src/inject/features/ws-running.js +472 -0
- zcode_pro/app/src/inject/index.js +55 -0
- zcode_pro/app/src/inject/ui.js +734 -0
- zcode_pro/cli.py +71 -0
- zcode_pro-0.16.0.dist-info/METADATA +45 -0
- zcode_pro-0.16.0.dist-info/RECORD +41 -0
- zcode_pro-0.16.0.dist-info/WHEEL +4 -0
- zcode_pro-0.16.0.dist-info/entry_points.txt +2 -0
- zcode_pro-0.16.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// 极简 CDP(Chrome DevTools Protocol)客户端。
|
|
2
|
+
// 仅依赖 Node >= 22 自带的全局 WebSocket 与 fetch,无第三方依赖。
|
|
3
|
+
// 连接浏览器级 ws 端点,通过 Target.setAutoAttach(flatten) 管理页面会话。
|
|
4
|
+
|
|
5
|
+
export class CdpConnection {
|
|
6
|
+
constructor(wsUrl) {
|
|
7
|
+
this.wsUrl = wsUrl;
|
|
8
|
+
this.ws = null;
|
|
9
|
+
this.nextId = 1;
|
|
10
|
+
this.pending = new Map(); // id -> {resolve, reject}
|
|
11
|
+
this.sessionListeners = new Map(); // sessionId -> Set<fn(message)>
|
|
12
|
+
this.attachedTargets = new Map(); // sessionId -> targetId
|
|
13
|
+
this.closed = false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async connect() {
|
|
17
|
+
if (typeof WebSocket !== 'function') {
|
|
18
|
+
throw new Error('当前运行时缺少全局 WebSocket(需要 Node >= 22 或 ZCode 内置 Node)');
|
|
19
|
+
}
|
|
20
|
+
await new Promise((resolve, reject) => {
|
|
21
|
+
const ws = new WebSocket(this.wsUrl);
|
|
22
|
+
ws.addEventListener('open', () => resolve(), { once: true });
|
|
23
|
+
ws.addEventListener('error', () => reject(new Error('无法连接 CDP: ' + this.wsUrl)), { once: true });
|
|
24
|
+
this.ws = ws;
|
|
25
|
+
});
|
|
26
|
+
this.ws.addEventListener('message', (ev) => this.#onMessage(String(ev.data)));
|
|
27
|
+
this.ws.addEventListener('close', () => {
|
|
28
|
+
this.closed = true;
|
|
29
|
+
for (const p of this.pending.values()) { clearTimeout(p.timer); p.reject(new Error('CDP 连接已关闭')); }
|
|
30
|
+
this.pending.clear();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#onMessage(text) {
|
|
35
|
+
let msg;
|
|
36
|
+
try { msg = JSON.parse(text); } catch { return; }
|
|
37
|
+
if (msg.id !== undefined) {
|
|
38
|
+
const p = this.pending.get(msg.id);
|
|
39
|
+
if (p) {
|
|
40
|
+
this.pending.delete(msg.id);
|
|
41
|
+
clearTimeout(p.timer);
|
|
42
|
+
if (msg.error) p.reject(new Error(msg.error.message || JSON.stringify(msg.error)));
|
|
43
|
+
else p.resolve(msg.result);
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// 事件
|
|
48
|
+
if (msg.method === 'Target.attachedToTarget') {
|
|
49
|
+
const { sessionId, targetInfo } = msg.params;
|
|
50
|
+
this.attachedTargets.set(sessionId, targetInfo.targetId);
|
|
51
|
+
// attach/detach 属于浏览器级事件,广播给 '' 级监听器
|
|
52
|
+
this.#emit('', { type: 'attached', sessionId, targetInfo });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (msg.method === 'Target.detachedFromTarget') {
|
|
56
|
+
const { sessionId } = msg.params;
|
|
57
|
+
this.attachedTargets.delete(sessionId);
|
|
58
|
+
this.#emit(sessionId, { type: 'detached', sessionId });
|
|
59
|
+
this.#emit('', { type: 'detached', sessionId });
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
this.#emit(msg.sessionId || '', { type: 'event', method: msg.method, params: msg.params, sessionId: msg.sessionId });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#emit(sessionId, message) {
|
|
66
|
+
const set = this.sessionListeners.get(sessionId);
|
|
67
|
+
if (set) for (const fn of set) { try { fn(message); } catch { /* listener error */ } }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
onSession(sessionId, fn) {
|
|
71
|
+
if (!this.sessionListeners.has(sessionId)) this.sessionListeners.set(sessionId, new Set());
|
|
72
|
+
this.sessionListeners.get(sessionId).add(fn);
|
|
73
|
+
return () => this.sessionListeners.get(sessionId)?.delete(fn);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
send(method, params = {}, sessionId, timeoutMs = 30000) {
|
|
77
|
+
if (this.closed) return Promise.reject(new Error('CDP 连接已关闭'));
|
|
78
|
+
const id = this.nextId++;
|
|
79
|
+
const payload = { id, method, params };
|
|
80
|
+
if (sessionId) payload.sessionId = sessionId;
|
|
81
|
+
return new Promise((resolve, reject) => {
|
|
82
|
+
// 超时兜底:连接假死(未触发 close)时调用方不至于永久挂起
|
|
83
|
+
const timer = setTimeout(() => {
|
|
84
|
+
this.pending.delete(id);
|
|
85
|
+
reject(new Error(`CDP 请求超时(${timeoutMs}ms): ${method}`));
|
|
86
|
+
}, timeoutMs);
|
|
87
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
88
|
+
try { this.ws.send(JSON.stringify(payload)); }
|
|
89
|
+
catch (err) { clearTimeout(timer); this.pending.delete(id); reject(err); }
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
close() {
|
|
94
|
+
this.closed = true;
|
|
95
|
+
for (const p of this.pending.values()) clearTimeout(p.timer);
|
|
96
|
+
try { this.ws?.close(); } catch { /* ignore */ }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 拉取 http://127.0.0.1:port/json 列表(type=page 的目标)
|
|
101
|
+
export async function listPageTargets(port, timeoutMs = 2000) {
|
|
102
|
+
const ac = new AbortController();
|
|
103
|
+
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
104
|
+
try {
|
|
105
|
+
const res = await fetch(`http://127.0.0.1:${port}/json`, { signal: ac.signal });
|
|
106
|
+
if (!res.ok) throw new Error('/json 状态码 ' + res.status);
|
|
107
|
+
const list = await res.json();
|
|
108
|
+
return Array.isArray(list) ? list.filter((t) => t.type === 'page') : [];
|
|
109
|
+
} finally {
|
|
110
|
+
clearTimeout(timer);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function fetchBrowserWsUrl(port, timeoutMs = 2000) {
|
|
115
|
+
const ac = new AbortController();
|
|
116
|
+
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
117
|
+
try {
|
|
118
|
+
const res = await fetch(`http://127.0.0.1:${port}/json/version`, { signal: ac.signal });
|
|
119
|
+
if (!res.ok) throw new Error('/json/version 状态码 ' + res.status);
|
|
120
|
+
const info = await res.json();
|
|
121
|
+
if (!info.webSocketDebuggerUrl) throw new Error('缺少 webSocketDebuggerUrl');
|
|
122
|
+
return info.webSocketDebuggerUrl;
|
|
123
|
+
} finally {
|
|
124
|
+
clearTimeout(timer);
|
|
125
|
+
}
|
|
126
|
+
}
|