





















@@ -1,28 +1,20 @@
11import fs from "node:fs";
22import path from "node:path";
33import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
4-import type { BrowserProfileConfig } from "../config/config.js";
5-import { getRuntimeConfig, mutateConfigFile } from "../config/config.js";
6-import { deriveDefaultBrowserCdpPortRange } from "../config/port-defaults.js";
4+import { getRuntimeConfig } from "../config/config.js";
75import { formatErrorMessage } from "../infra/errors.js";
86import { resolveUserPath } from "../utils.js";
97import { assertCdpEndpointAllowed } from "./cdp.helpers.js";
108import { resolveOpenClawUserDataDir } from "./chrome.js";
11-import { parseHttpUrl, resolveBrowserConfig, resolveProfile } from "./config.js";
9+import { createBrowserProfileConfig, deleteBrowserProfileConfig } from "./config-mutations.js";
10+import { parseHttpUrl, resolveProfile } from "./config.js";
1211import {
1312BrowserConflictError,
1413BrowserProfileNotFoundError,
15-BrowserResourceExhaustedError,
1614BrowserValidationError,
1715} from "./errors.js";
1816import { getBrowserProfileCapabilities } from "./profile-capabilities.js";
19-import {
20-allocateCdpPort,
21-allocateColor,
22-getUsedColors,
23-getUsedPorts,
24-isValidProfileName,
25-} from "./profiles.js";
17+import { isValidProfileName } from "./profiles.js";
2618import type { BrowserRouteContext, ProfileStatus } from "./server-context.js";
2719import { movePathToTrash } from "./trash.js";
2820@@ -53,30 +45,6 @@ export type DeleteProfileResult = {
53455446const HEX_COLOR_RE = /^#[0-9A-Fa-f]{6}$/;
554756-const cdpPortRange = (resolved: {
57-controlPort: number;
58-cdpPortRangeStart?: number;
59-cdpPortRangeEnd?: number;
60-}): { start: number; end: number } => {
61-const start = resolved.cdpPortRangeStart;
62-const end = resolved.cdpPortRangeEnd;
63-if (
64-typeof start === "number" &&
65-Number.isFinite(start) &&
66-Number.isInteger(start) &&
67-typeof end === "number" &&
68-Number.isFinite(end) &&
69-Number.isInteger(end) &&
70-start > 0 &&
71-end >= start &&
72-end <= 65535
73-) {
74-return { start, end };
75-}
76-77-return deriveDefaultBrowserCdpPortRange(resolved.controlPort);
78-};
79-8048export function createBrowserProfilesService(ctx: BrowserRouteContext) {
8149const listProfiles = async (): Promise<ProfileStatus[]> => {
8250return await ctx.listProfiles();
@@ -138,76 +106,14 @@ export function createBrowserProfilesService(ctx: BrowserRouteContext) {
138106parsedCdpUrl = parsed.normalized;
139107}
140108141-const mutation = await mutateConfigFile<BrowserProfileConfig>({
142-afterWrite: { mode: "auto" },
143-mutate: async (draft) => {
144-const latestResolved = resolveBrowserConfig({
145- ...state.resolved,
146- ...draft.browser,
147-profiles: draft.browser?.profiles ?? state.resolved.profiles,
148-});
149-const latestProfiles = draft.browser?.profiles ?? {};
150-if (name in latestProfiles || name in latestResolved.profiles) {
151-throw new BrowserConflictError(`profile "${name}" already exists`);
152-}
153-154-const profileColor =
155-explicitProfileColor ?? allocateColor(getUsedColors(latestResolved.profiles));
156-157-let nextProfileConfig: BrowserProfileConfig;
158-if (parsedCdpUrl) {
159-try {
160-await assertCdpEndpointAllowed(parsedCdpUrl, latestResolved.ssrfPolicy);
161-} catch (err) {
162-throw new BrowserValidationError(formatErrorMessage(err));
163-}
164-nextProfileConfig = {
165-cdpUrl: parsedCdpUrl,
166- ...(driver ? { driver } : {}),
167-color: profileColor,
168-};
169-} else if (driver === "existing-session") {
170-// existing-session uses Chrome MCP auto-connect; no CDP port needed.
171-nextProfileConfig = {
172- driver,
173-attachOnly: true,
174- ...(normalizedUserDataDir ? { userDataDir: normalizedUserDataDir } : {}),
175-color: profileColor,
176-};
177-} else {
178-const usedPorts = getUsedPorts(latestResolved.profiles);
179-const rangeStart = draft.browser?.cdpPortRangeStart ?? state.resolved.cdpPortRangeStart;
180-const range = cdpPortRange({
181-controlPort: state.resolved.controlPort,
182-cdpPortRangeStart: rangeStart,
183-cdpPortRangeEnd:
184-draft.browser?.cdpPortRangeStart === undefined
185- ? state.resolved.cdpPortRangeEnd
186- : latestResolved.cdpPortRangeEnd,
187-});
188-const cdpPort = allocateCdpPort(usedPorts, range);
189-if (cdpPort === null) {
190-throw new BrowserResourceExhaustedError("no available CDP ports in range");
191-}
192-nextProfileConfig = {
193- cdpPort,
194- ...(driver ? { driver } : {}),
195-color: profileColor,
196-};
197-}
198-199-draft.browser = {
200- ...draft.browser,
201-profiles: {
202- ...draft.browser?.profiles,
203-[name]: nextProfileConfig,
204-},
205-};
206-return nextProfileConfig;
207-},
109+const profileConfig = await createBrowserProfileConfig({
110+ name,
111+resolved: state.resolved,
112+ ...(explicitProfileColor ? { color: explicitProfileColor } : {}),
113+ ...(parsedCdpUrl ? { parsedCdpUrl } : {}),
114+ ...(normalizedUserDataDir ? { userDataDir: normalizedUserDataDir } : {}),
115+ ...(driver ? { driver } : {}),
208116});
209-210-const profileConfig = mutation.result;
211117if (!profileConfig) {
212118throw new BrowserProfileNotFoundError(`profile "${name}" not found after creation`);
213119}
@@ -270,22 +176,7 @@ export function createBrowserProfilesService(ctx: BrowserRouteContext) {
270176}
271177}
272178273-await mutateConfigFile({
274-afterWrite: { mode: "auto" },
275-mutate: (draft) => {
276-const { [name]: _removed, ...remainingProfiles } = draft.browser?.profiles ?? {};
277-const nextBrowser = {
278- ...draft.browser,
279-profiles: remainingProfiles,
280-};
281-if (nextBrowser.defaultProfile === name) {
282-delete nextBrowser.defaultProfile;
283-}
284-draft.browser = {
285- ...nextBrowser,
286-};
287-},
288-});
179+await deleteBrowserProfileConfig(name);
289180290181delete state.resolved.profiles[name];
291182state.profiles.delete(name);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。