@web-auto/camo 0.2.0 → 0.2.2
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/LICENSE +21 -21
- package/README.md +586 -586
- package/bin/browser-service.mjs +11 -11
- package/bin/camo.mjs +22 -22
- package/package.json +48 -48
- package/scripts/build.mjs +19 -19
- package/scripts/bump-version.mjs +34 -34
- package/scripts/check-file-size.mjs +80 -80
- package/scripts/file-size-policy.json +12 -2
- package/scripts/install.mjs +76 -76
- package/scripts/release.sh +54 -54
- package/src/autoscript/action-providers/index.mjs +6 -6
- package/src/autoscript/impact-engine.mjs +78 -78
- package/src/autoscript/runtime.mjs +1017 -1017
- package/src/autoscript/schema.mjs +376 -376
- package/src/cli.mjs +405 -405
- package/src/commands/attach.mjs +141 -141
- package/src/commands/autoscript.mjs +1011 -1011
- package/src/commands/browser.mjs +1255 -1255
- package/src/commands/container.mjs +401 -401
- package/src/commands/cookies.mjs +69 -69
- package/src/commands/create.mjs +98 -98
- package/src/commands/devtools.mjs +349 -349
- package/src/commands/events.mjs +152 -152
- package/src/commands/highlight-mode.mjs +24 -24
- package/src/commands/init.mjs +68 -68
- package/src/commands/lifecycle.mjs +275 -275
- package/src/commands/mouse.mjs +45 -45
- package/src/commands/profile.mjs +46 -46
- package/src/commands/record.mjs +115 -115
- package/src/commands/system.mjs +14 -14
- package/src/commands/window.mjs +123 -123
- package/src/container/change-notifier.mjs +362 -362
- package/src/container/element-filter.mjs +143 -143
- package/src/container/index.mjs +3 -3
- package/src/container/runtime-core/checkpoint.mjs +209 -209
- package/src/container/runtime-core/index.mjs +21 -21
- package/src/container/runtime-core/operations/index.mjs +774 -774
- package/src/container/runtime-core/operations/selector-scripts.mjs +277 -277
- package/src/container/runtime-core/operations/tab-pool.mjs +746 -746
- package/src/container/runtime-core/operations/viewport.mjs +189 -189
- package/src/container/runtime-core/search.mjs +190 -190
- package/src/container/runtime-core/subscription.mjs +224 -224
- package/src/container/runtime-core/utils.mjs +94 -94
- package/src/container/runtime-core/validation.mjs +127 -127
- package/src/container/runtime-core.mjs +1 -1
- package/src/container/subscription-registry.mjs +459 -459
- package/src/core/actions.mjs +561 -561
- package/src/core/browser.mjs +266 -266
- package/src/core/index.mjs +52 -52
- package/src/core/utils.mjs +91 -91
- package/src/events/daemon-entry.mjs +33 -33
- package/src/events/daemon.mjs +80 -80
- package/src/events/progress-log.mjs +109 -109
- package/src/events/ws-server.mjs +239 -239
- package/src/lib/client.mjs +200 -200
- package/src/lifecycle/cleanup.mjs +83 -83
- package/src/lifecycle/lock.mjs +126 -126
- package/src/lifecycle/session-registry.mjs +279 -279
- package/src/lifecycle/session-view.mjs +76 -76
- package/src/lifecycle/session-watchdog.mjs +281 -281
- package/src/services/browser-service/index.js +671 -671
- package/src/services/browser-service/internal/BrowserSession.input.test.js +389 -389
- package/src/services/browser-service/internal/BrowserSession.js +325 -304
- package/src/services/browser-service/internal/ElementRegistry.js +60 -60
- package/src/services/browser-service/internal/ProfileLock.js +84 -84
- package/src/services/browser-service/internal/SessionManager.js +184 -184
- package/src/services/browser-service/internal/SessionManager.test.js +39 -39
- package/src/services/browser-service/internal/browser-session/cookies.js +144 -144
- package/src/services/browser-service/internal/browser-session/input-ops.js +222 -222
- package/src/services/browser-service/internal/browser-session/input-pipeline.js +144 -144
- package/src/services/browser-service/internal/browser-session/logging.js +46 -46
- package/src/services/browser-service/internal/browser-session/navigation.js +38 -38
- package/src/services/browser-service/internal/browser-session/page-hooks.js +442 -442
- package/src/services/browser-service/internal/browser-session/page-management.js +302 -302
- package/src/services/browser-service/internal/browser-session/page-management.test.js +148 -148
- package/src/services/browser-service/internal/browser-session/recording.js +198 -198
- package/src/services/browser-service/internal/browser-session/runtime-events.js +61 -61
- package/src/services/browser-service/internal/browser-session/session-core.js +84 -84
- package/src/services/browser-service/internal/browser-session/session-state.js +38 -38
- package/src/services/browser-service/internal/browser-session/types.js +14 -14
- package/src/services/browser-service/internal/browser-session/utils.js +95 -95
- package/src/services/browser-service/internal/browser-session/viewport-manager.js +46 -46
- package/src/services/browser-service/internal/browser-session/viewport.js +215 -215
- package/src/services/browser-service/internal/container-matcher.js +851 -851
- package/src/services/browser-service/internal/container-registry.js +182 -182
- package/src/services/browser-service/internal/engine-manager.js +259 -259
- package/src/services/browser-service/internal/fingerprint.js +203 -203
- package/src/services/browser-service/internal/heartbeat.js +137 -137
- package/src/services/browser-service/internal/logging.js +46 -46
- package/src/services/browser-service/internal/page-runtime/runtime.js +1317 -1317
- package/src/services/browser-service/internal/pageRuntime.js +28 -28
- package/src/services/browser-service/internal/runtimeInjector.js +31 -31
- package/src/services/browser-service/internal/service-process-logger.js +140 -140
- package/src/services/browser-service/internal/state-bus.js +45 -45
- package/src/services/browser-service/internal/storage-paths.js +42 -42
- package/src/services/browser-service/internal/ws-server.js +1194 -1194
- package/src/services/browser-service/internal/ws-server.test.js +58 -58
- package/src/services/browser-service/server.mjs +6 -6
- package/src/services/controller/cli-bridge.js +93 -93
- package/src/services/controller/container-index.js +50 -50
- package/src/services/controller/container-storage.js +36 -36
- package/src/services/controller/controller-actions.js +207 -207
- package/src/services/controller/controller.js +1138 -1138
- package/src/services/controller/selectors.js +54 -54
- package/src/services/controller/transport.js +125 -125
- package/src/utils/args.mjs +26 -26
- package/src/utils/browser-service.mjs +544 -544
- package/src/utils/command-log.mjs +64 -64
- package/src/utils/config.mjs +214 -214
- package/src/utils/fingerprint.mjs +181 -181
- package/src/utils/help.mjs +216 -216
- package/src/utils/js-policy.mjs +13 -13
- package/src/utils/ws-client.mjs +30 -30
|
@@ -1,207 +1,207 @@
|
|
|
1
|
-
import { normalizeSelectors } from './selectors.js';
|
|
2
|
-
import { inferSiteFromContainerId, resolveSiteKeyFromUrl } from './container-index.js';
|
|
3
|
-
import { readUserContainerDefinition, writeUserContainerDefinition } from './container-storage.js';
|
|
4
|
-
|
|
5
|
-
export function createContainerActionHandlers(ctx) {
|
|
6
|
-
const {
|
|
7
|
-
getContainerIndex,
|
|
8
|
-
fetchInspectorSnapshot,
|
|
9
|
-
fetchInspectorBranch,
|
|
10
|
-
fetchContainerMatch,
|
|
11
|
-
userContainerRoot,
|
|
12
|
-
errorHandler,
|
|
13
|
-
} = ctx;
|
|
14
|
-
|
|
15
|
-
async function handleContainerInspect(payload = {}) {
|
|
16
|
-
const profile = payload.profile;
|
|
17
|
-
if (!profile) throw new Error('缺少 profile');
|
|
18
|
-
const context = await fetchInspectorSnapshot({
|
|
19
|
-
profile,
|
|
20
|
-
url: payload.url,
|
|
21
|
-
maxDepth: payload.maxDepth,
|
|
22
|
-
maxChildren: payload.maxChildren,
|
|
23
|
-
containerId: payload.containerId,
|
|
24
|
-
rootSelector: payload.rootSelector,
|
|
25
|
-
});
|
|
26
|
-
const snapshot = context.snapshot;
|
|
27
|
-
return {
|
|
28
|
-
success: true,
|
|
29
|
-
data: {
|
|
30
|
-
sessionId: context.sessionId,
|
|
31
|
-
profileId: context.profileId,
|
|
32
|
-
url: context.targetUrl,
|
|
33
|
-
snapshot,
|
|
34
|
-
containerSnapshot: snapshot,
|
|
35
|
-
domTree: snapshot?.dom_tree || null,
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function handleContainerInspectContainer(payload = {}) {
|
|
41
|
-
if (!payload.profile) throw new Error('缺少 profile');
|
|
42
|
-
if (!payload.containerId) throw new Error('缺少 containerId');
|
|
43
|
-
const context = await fetchInspectorSnapshot({
|
|
44
|
-
profile: payload.profile,
|
|
45
|
-
url: payload.url,
|
|
46
|
-
maxDepth: payload.maxDepth,
|
|
47
|
-
maxChildren: payload.maxChildren,
|
|
48
|
-
containerId: payload.containerId,
|
|
49
|
-
rootSelector: payload.rootSelector,
|
|
50
|
-
});
|
|
51
|
-
return {
|
|
52
|
-
success: true,
|
|
53
|
-
data: {
|
|
54
|
-
sessionId: context.sessionId,
|
|
55
|
-
profileId: context.profileId,
|
|
56
|
-
url: context.targetUrl,
|
|
57
|
-
snapshot: context.snapshot,
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async function handleContainerInspectBranch(payload = {}) {
|
|
63
|
-
if (!payload.profile) throw new Error('缺少 profile');
|
|
64
|
-
if (!payload.path) throw new Error('缺少 DOM 路径');
|
|
65
|
-
const context = await fetchInspectorBranch({
|
|
66
|
-
profile: payload.profile,
|
|
67
|
-
url: payload.url,
|
|
68
|
-
path: payload.path,
|
|
69
|
-
rootSelector: payload.rootSelector,
|
|
70
|
-
maxDepth: payload.maxDepth,
|
|
71
|
-
maxChildren: payload.maxChildren,
|
|
72
|
-
});
|
|
73
|
-
return {
|
|
74
|
-
success: true,
|
|
75
|
-
data: {
|
|
76
|
-
sessionId: context.sessionId,
|
|
77
|
-
profileId: context.profileId,
|
|
78
|
-
url: context.targetUrl,
|
|
79
|
-
branch: context.branch,
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async function handleContainerRemap(payload = {}) {
|
|
85
|
-
const containerId = payload.containerId || payload.id;
|
|
86
|
-
const selector = (payload.selector || '').trim();
|
|
87
|
-
const definition = payload.definition || {};
|
|
88
|
-
if (!containerId) throw new Error('缺少容器 ID');
|
|
89
|
-
if (!selector) throw new Error('缺少新的 selector');
|
|
90
|
-
const siteKey =
|
|
91
|
-
payload.siteKey ||
|
|
92
|
-
resolveSiteKeyFromUrl(payload.url, getContainerIndex()) ||
|
|
93
|
-
inferSiteFromContainerId(containerId);
|
|
94
|
-
if (!siteKey) throw new Error('无法确定容器所属站点');
|
|
95
|
-
const normalizedDefinition = { ...definition, id: containerId };
|
|
96
|
-
const existingSelectors = Array.isArray(normalizedDefinition.selectors) ? normalizedDefinition.selectors : [];
|
|
97
|
-
const filtered = existingSelectors.filter((item) => (item?.css || '').trim() && (item.css || '').trim() !== selector);
|
|
98
|
-
normalizedDefinition.selectors = [{ css: selector, variant: 'primary', score: 1 }, ...filtered];
|
|
99
|
-
await writeUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId, definition: normalizedDefinition });
|
|
100
|
-
return handleContainerInspect({ profile: payload.profile, url: payload.url });
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
async function handleContainerCreateChild(payload = {}) {
|
|
104
|
-
const parentId = payload.parentId || payload.parent_id;
|
|
105
|
-
const containerId = payload.containerId || payload.childId || payload.id;
|
|
106
|
-
if (!parentId) throw new Error('缺少父容器 ID');
|
|
107
|
-
if (!containerId) throw new Error('缺少子容器 ID');
|
|
108
|
-
const siteKey =
|
|
109
|
-
payload.siteKey ||
|
|
110
|
-
resolveSiteKeyFromUrl(payload.url, getContainerIndex()) ||
|
|
111
|
-
inferSiteFromContainerId(containerId) ||
|
|
112
|
-
inferSiteFromContainerId(parentId);
|
|
113
|
-
if (!siteKey) throw new Error('无法确定容器所属站点');
|
|
114
|
-
const selectorEntries = normalizeSelectors(payload.selectors || payload.selector || []) || [];
|
|
115
|
-
if (!selectorEntries.length) throw new Error('缺少 selector 定义');
|
|
116
|
-
const parentDefinition = (await readUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId: parentId, errorHandler })) || { id: parentId, children: [] };
|
|
117
|
-
const normalizedChild = {
|
|
118
|
-
...(payload.definition || {}),
|
|
119
|
-
id: containerId,
|
|
120
|
-
selectors: selectorEntries,
|
|
121
|
-
name: payload.definition?.name || payload.alias || containerId,
|
|
122
|
-
type: payload.definition?.type || 'section',
|
|
123
|
-
capabilities:
|
|
124
|
-
Array.isArray(payload.definition?.capabilities) && payload.definition.capabilities.length
|
|
125
|
-
? payload.definition.capabilities
|
|
126
|
-
: ['highlight', 'find-child', 'scroll'],
|
|
127
|
-
};
|
|
128
|
-
const alias = typeof payload.alias === 'string' ? payload.alias.trim() : '';
|
|
129
|
-
const metadata = { ...(normalizedChild.metadata || {}) };
|
|
130
|
-
if (alias) {
|
|
131
|
-
metadata.alias = alias;
|
|
132
|
-
normalizedChild.alias = alias;
|
|
133
|
-
normalizedChild.nickname = alias;
|
|
134
|
-
if (!normalizedChild.name) normalizedChild.name = alias;
|
|
135
|
-
} else {
|
|
136
|
-
delete metadata.alias;
|
|
137
|
-
}
|
|
138
|
-
if (payload.domPath) metadata.source_dom_path = payload.domPath;
|
|
139
|
-
if (payload.domMeta && typeof payload.domMeta === 'object') metadata.source_dom_meta = payload.domMeta;
|
|
140
|
-
normalizedChild.metadata = metadata;
|
|
141
|
-
if (!normalizedChild.page_patterns || !normalizedChild.page_patterns.length) {
|
|
142
|
-
const parentPatterns = parentDefinition.page_patterns || parentDefinition.pagePatterns;
|
|
143
|
-
if (parentPatterns?.length) normalizedChild.page_patterns = parentPatterns;
|
|
144
|
-
}
|
|
145
|
-
const nextParent = { ...parentDefinition };
|
|
146
|
-
const childList = Array.isArray(nextParent.children) ? [...nextParent.children] : [];
|
|
147
|
-
if (!childList.includes(containerId)) childList.push(containerId);
|
|
148
|
-
nextParent.children = childList;
|
|
149
|
-
await writeUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId, definition: normalizedChild });
|
|
150
|
-
await writeUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId: parentId, definition: nextParent });
|
|
151
|
-
return fetchContainerMatch({ profile: payload.profile, url: payload.url, maxDepth: payload.maxDepth, maxChildren: payload.maxChildren, rootSelector: payload.rootSelector });
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
async function handleContainerUpdateAlias(payload = {}) {
|
|
155
|
-
const containerId = payload.containerId || payload.id;
|
|
156
|
-
if (!containerId) throw new Error('缺少容器 ID');
|
|
157
|
-
const alias = typeof payload.alias === 'string' ? payload.alias.trim() : '';
|
|
158
|
-
const siteKey =
|
|
159
|
-
payload.siteKey ||
|
|
160
|
-
resolveSiteKeyFromUrl(payload.url, getContainerIndex()) ||
|
|
161
|
-
inferSiteFromContainerId(containerId);
|
|
162
|
-
if (!siteKey) throw new Error('无法确定容器所属站点');
|
|
163
|
-
const baseDefinition = (await readUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId, errorHandler })) || { id: containerId };
|
|
164
|
-
const metadata = { ...(baseDefinition.metadata || {}) };
|
|
165
|
-
if (alias) metadata.alias = alias; else delete metadata.alias;
|
|
166
|
-
const next = { ...baseDefinition, name: baseDefinition.name || alias || containerId, metadata };
|
|
167
|
-
if (alias) {
|
|
168
|
-
next.alias = alias;
|
|
169
|
-
next.nickname = alias;
|
|
170
|
-
} else {
|
|
171
|
-
delete next.alias;
|
|
172
|
-
delete next.nickname;
|
|
173
|
-
}
|
|
174
|
-
await writeUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId, definition: next });
|
|
175
|
-
return handleContainerInspect({ profile: payload.profile, url: payload.url });
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
async function handleContainerUpdateOperations(payload = {}) {
|
|
179
|
-
const containerId = payload.containerId || payload.id;
|
|
180
|
-
if (!containerId) throw new Error('缺少容器 ID');
|
|
181
|
-
const siteKey =
|
|
182
|
-
payload.siteKey ||
|
|
183
|
-
resolveSiteKeyFromUrl(payload.url, getContainerIndex()) ||
|
|
184
|
-
inferSiteFromContainerId(containerId);
|
|
185
|
-
if (!siteKey) throw new Error('无法确定容器所属站点');
|
|
186
|
-
const operations = Array.isArray(payload.operations) ? payload.operations : [];
|
|
187
|
-
const baseDefinition = (await readUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId, errorHandler })) || { id: containerId };
|
|
188
|
-
const next = { ...baseDefinition, operations };
|
|
189
|
-
await writeUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId, definition: next });
|
|
190
|
-
return handleContainerInspect({ profile: payload.profile, url: payload.url, containerId });
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
async function handleContainerMatch(payload = {}) {
|
|
194
|
-
return fetchContainerMatch(payload);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return {
|
|
198
|
-
handleContainerInspect,
|
|
199
|
-
handleContainerInspectContainer,
|
|
200
|
-
handleContainerInspectBranch,
|
|
201
|
-
handleContainerRemap,
|
|
202
|
-
handleContainerCreateChild,
|
|
203
|
-
handleContainerUpdateAlias,
|
|
204
|
-
handleContainerUpdateOperations,
|
|
205
|
-
handleContainerMatch,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
1
|
+
import { normalizeSelectors } from './selectors.js';
|
|
2
|
+
import { inferSiteFromContainerId, resolveSiteKeyFromUrl } from './container-index.js';
|
|
3
|
+
import { readUserContainerDefinition, writeUserContainerDefinition } from './container-storage.js';
|
|
4
|
+
|
|
5
|
+
export function createContainerActionHandlers(ctx) {
|
|
6
|
+
const {
|
|
7
|
+
getContainerIndex,
|
|
8
|
+
fetchInspectorSnapshot,
|
|
9
|
+
fetchInspectorBranch,
|
|
10
|
+
fetchContainerMatch,
|
|
11
|
+
userContainerRoot,
|
|
12
|
+
errorHandler,
|
|
13
|
+
} = ctx;
|
|
14
|
+
|
|
15
|
+
async function handleContainerInspect(payload = {}) {
|
|
16
|
+
const profile = payload.profile;
|
|
17
|
+
if (!profile) throw new Error('缺少 profile');
|
|
18
|
+
const context = await fetchInspectorSnapshot({
|
|
19
|
+
profile,
|
|
20
|
+
url: payload.url,
|
|
21
|
+
maxDepth: payload.maxDepth,
|
|
22
|
+
maxChildren: payload.maxChildren,
|
|
23
|
+
containerId: payload.containerId,
|
|
24
|
+
rootSelector: payload.rootSelector,
|
|
25
|
+
});
|
|
26
|
+
const snapshot = context.snapshot;
|
|
27
|
+
return {
|
|
28
|
+
success: true,
|
|
29
|
+
data: {
|
|
30
|
+
sessionId: context.sessionId,
|
|
31
|
+
profileId: context.profileId,
|
|
32
|
+
url: context.targetUrl,
|
|
33
|
+
snapshot,
|
|
34
|
+
containerSnapshot: snapshot,
|
|
35
|
+
domTree: snapshot?.dom_tree || null,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function handleContainerInspectContainer(payload = {}) {
|
|
41
|
+
if (!payload.profile) throw new Error('缺少 profile');
|
|
42
|
+
if (!payload.containerId) throw new Error('缺少 containerId');
|
|
43
|
+
const context = await fetchInspectorSnapshot({
|
|
44
|
+
profile: payload.profile,
|
|
45
|
+
url: payload.url,
|
|
46
|
+
maxDepth: payload.maxDepth,
|
|
47
|
+
maxChildren: payload.maxChildren,
|
|
48
|
+
containerId: payload.containerId,
|
|
49
|
+
rootSelector: payload.rootSelector,
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
success: true,
|
|
53
|
+
data: {
|
|
54
|
+
sessionId: context.sessionId,
|
|
55
|
+
profileId: context.profileId,
|
|
56
|
+
url: context.targetUrl,
|
|
57
|
+
snapshot: context.snapshot,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function handleContainerInspectBranch(payload = {}) {
|
|
63
|
+
if (!payload.profile) throw new Error('缺少 profile');
|
|
64
|
+
if (!payload.path) throw new Error('缺少 DOM 路径');
|
|
65
|
+
const context = await fetchInspectorBranch({
|
|
66
|
+
profile: payload.profile,
|
|
67
|
+
url: payload.url,
|
|
68
|
+
path: payload.path,
|
|
69
|
+
rootSelector: payload.rootSelector,
|
|
70
|
+
maxDepth: payload.maxDepth,
|
|
71
|
+
maxChildren: payload.maxChildren,
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
success: true,
|
|
75
|
+
data: {
|
|
76
|
+
sessionId: context.sessionId,
|
|
77
|
+
profileId: context.profileId,
|
|
78
|
+
url: context.targetUrl,
|
|
79
|
+
branch: context.branch,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function handleContainerRemap(payload = {}) {
|
|
85
|
+
const containerId = payload.containerId || payload.id;
|
|
86
|
+
const selector = (payload.selector || '').trim();
|
|
87
|
+
const definition = payload.definition || {};
|
|
88
|
+
if (!containerId) throw new Error('缺少容器 ID');
|
|
89
|
+
if (!selector) throw new Error('缺少新的 selector');
|
|
90
|
+
const siteKey =
|
|
91
|
+
payload.siteKey ||
|
|
92
|
+
resolveSiteKeyFromUrl(payload.url, getContainerIndex()) ||
|
|
93
|
+
inferSiteFromContainerId(containerId);
|
|
94
|
+
if (!siteKey) throw new Error('无法确定容器所属站点');
|
|
95
|
+
const normalizedDefinition = { ...definition, id: containerId };
|
|
96
|
+
const existingSelectors = Array.isArray(normalizedDefinition.selectors) ? normalizedDefinition.selectors : [];
|
|
97
|
+
const filtered = existingSelectors.filter((item) => (item?.css || '').trim() && (item.css || '').trim() !== selector);
|
|
98
|
+
normalizedDefinition.selectors = [{ css: selector, variant: 'primary', score: 1 }, ...filtered];
|
|
99
|
+
await writeUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId, definition: normalizedDefinition });
|
|
100
|
+
return handleContainerInspect({ profile: payload.profile, url: payload.url });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function handleContainerCreateChild(payload = {}) {
|
|
104
|
+
const parentId = payload.parentId || payload.parent_id;
|
|
105
|
+
const containerId = payload.containerId || payload.childId || payload.id;
|
|
106
|
+
if (!parentId) throw new Error('缺少父容器 ID');
|
|
107
|
+
if (!containerId) throw new Error('缺少子容器 ID');
|
|
108
|
+
const siteKey =
|
|
109
|
+
payload.siteKey ||
|
|
110
|
+
resolveSiteKeyFromUrl(payload.url, getContainerIndex()) ||
|
|
111
|
+
inferSiteFromContainerId(containerId) ||
|
|
112
|
+
inferSiteFromContainerId(parentId);
|
|
113
|
+
if (!siteKey) throw new Error('无法确定容器所属站点');
|
|
114
|
+
const selectorEntries = normalizeSelectors(payload.selectors || payload.selector || []) || [];
|
|
115
|
+
if (!selectorEntries.length) throw new Error('缺少 selector 定义');
|
|
116
|
+
const parentDefinition = (await readUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId: parentId, errorHandler })) || { id: parentId, children: [] };
|
|
117
|
+
const normalizedChild = {
|
|
118
|
+
...(payload.definition || {}),
|
|
119
|
+
id: containerId,
|
|
120
|
+
selectors: selectorEntries,
|
|
121
|
+
name: payload.definition?.name || payload.alias || containerId,
|
|
122
|
+
type: payload.definition?.type || 'section',
|
|
123
|
+
capabilities:
|
|
124
|
+
Array.isArray(payload.definition?.capabilities) && payload.definition.capabilities.length
|
|
125
|
+
? payload.definition.capabilities
|
|
126
|
+
: ['highlight', 'find-child', 'scroll'],
|
|
127
|
+
};
|
|
128
|
+
const alias = typeof payload.alias === 'string' ? payload.alias.trim() : '';
|
|
129
|
+
const metadata = { ...(normalizedChild.metadata || {}) };
|
|
130
|
+
if (alias) {
|
|
131
|
+
metadata.alias = alias;
|
|
132
|
+
normalizedChild.alias = alias;
|
|
133
|
+
normalizedChild.nickname = alias;
|
|
134
|
+
if (!normalizedChild.name) normalizedChild.name = alias;
|
|
135
|
+
} else {
|
|
136
|
+
delete metadata.alias;
|
|
137
|
+
}
|
|
138
|
+
if (payload.domPath) metadata.source_dom_path = payload.domPath;
|
|
139
|
+
if (payload.domMeta && typeof payload.domMeta === 'object') metadata.source_dom_meta = payload.domMeta;
|
|
140
|
+
normalizedChild.metadata = metadata;
|
|
141
|
+
if (!normalizedChild.page_patterns || !normalizedChild.page_patterns.length) {
|
|
142
|
+
const parentPatterns = parentDefinition.page_patterns || parentDefinition.pagePatterns;
|
|
143
|
+
if (parentPatterns?.length) normalizedChild.page_patterns = parentPatterns;
|
|
144
|
+
}
|
|
145
|
+
const nextParent = { ...parentDefinition };
|
|
146
|
+
const childList = Array.isArray(nextParent.children) ? [...nextParent.children] : [];
|
|
147
|
+
if (!childList.includes(containerId)) childList.push(containerId);
|
|
148
|
+
nextParent.children = childList;
|
|
149
|
+
await writeUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId, definition: normalizedChild });
|
|
150
|
+
await writeUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId: parentId, definition: nextParent });
|
|
151
|
+
return fetchContainerMatch({ profile: payload.profile, url: payload.url, maxDepth: payload.maxDepth, maxChildren: payload.maxChildren, rootSelector: payload.rootSelector });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function handleContainerUpdateAlias(payload = {}) {
|
|
155
|
+
const containerId = payload.containerId || payload.id;
|
|
156
|
+
if (!containerId) throw new Error('缺少容器 ID');
|
|
157
|
+
const alias = typeof payload.alias === 'string' ? payload.alias.trim() : '';
|
|
158
|
+
const siteKey =
|
|
159
|
+
payload.siteKey ||
|
|
160
|
+
resolveSiteKeyFromUrl(payload.url, getContainerIndex()) ||
|
|
161
|
+
inferSiteFromContainerId(containerId);
|
|
162
|
+
if (!siteKey) throw new Error('无法确定容器所属站点');
|
|
163
|
+
const baseDefinition = (await readUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId, errorHandler })) || { id: containerId };
|
|
164
|
+
const metadata = { ...(baseDefinition.metadata || {}) };
|
|
165
|
+
if (alias) metadata.alias = alias; else delete metadata.alias;
|
|
166
|
+
const next = { ...baseDefinition, name: baseDefinition.name || alias || containerId, metadata };
|
|
167
|
+
if (alias) {
|
|
168
|
+
next.alias = alias;
|
|
169
|
+
next.nickname = alias;
|
|
170
|
+
} else {
|
|
171
|
+
delete next.alias;
|
|
172
|
+
delete next.nickname;
|
|
173
|
+
}
|
|
174
|
+
await writeUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId, definition: next });
|
|
175
|
+
return handleContainerInspect({ profile: payload.profile, url: payload.url });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function handleContainerUpdateOperations(payload = {}) {
|
|
179
|
+
const containerId = payload.containerId || payload.id;
|
|
180
|
+
if (!containerId) throw new Error('缺少容器 ID');
|
|
181
|
+
const siteKey =
|
|
182
|
+
payload.siteKey ||
|
|
183
|
+
resolveSiteKeyFromUrl(payload.url, getContainerIndex()) ||
|
|
184
|
+
inferSiteFromContainerId(containerId);
|
|
185
|
+
if (!siteKey) throw new Error('无法确定容器所属站点');
|
|
186
|
+
const operations = Array.isArray(payload.operations) ? payload.operations : [];
|
|
187
|
+
const baseDefinition = (await readUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId, errorHandler })) || { id: containerId };
|
|
188
|
+
const next = { ...baseDefinition, operations };
|
|
189
|
+
await writeUserContainerDefinition({ rootDir: userContainerRoot, siteKey, containerId, definition: next });
|
|
190
|
+
return handleContainerInspect({ profile: payload.profile, url: payload.url, containerId });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function handleContainerMatch(payload = {}) {
|
|
194
|
+
return fetchContainerMatch(payload);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
handleContainerInspect,
|
|
199
|
+
handleContainerInspectContainer,
|
|
200
|
+
handleContainerInspectBranch,
|
|
201
|
+
handleContainerRemap,
|
|
202
|
+
handleContainerCreateChild,
|
|
203
|
+
handleContainerUpdateAlias,
|
|
204
|
+
handleContainerUpdateOperations,
|
|
205
|
+
handleContainerMatch,
|
|
206
|
+
};
|
|
207
|
+
}
|