Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,6 @@ AskOnce can broadcast a single query to multiple configured providers and show t

### Helper scripts (first-time & daily use)

You can either run `./start.sh` directly, or follow the steps below manually.

```text
First-time:
1. Build npm install && npm run build && pnpm ui:build
Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ AskOnce 一次提问多模型回答

### 配置步骤

你可以直接运行 `./start.sh` 一键启动,也可以按照下面的命令步骤依次执行
请按照下面的命令步骤依次执行

1. **编译**:下载项目后执行 `npm install && npm run build && pnpm ui:build`
2. **打开浏览器调试**:运行 `./start-chrome-debug.sh`
Expand Down
169 changes: 169 additions & 0 deletions src/agents/perplexity-web-stream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import type { StreamFn } from "@mariozechner/pi-agent-core";
import {
createAssistantMessageEventStream,
type AssistantMessage,
type TextContent,
type ToolCall,
type ToolResultMessage,
} from "@mariozechner/pi-ai";
import {
PerplexityWebClientBrowser,
type PerplexityWebClientOptions,
} from "../providers/perplexity-web-client-browser.js";

// Helper to strip messages for web providers
function stripForWebProvider(prompt: string): string {
return prompt;
}

export function createPerplexityWebStreamFn(cookieOrJson: string): StreamFn {
let options: PerplexityWebClientOptions;
try {
const parsed = JSON.parse(cookieOrJson);
options = typeof parsed === "string" ? { cookie: parsed, userAgent: "Mozilla/5.0" } : parsed;
} catch {
options = { cookie: cookieOrJson, userAgent: "Mozilla/5.0" };
}
const client = new PerplexityWebClientBrowser(options);

return (model, context, streamOptions) => {
const stream = createAssistantMessageEventStream();

const run = async () => {
try {
await client.init();

const messages = context.messages || [];
const systemPrompt = (context as unknown as { systemPrompt?: string }).systemPrompt || "";

const historyParts: string[] = [];
if (systemPrompt && !messages.some((m) => (m.role as string) === "system")) {
historyParts.push(`System: ${systemPrompt}`);
}
for (const m of messages) {
const role = m.role === "user" || m.role === "toolResult" ? "User" : "Assistant";
let content = "";
if (m.role === "toolResult") {
const tr = m as unknown as ToolResultMessage;
let resultText = "";
if (Array.isArray(tr.content)) {
for (const part of tr.content) {
if (part.type === "text") resultText += part.text;
}
}
content = `\n[Tool Result: ${tr.toolName}]\n${resultText}\n`;
} else if (Array.isArray(m.content)) {
for (const part of m.content) {
if (part.type === "text") content += (part as TextContent).text;
}
} else {
content = String(m.content);
}
if (m.role === "user" && content) {
content = stripForWebProvider(content) || content;
}
historyParts.push(`${role}: ${content}`);
}

const prompt = historyParts.join("\n\n");
if (!prompt) throw new Error("No message found to send to Perplexity API");

console.log(`[PerplexityWebStream] Starting run`);

const responseStream = await client.chatCompletions({
message: prompt,
model: model.id,
signal: streamOptions?.signal,
});

if (!responseStream) throw new Error("Perplexity API returned empty response body");

const reader = responseStream.getReader();
const decoder = new TextDecoder();
let buffer = "";

const contentParts: (TextContent | ToolCall)[] = [];

const createPartial = (): AssistantMessage => ({
role: "assistant",
content: [...contentParts],
api: model.api,
provider: model.provider,
model: model.id,
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } },
stopReason: "stop",
timestamp: Date.now(),
});

const processLine = (line: string) => {
if (!line || !line.startsWith("data:")) return;
const dataStr = line.slice(5).trim();
if (dataStr === "[DONE]" || !dataStr) return;
try {
const data = JSON.parse(dataStr);
const delta = data.text || data.content || data.delta;
if (typeof delta === "string" && delta) {
if (contentParts.length === 0) {
contentParts[0] = { type: "text", text: "" };
stream.push({ type: "text_start", contentIndex: 0, partial: createPartial() });
}
(contentParts[0] as TextContent).text += delta;
stream.push({ type: "text_delta", contentIndex: 0, delta, partial: createPartial() });
}
} catch {
// ignore
}
};

while (true) {
const { done, value } = await reader.read();
if (done) {
if (buffer.trim()) processLine(buffer.trim());
break;
}
const chunk = decoder.decode(value, { stream: true });
const combined = buffer + chunk;
const parts = combined.split("\n");
buffer = parts.pop() || "";
for (const part of parts) {
processLine(part.trim());
}
}

const assistantMessage: AssistantMessage = {
role: "assistant",
content: contentParts.length > 0 ? contentParts : [{ type: "text", text: "" }],
stopReason: "stop",
api: model.api,
provider: model.provider,
model: model.id,
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } },
timestamp: Date.now(),
};

stream.push({ type: "done", reason: "stop", message: assistantMessage });
} catch (err) {
stream.push({
type: "error",
reason: "error",
error: {
role: "assistant",
content: [],
stopReason: "error",
errorMessage: err instanceof Error ? err.message : String(err),
api: model.api,
provider: model.provider,
model: model.id,
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } },
timestamp: Date.now(),
},
} as any);
Comment on lines +145 to +160
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 检查 AssistantMessageEventStream 的类型定义,确认支持哪些事件类型
ast-grep --pattern 'type $_ = {
  $$$
  type: $_
  $$$
}'

# 搜索其他文件中如何处理 error 事件
rg -n "type.*error" --type ts -C3

Repository: linuxhsj/openclaw-zero-token

Length of output: 50385


🏁 Script executed:

#!/bin/bash
# Find the AssistantMessageEventStream type definition
fd -t f "\.ts$" -x grep -l "AssistantMessageEventStream\|createAssistantMessageEventStream" {}

# Also look at the perplexity-web-stream.ts file directly
head -20 src/agents/perplexity-web-stream.ts

Repository: linuxhsj/openclaw-zero-token

Length of output: 1404


🏁 Script executed:

#!/bin/bash
# Check imports and type definitions in perplexity-web-stream.ts
grep -n "import\|type\|interface" src/agents/perplexity-web-stream.ts | head -30

Repository: linuxhsj/openclaw-zero-token

Length of output: 1084


🏁 Script executed:

#!/bin/bash
# Look at similar agent files to see how they handle errors
grep -A 20 "catch (err)" src/agents/claude-web-stream.ts | head -30
grep -A 20 "catch (err)" src/agents/deepseek-web-stream.ts | head -30
grep -A 20 "catch (err)" src/agents/openai-ws-stream.ts | head -30

Repository: linuxhsj/openclaw-zero-token

Length of output: 1391


🏁 Script executed:

#!/bin/bash
# Check what the actual stream.push calls look like in the perplexity file
sed -n '140,165p' src/agents/perplexity-web-stream.ts

Repository: linuxhsj/openclaw-zero-token

Length of output: 1012


🏁 Script executed:

#!/bin/bash
# Let's examine the deepseek file to see if it has the same pattern
sed -n '140,165p' src/agents/deepseek-web-stream.ts

# Also check other files
rg "stream\.push.*type.*error" --type ts -A 3 -B 1

Repository: linuxhsj/openclaw-zero-token

Length of output: 1079


🏁 Script executed:

#!/bin/bash
# Let's look for type definitions related to stream events
rg "type.*Event\|interface.*Event" src/agents/ --type ts | grep -i stream

# Also search for createAssistantMessageEventStream usage/definition
rg "createAssistantMessageEventStream" --type ts -B 2 -A 5 | head -50

Repository: linuxhsj/openclaw-zero-token

Length of output: 2874


🏁 Script executed:

#!/bin/bash
# Search for error handling in all web stream files to see the pattern
rg "stream\.push.*type.*error" src/agents/ --type ts -A 10 | head -100

# Also specifically look at chatgpt, claude, and other agents
grep -n "as any" src/agents/chatgpt-web-stream.ts
grep -n "as any" src/agents/claude-web-stream.ts
grep -n "as any" src/agents/gemini-web-stream.ts

Repository: linuxhsj/openclaw-zero-token

Length of output: 140


🏁 Script executed:

#!/bin/bash
# Check if there are any TypeScript config or declaration files that might help
find . -name "*.d.ts" -o -name "tsconfig.json" | head -10

# Look for pi-ai package or its types in node_modules or package references
cat package.json | grep -A 2 -B 2 pi-ai

Repository: linuxhsj/openclaw-zero-token

Length of output: 612


🏁 Script executed:

#!/bin/bash
# Let's see all the files using stream.push with type "error" and check for as any
rg "stream\.push.*type.*error" src/agents/ --type ts -B 5 -A 15

# Check if any file handles error without as any
rg "type.*error.*reason" src/agents/ --type ts -B 3 -A 8

Repository: linuxhsj/openclaw-zero-token

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Let's search for all stream.push calls with error handling
rg "stream\.push" src/agents/ --type ts -B 3 -A 8 | grep -A 15 "type.*error"

Repository: linuxhsj/openclaw-zero-token

Length of output: 13296


🏁 Script executed:

#!/bin/bash
# Check the actual line counts and content around line 147-160 in perplexity-web-stream.ts
wc -l src/agents/perplexity-web-stream.ts

# Get the full catch block
sed -n '145,165p' src/agents/perplexity-web-stream.ts

Repository: linuxhsj/openclaw-zero-token

Length of output: 768


🏁 Script executed:

#!/bin/bash
# Let's examine ollama-stream.ts to see how it handles errors differently
sed -n '140,170p' src/agents/ollama-stream.ts

# Also check buildStreamErrorAssistantMessage
rg "buildStreamErrorAssistantMessage" --type ts -B 2 -A 10

Repository: linuxhsj/openclaw-zero-token

Length of output: 4464


🏁 Script executed:

#!/bin/bash
# Check what files use as any for stream.push with error
grep -l "as any.*stream.push\|stream.push.*as any" src/agents/*.ts

# Count how many files have this pattern
grep -r "stream.push.*as any" src/agents/ --type ts | wc -l

Repository: linuxhsj/openclaw-zero-token

Length of output: 208


🏁 Script executed:

#!/bin/bash
# Check if perplexity-web-stream.ts imports buildStreamErrorAssistantMessage
grep "buildStreamErrorAssistantMessage\|stream-message-shared" src/agents/perplexity-web-stream.ts

# Check the full imports section
head -20 src/agents/perplexity-web-stream.ts

Repository: linuxhsj/openclaw-zero-token

Length of output: 669


🏁 Script executed:

#!/antml/bin
# Let's also verify the type signature of buildStreamErrorAssistantMessage
sed -n '1,50p' src/agents/stream-message-shared.ts | grep -A 15 "buildStreamErrorAssistantMessage"

Repository: linuxhsj/openclaw-zero-token

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Let's look at the full stream-message-shared.ts file
cat src/agents/stream-message-shared.ts | head -80

Repository: linuxhsj/openclaw-zero-token

Length of output: 2084


🏁 Script executed:

#!/bin/bash
# Also look at how chatgpt-web-stream uses the error pattern
grep -A 20 "catch (err)" src/agents/chatgpt-web-stream.ts | tail -25

Repository: linuxhsj/openclaw-zero-token

Length of output: 731


删除 as any 类型断言,使用类型化的错误消息构造函数

第 160 行的 as any 违反了编码规范。应该导入并使用 buildStreamErrorAssistantMessage 辅助函数(在 src/agents/stream-message-shared.ts 中定义),它已经提供了正确的类型推断,无需类型转换。

建议的修复方案
-import {
-  PerplexityWebClientBrowser,
-  type PerplexityWebClientOptions,
-} from "../providers/perplexity-web-client-browser.js";
+import {
+  buildStreamErrorAssistantMessage,
+} from "./stream-message-shared.js";
+import {
+  PerplexityWebClientBrowser,
+  type PerplexityWebClientOptions,
+} from "../providers/perplexity-web-client-browser.js";

       } catch (err) {
-        stream.push({
-          type: "error",
-          reason: "error",
-          error: {
-            role: "assistant",
-            content: [],
-            stopReason: "error",
-            errorMessage: err instanceof Error ? err.message : String(err),
-            api: model.api,
-            provider: model.provider,
-            model: model.id,
-            usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } },
-            timestamp: Date.now(),
-          },
-        } as any);
+        stream.push({
+          type: "error",
+          reason: "error",
+          error: buildStreamErrorAssistantMessage({
+            model,
+            errorMessage: err instanceof Error ? err.message : String(err),
+          }),
+        });

该模式已在其他代理文件(如 ollama-stream.tsopenai-ws-stream.ts)中成功应用,无需 as any 转换。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/agents/perplexity-web-stream.ts` around lines 145 - 160, Replace the
ad-hoc typed error object (the object pushed to stream inside the catch block)
and the trailing "as any" with a call to the typed helper
buildStreamErrorAssistantMessage: import buildStreamErrorAssistantMessage from
src/agents/stream-message-shared.ts, build the error payload using the caught
err (err instanceof Error ? err.message : String(err)), model info (model.api,
model.provider, model.id), usage defaults, and timestamp, then push the result
of buildStreamErrorAssistantMessage(...) to stream (instead of the current
object and "as any"); update the catch block in perplexity-web-stream.ts to use
buildStreamErrorAssistantMessage so TypeScript infers the correct type.

} finally {
stream.end();
}
};

queueMicrotask(() => void run());
return stream;
};
}
94 changes: 79 additions & 15 deletions src/commands/onboard-web-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,31 @@
* 支持同时授权多个 Web 模型
*/

import type { WizardStep } from "../wizard/types.js";
import { loadConfig, writeConfigFile } from "../config/io.js";
import type { OpenClawConfig } from "../config/config.js";
import { resolveBrowserConfig, resolveProfile } from "../browser/config.js";
import fs from "node:fs/promises";
import path from "node:path";
import { resolveOpenClawAgentDir } from "../agents/agent-paths.js";
import { ensureAuthProfileStore, saveAuthProfileStore } from "../agents/auth-profiles.js";
import type { AuthProfileStore } from "../agents/auth-profiles/types.js";

import { ensureOpenClawModelsJson } from "../agents/models-config.js";
import type { OpenClawConfig } from "../config/config.js";
import { loadConfig, writeConfigFile } from "../config/io.js";
import { resolveAgentModelPrimaryValue } from "../config/model-input.js";
import { loginChatGPTWeb } from "../providers/chatgpt-web-auth.js";
// 导入各个 web 模型的登录函数
import { loginClaudeWeb } from "../providers/claude-web-auth.js";
import { loginChatGPTWeb } from "../providers/chatgpt-web-auth.js";
import { loginDeepseekWeb } from "../providers/deepseek-web-auth.js";
import { loginDoubaoWeb } from "../providers/doubao-web-auth.js";
import { loginGeminiWeb } from "../providers/gemini-web-auth.js";
import { loginZWeb } from "../providers/glm-web-auth.js";
import { loginGlmIntlWeb } from "../providers/glm-intl-web-auth.js";
import { loginZWeb } from "../providers/glm-web-auth.js";
import { loginGrokWeb } from "../providers/grok-web-auth.js";
import { loginKimiWeb } from "../providers/kimi-web-auth.js";
import { loginQwenWeb } from "../providers/qwen-web-auth.js";
import { loginQwenCNWeb } from "../providers/qwen-cn-web-auth.js";
import { loginQwenWeb } from "../providers/qwen-web-auth.js";
import type { WizardStep } from "../wizard/types.js";
import { applyAgentDefaultModelPrimary } from "./onboard-auth.config-shared.js";

// Web 模型凭证保存助手函数
async function saveWebModelCredentials(
providerId: string,
credentials: unknown
): Promise<void> {
async function saveWebModelCredentials(providerId: string, credentials: unknown): Promise<void> {
const store = ensureAuthProfileStore();
const profileId = `${providerId}:default`;

Expand Down Expand Up @@ -105,6 +105,65 @@ async function addModelToWhitelist(providerId: string, modelIds: string[]): Prom
console.log(` > 已更新模型白名单到 openclaw.json`);
}

/**
* 将 agent models.json 中的 providers 同步到 openclaw.json。
* 解决首次运行时报错的问题:openclaw.json 初始 models.providers 为空,
* 导致 resolveConfiguredModelRef 默认回退到 anthropic,且 model catalog 无可用 provider。
*/
async function syncModelsProvidersToConfig(): Promise<void> {
const config = loadConfig();
await ensureOpenClawModelsJson(config);

const agentDir = resolveOpenClawAgentDir();
const modelsPath = path.join(agentDir, "models.json");

let providers: Record<string, unknown> = {};
try {
const raw = await fs.readFile(modelsPath, "utf8");
const parsed = JSON.parse(raw) as { providers?: Record<string, unknown> };
if (parsed?.providers && typeof parsed.providers === "object") {
providers = parsed.providers;
}
} catch {
return;
}
Comment on lines +127 to +129
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

不要静默吞掉同步失败异常。

Line 127Line 129catch { return; } 会让同步失败无日志,用户只看到“授权完成”,但配置可能未落盘,排障成本高。建议至少输出 warning,并区分文件缺失和 JSON 异常。

🔧 建议修复
-  } catch {
-    return;
+  } catch (error) {
+    const code = (error as NodeJS.ErrnoException)?.code;
+    if (code === "ENOENT") return;
+    const message = error instanceof Error ? error.message : String(error);
+    console.warn(`  > 同步 models.providers 失败: ${modelsPath} (${message})`);
+    return;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch {
return;
}
} catch (error) {
const code = (error as NodeJS.ErrnoException)?.code;
if (code === "ENOENT") return;
const message = error instanceof Error ? error.message : String(error);
console.warn(` > 同步 models.providers 失败: ${modelsPath} (${message})`);
return;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/onboard-web-auth.ts` around lines 127 - 129, The empty catch
block after the config sync (the try/catch in src/commands/onboard-web-auth.ts)
is silently swallowing errors; update the catch to accept an error (e.g. catch
(err)) and log a warning with the error details instead of just returning.
Distinguish common cases: if err.code === 'ENOENT' log a specific "file missing"
warning, if err is a SyntaxError (JSON.parse) log a "JSON parse" warning
including err.message, otherwise log a generic warning with the full error; keep
the current control flow (return or rethrow) after logging so failures are
observable.


if (Object.keys(providers).length === 0) {
return;
}

let nextConfig: OpenClawConfig = {
...config,
models: {
...config.models,
mode: config.models?.mode ?? "merge",
providers: { ...(config.models?.providers ?? {}), ...providers },
},
};

// 若尚未设置主模型,使用首个 web provider 的首个模型,避免回退到 anthropic
if (!resolveAgentModelPrimaryValue(config.agents?.defaults?.model)) {
const firstEntry = Object.entries(providers).find(
([, p]) =>
p &&
typeof p === "object" &&
Array.isArray((p as { models?: unknown[] }).models) &&
(p as { models: { id?: string }[] }).models.length > 0,
);
Comment on lines +146 to +152
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

默认主模型选择应与注释一致,仅限 web provider。

注释写的是“首个 web provider”,但当前 find 条件没有限制 providerId,可能选到非 web provider。建议在筛选条件里加 providerId.endsWith("-web")

🔧 建议修复
-    const firstEntry = Object.entries(providers).find(
-      ([, p]) =>
+    const firstEntry = Object.entries(providers).find(
+      ([providerId, p]) =>
+        providerId.endsWith("-web") &&
         p &&
         typeof p === "object" &&
         Array.isArray((p as { models?: unknown[] }).models) &&
         (p as { models: { id?: string }[] }).models.length > 0,
     );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const firstEntry = Object.entries(providers).find(
([, p]) =>
p &&
typeof p === "object" &&
Array.isArray((p as { models?: unknown[] }).models) &&
(p as { models: { id?: string }[] }).models.length > 0,
);
const firstEntry = Object.entries(providers).find(
([providerId, p]) =>
providerId.endsWith("-web") &&
p &&
typeof p === "object" &&
Array.isArray((p as { models?: unknown[] }).models) &&
(p as { models: { id?: string }[] }).models.length > 0,
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/onboard-web-auth.ts` around lines 146 - 152, The current
selection of firstEntry from providers can pick non-web providers because the
find predicate doesn't check providerId; update the predicate used when
computing firstEntry to require the provider key (providerId) to end with "-web"
(e.g., add providerId.endsWith("-web") to the condition), so only web providers
are considered while keeping the existing checks for p being an object and
having a non-empty models array.

if (firstEntry) {
const [providerId, provider] = firstEntry;
const firstModel = (provider as { models: { id: string }[] }).models[0];
if (firstModel?.id) {
nextConfig = applyAgentDefaultModelPrimary(nextConfig, `${providerId}/${firstModel.id}`);
console.log(` > 已设置默认模型: ${providerId}/${firstModel.id}`);
}
}
}

await writeConfigFile(nextConfig);
console.log(` > 已同步 models.providers 到 openclaw.json`);
}

// Web 模型定义
interface WebModelProvider {
id: string;
Expand Down Expand Up @@ -134,8 +193,8 @@ export async function runOnboardWebAuth(): Promise<void> {

// 显示已授权的模型
const store = ensureAuthProfileStore();
const authorizedModels = Object.keys(store.profiles).filter((key) =>
key.endsWith("-web") || key.includes("-web:")
const authorizedModels = Object.keys(store.profiles).filter(
(key) => key.endsWith("-web") || key.includes("-web:"),
);

if (authorizedModels.length > 0) {
Expand Down Expand Up @@ -242,6 +301,11 @@ export async function runOnboardWebAuth(): Promise<void> {
}
}

// 将 agent models.json 的 providers 同步到 openclaw.json,避免首次运行时报错
if (selectedProviders.length > 0) {
await syncModelsProvidersToConfig();
}

console.log("\n授权完成!");
console.log("你可以在 Web UI 中使用这些模型了。");
}
Expand Down
2 changes: 1 addition & 1 deletion start-chrome-debug.sh
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ if curl -s http://127.0.0.1:9222/json/version > /dev/null 2>&1; then
echo "=========================================="
echo "1. 在各标签页中登录需要使用的平台"
echo "2. 确保 config 中 browser.attachOnly=true 且 browser.cdpUrl=http://127.0.0.1:9222"
echo "3. 运行 ./onboard.sh 选择对应平台完成授权(将复用此浏览器)"
echo "3. 运行 ./onboard.sh webauth 选择对应平台完成授权(将复用此浏览器)"
echo ""
echo "停止调试模式:"
echo " pkill -f 'chrome.*remote-debugging-port=9222'"
Expand Down
31 changes: 0 additions & 31 deletions start-quick.sh

This file was deleted.

Loading
Loading