@bryceli/openclaw 0.1.0 → 0.1.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/README.md +22 -28
- package/index.js +3 -0
- package/package.json +19 -11
- package/scripts/cli.mjs +65 -0
- package/scripts/paths.mjs +25 -0
- package/scripts/run-openclaw.mjs +29 -24
- package/scripts/runtime-install.mjs +85 -0
- package/scripts/setup-openclaw.mjs +52 -79
package/README.md
CHANGED
|
@@ -1,45 +1,39 @@
|
|
|
1
|
-
# OpenClaw
|
|
1
|
+
# OpenClaw Lightweight Wrapper
|
|
2
2
|
|
|
3
|
-
This
|
|
3
|
+
This package is a lightweight wrapper around `openclaw`.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Why this version is lighter
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
7
|
+
- `npm install @bryceli/openclaw` only installs the wrapper
|
|
8
|
+
- the heavy upstream `openclaw` runtime is installed later, on demand
|
|
9
|
+
- runtime files are stored in the current project under `.openclaw-runtime`
|
|
10
|
+
- config files are stored in the current project under `.openclaw-home`
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## Current-project isolation
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Everything is project-local:
|
|
15
|
+
|
|
16
|
+
- `.openclaw-runtime`
|
|
17
|
+
- `.openclaw-home/openclaw.json`
|
|
18
|
+
- `.openclaw-home/.env`
|
|
19
|
+
- `.openclaw-home/workspace`
|
|
15
20
|
|
|
16
21
|
## Quick start
|
|
17
22
|
|
|
18
23
|
```bash
|
|
19
|
-
|
|
20
|
-
npm run setup
|
|
21
|
-
npm run gateway
|
|
24
|
+
npx @bryceli/openclaw
|
|
22
25
|
```
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
Or explicitly:
|
|
25
28
|
|
|
26
29
|
```bash
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
npx @bryceli/openclaw setup
|
|
31
|
+
npx @bryceli/openclaw gateway
|
|
32
|
+
npx @bryceli/openclaw dashboard
|
|
30
33
|
```
|
|
31
34
|
|
|
32
|
-
## Interface types
|
|
33
|
-
|
|
34
|
-
- OpenAI Compatible (最常用)
|
|
35
|
-
- Anthropic Native (原生)
|
|
36
|
-
- OpenAI Responses (新接口)
|
|
37
|
-
- Google Gemini (原生)
|
|
38
|
-
- Ollama Native (本地原生)
|
|
39
|
-
|
|
40
35
|
## Notes
|
|
41
36
|
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
- All paths are project-local through `OPENCLAW_HOME`.
|
|
37
|
+
- The wrapper defaults to `setup` when no subcommand is given.
|
|
38
|
+
- The OpenClaw runtime is installed lazily when you first run setup or gateway-related commands.
|
|
39
|
+
- The UI keeps short Chinese annotations, but the package itself stays lightweight.
|
package/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,25 +1,33 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
2
|
"name": "@bryceli/openclaw",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Lightweight OpenClaw wrapper with project-local runtime install.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"bryce-openclaw": "index.js"
|
|
8
|
+
},
|
|
6
9
|
"publishConfig": {
|
|
7
10
|
"access": "public"
|
|
8
11
|
},
|
|
12
|
+
"files": [
|
|
13
|
+
"index.js",
|
|
14
|
+
"README.md",
|
|
15
|
+
"scripts"
|
|
16
|
+
],
|
|
9
17
|
"scripts": {
|
|
10
|
-
"setup": "node
|
|
11
|
-
"openclaw": "node
|
|
12
|
-
"gateway": "node
|
|
13
|
-
"dashboard": "node
|
|
14
|
-
"models:list": "node
|
|
15
|
-
"models:test": "node
|
|
18
|
+
"setup": "node index.js setup",
|
|
19
|
+
"openclaw": "node index.js openclaw",
|
|
20
|
+
"gateway": "node index.js gateway",
|
|
21
|
+
"dashboard": "node index.js dashboard",
|
|
22
|
+
"models:list": "node index.js models:list",
|
|
23
|
+
"models:test": "node index.js models:test",
|
|
24
|
+
"runtime:install": "node index.js runtime:install"
|
|
16
25
|
},
|
|
17
26
|
"engines": {
|
|
18
27
|
"node": ">=22"
|
|
19
28
|
},
|
|
20
29
|
"dependencies": {
|
|
21
30
|
"@clack/prompts": "^0.10.1",
|
|
22
|
-
"openclaw": "latest",
|
|
23
31
|
"picocolors": "^1.1.1"
|
|
24
32
|
}
|
|
25
|
-
}
|
|
33
|
+
}
|
package/scripts/cli.mjs
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import pc from "picocolors";
|
|
2
|
+
import { ensureRuntimeInstalled } from "./runtime-install.mjs";
|
|
3
|
+
import { runOpenClaw } from "./run-openclaw.mjs";
|
|
4
|
+
import { runSetup } from "./setup-openclaw.mjs";
|
|
5
|
+
|
|
6
|
+
const command = process.argv[2] ?? "setup";
|
|
7
|
+
const rest = process.argv.slice(3);
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
switch (command) {
|
|
11
|
+
case "setup":
|
|
12
|
+
await runSetup();
|
|
13
|
+
break;
|
|
14
|
+
case "runtime:install":
|
|
15
|
+
await ensureRuntimeInstalled();
|
|
16
|
+
console.log(pc.green("OpenClaw runtime installed for the current project."));
|
|
17
|
+
break;
|
|
18
|
+
case "gateway":
|
|
19
|
+
await runOpenClaw(["gateway", "run", ...rest]);
|
|
20
|
+
break;
|
|
21
|
+
case "dashboard":
|
|
22
|
+
await runOpenClaw(["dashboard", ...rest]);
|
|
23
|
+
break;
|
|
24
|
+
case "models:list":
|
|
25
|
+
await runOpenClaw(["models", "list", ...rest]);
|
|
26
|
+
break;
|
|
27
|
+
case "models:test":
|
|
28
|
+
await runOpenClaw(["models", "test", ...rest]);
|
|
29
|
+
break;
|
|
30
|
+
case "openclaw":
|
|
31
|
+
await runOpenClaw(rest);
|
|
32
|
+
break;
|
|
33
|
+
case "help":
|
|
34
|
+
case "--help":
|
|
35
|
+
case "-h":
|
|
36
|
+
printHelp();
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
await runOpenClaw([command, ...rest]);
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
44
|
+
console.error(pc.red(message));
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function printHelp() {
|
|
49
|
+
console.log(
|
|
50
|
+
[
|
|
51
|
+
"Usage:",
|
|
52
|
+
" npx @bryceli/openclaw",
|
|
53
|
+
" npx @bryceli/openclaw setup",
|
|
54
|
+
" npx @bryceli/openclaw gateway",
|
|
55
|
+
" npx @bryceli/openclaw dashboard",
|
|
56
|
+
" npx @bryceli/openclaw models:list",
|
|
57
|
+
" npx @bryceli/openclaw models:test",
|
|
58
|
+
"",
|
|
59
|
+
"Notes:",
|
|
60
|
+
" setup installs only a small wrapper first.",
|
|
61
|
+
" The heavy OpenClaw runtime is installed lazily into ./.openclaw-runtime when needed.",
|
|
62
|
+
" All config is stored in the current project via ./.openclaw-home."
|
|
63
|
+
].join("\n")
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = path.dirname(__filename);
|
|
6
|
+
|
|
7
|
+
export function getPackageRoot() {
|
|
8
|
+
return path.resolve(__dirname, "..");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getProjectRoot() {
|
|
12
|
+
return process.cwd();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getOpenClawHome(projectRoot = getProjectRoot()) {
|
|
16
|
+
return path.join(projectRoot, ".openclaw-home");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getRuntimeRoot(projectRoot = getProjectRoot()) {
|
|
20
|
+
return path.join(projectRoot, ".openclaw-runtime");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getRuntimePackageJson(projectRoot = getProjectRoot()) {
|
|
24
|
+
return path.join(getRuntimeRoot(projectRoot), "package.json");
|
|
25
|
+
}
|
package/scripts/run-openclaw.mjs
CHANGED
|
@@ -1,29 +1,34 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { ensureRuntimeInstalled } from "./runtime-install.mjs";
|
|
3
|
+
import { getOpenClawHome, getProjectRoot } from "./paths.mjs";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
5
|
+
export async function runOpenClaw(args = process.argv.slice(2)) {
|
|
6
|
+
const projectRoot = getProjectRoot();
|
|
7
|
+
const openclawHome = getOpenClawHome(projectRoot);
|
|
8
|
+
const { runtimeBin } = await ensureRuntimeInstalled({ quiet: false });
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
const
|
|
10
|
+
await new Promise((resolve, reject) => {
|
|
11
|
+
const child = spawn(runtimeBin, args, {
|
|
12
|
+
cwd: projectRoot,
|
|
13
|
+
env: {
|
|
14
|
+
...process.env,
|
|
15
|
+
OPENCLAW_HOME: openclawHome
|
|
16
|
+
},
|
|
17
|
+
stdio: "inherit"
|
|
18
|
+
});
|
|
12
19
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
},
|
|
19
|
-
stdio: "inherit"
|
|
20
|
-
});
|
|
20
|
+
child.on("exit", (code, signal) => {
|
|
21
|
+
if (signal) {
|
|
22
|
+
reject(new Error(`OpenClaw exited via signal ${signal}`));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
26
|
+
if ((code ?? 0) !== 0) {
|
|
27
|
+
reject(new Error(`OpenClaw exited with code ${code ?? 0}`));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
});
|
|
31
|
+
resolve();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import pc from "picocolors";
|
|
5
|
+
import { getProjectRoot, getRuntimePackageJson, getRuntimeRoot } from "./paths.mjs";
|
|
6
|
+
|
|
7
|
+
const RUNTIME_PACKAGE_JSON = {
|
|
8
|
+
name: "openclaw-runtime-local",
|
|
9
|
+
private: true,
|
|
10
|
+
version: "0.0.0",
|
|
11
|
+
description: "Project-local OpenClaw runtime installed by @bryceli/openclaw",
|
|
12
|
+
type: "module"
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export async function ensureRuntimeInstalled({ quiet = false } = {}) {
|
|
16
|
+
const projectRoot = getProjectRoot();
|
|
17
|
+
const runtimeRoot = getRuntimeRoot(projectRoot);
|
|
18
|
+
const runtimePackageJson = getRuntimePackageJson(projectRoot);
|
|
19
|
+
const runtimeBin = getRuntimeExecutable(projectRoot);
|
|
20
|
+
|
|
21
|
+
if (existsSync(runtimeBin)) {
|
|
22
|
+
return { projectRoot, runtimeRoot, runtimeBin };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
mkdirSync(runtimeRoot, { recursive: true });
|
|
26
|
+
if (!existsSync(runtimePackageJson)) {
|
|
27
|
+
writeFileSync(runtimePackageJson, `${JSON.stringify(RUNTIME_PACKAGE_JSON, null, 2)}\n`, "utf8");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!quiet) {
|
|
31
|
+
console.log(pc.cyan("Installing OpenClaw runtime into the current project (.openclaw-runtime)..."));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
await runNpmInstall(runtimeRoot);
|
|
35
|
+
|
|
36
|
+
if (!existsSync(runtimeBin)) {
|
|
37
|
+
throw new Error("OpenClaw runtime install finished but the executable was not found.");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { projectRoot, runtimeRoot, runtimeBin };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getRuntimeExecutable(projectRoot = getProjectRoot()) {
|
|
44
|
+
const binName = process.platform === "win32" ? "openclaw.cmd" : "openclaw";
|
|
45
|
+
return path.join(getRuntimeRoot(projectRoot), "node_modules", ".bin", binName);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function runNpmInstall(runtimeRoot) {
|
|
49
|
+
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
50
|
+
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const child = spawn(npmCommand, ["install", "--no-save", "openclaw@latest"], {
|
|
53
|
+
cwd: runtimeRoot,
|
|
54
|
+
env: withNodePath(process.env),
|
|
55
|
+
stdio: "inherit"
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
child.on("exit", (code, signal) => {
|
|
59
|
+
if (signal) {
|
|
60
|
+
reject(new Error(`npm install terminated by signal ${signal}`));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (code !== 0) {
|
|
65
|
+
reject(new Error(`npm install exited with code ${code}`));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
resolve();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function withNodePath(env) {
|
|
75
|
+
const nextEnv = { ...env };
|
|
76
|
+
const nodeDir = process.execPath ? path.dirname(process.execPath) : "";
|
|
77
|
+
if (!nodeDir) {
|
|
78
|
+
return nextEnv;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const delimiter = process.platform === "win32" ? ";" : ":";
|
|
82
|
+
nextEnv.Path = nextEnv.Path ? `${nodeDir}${delimiter}${nextEnv.Path}` : nodeDir;
|
|
83
|
+
nextEnv.PATH = nextEnv.PATH ? `${nodeDir}${delimiter}${nextEnv.PATH}` : nodeDir;
|
|
84
|
+
return nextEnv;
|
|
85
|
+
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import * as p from "@clack/prompts";
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
2
|
import pc from "picocolors";
|
|
3
3
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import { ensureRuntimeInstalled } from "./runtime-install.mjs";
|
|
6
|
+
import { getOpenClawHome, getProjectRoot } from "./paths.mjs";
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const projectRoot = path.resolve(__dirname, "..");
|
|
10
|
-
const openclawHome = path.join(projectRoot, ".openclaw-home");
|
|
8
|
+
const projectRoot = getProjectRoot();
|
|
9
|
+
const openclawHome = getOpenClawHome(projectRoot);
|
|
11
10
|
const workspaceDir = path.join(openclawHome, "workspace");
|
|
12
11
|
const configPath = path.join(openclawHome, "openclaw.json");
|
|
13
12
|
const envPath = path.join(openclawHome, ".env");
|
|
@@ -45,12 +44,27 @@ const INTERFACE_TYPES = [
|
|
|
45
44
|
}
|
|
46
45
|
];
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
export async function runSetup() {
|
|
48
|
+
await main();
|
|
49
|
+
}
|
|
49
50
|
|
|
50
51
|
async function main() {
|
|
51
52
|
console.clear();
|
|
52
53
|
p.intro(pc.bgBlue(pc.white(" OpenClaw Local Setup (安装器) ")));
|
|
53
54
|
|
|
55
|
+
const installRuntimeAnswer = await p.confirm({
|
|
56
|
+
message: "Install the heavy OpenClaw runtime now? (现在安装真正的 OpenClaw 运行时)",
|
|
57
|
+
initialValue: true
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (p.isCancel(installRuntimeAnswer)) {
|
|
61
|
+
exitCancelled();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (installRuntimeAnswer) {
|
|
65
|
+
await ensureRuntimeInstalled();
|
|
66
|
+
}
|
|
67
|
+
|
|
54
68
|
const interfaceType = await p.select({
|
|
55
69
|
message: "Select the API interface type (选择接口类型)",
|
|
56
70
|
options: INTERFACE_TYPES.map((item) => ({
|
|
@@ -70,7 +84,7 @@ async function main() {
|
|
|
70
84
|
throw new Error(`Unsupported interface type: ${interfaceType}`);
|
|
71
85
|
}
|
|
72
86
|
|
|
73
|
-
const suggestedBaseUrl =
|
|
87
|
+
const suggestedBaseUrl = interfaceConfig.defaultBaseUrl;
|
|
74
88
|
const baseUrl = await promptRequiredText({
|
|
75
89
|
message: "Enter the API base URL (接口地址)",
|
|
76
90
|
placeholder: suggestedBaseUrl,
|
|
@@ -106,17 +120,9 @@ async function main() {
|
|
|
106
120
|
});
|
|
107
121
|
|
|
108
122
|
mkdirSync(workspaceDir, { recursive: true });
|
|
109
|
-
writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
123
|
+
writeFileSync(configPath, `${JSON.stringify(config.file, null, 2)}\n`, "utf8");
|
|
110
124
|
writeFileSync(envPath, buildEnvFile(config.envEntries), "utf8");
|
|
111
125
|
|
|
112
|
-
const notes = [];
|
|
113
|
-
if (interfaceType === "openai-responses" && !isOfficialOpenAiBaseUrl(baseUrl)) {
|
|
114
|
-
notes.push("Custom Responses endpoints are written using OpenClaw's practical openai-completions compatibility mode.");
|
|
115
|
-
}
|
|
116
|
-
if (interfaceType === "ollama-native") {
|
|
117
|
-
notes.push("Ollama uses native API mode with no /v1 suffix so tool calling stays reliable.");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
126
|
let outro = `${pc.green("OpenClaw project setup complete")}\n\n`;
|
|
121
127
|
outro += `${pc.cyan("Project")}: ${projectRoot}\n`;
|
|
122
128
|
outro += `${pc.cyan("OPENCLAW_HOME")}: ${openclawHome}\n`;
|
|
@@ -125,21 +131,9 @@ async function main() {
|
|
|
125
131
|
outro += `${pc.cyan("Primary model")}: ${config.primaryModelRef}\n`;
|
|
126
132
|
outro += `${pc.cyan("Fallback model")}: ${config.fallbackModelRef}\n`;
|
|
127
133
|
|
|
128
|
-
if (notes.length) {
|
|
129
|
-
outro += `\n${pc.yellow("Notes")}:\n- ${notes.join("\n- ")}`;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
134
|
p.outro(outro);
|
|
133
135
|
}
|
|
134
136
|
|
|
135
|
-
async function getSuggestedBaseUrl(interfaceConfig) {
|
|
136
|
-
if (interfaceConfig.value === "ollama-native") {
|
|
137
|
-
return "http://127.0.0.1:11434";
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return interfaceConfig.defaultBaseUrl;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
137
|
function buildOpenClawConfig({ interfaceType, baseUrl, apiKey, primaryModelId, fallbackModelId }) {
|
|
144
138
|
const providerId = getProviderId(interfaceType);
|
|
145
139
|
const envEntries = buildEnvEntries(interfaceType, apiKey);
|
|
@@ -158,40 +152,33 @@ function buildOpenClawConfig({ interfaceType, baseUrl, apiKey, primaryModelId, f
|
|
|
158
152
|
}
|
|
159
153
|
}));
|
|
160
154
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
ref,
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
155
|
+
return {
|
|
156
|
+
file: {
|
|
157
|
+
agents: {
|
|
158
|
+
defaults: {
|
|
159
|
+
workspace: workspaceDir,
|
|
160
|
+
model: {
|
|
161
|
+
primary: `${providerId}/${primaryModelId}`,
|
|
162
|
+
fallbacks:
|
|
163
|
+
fallbackModelId && fallbackModelId !== primaryModelId
|
|
164
|
+
? [`${providerId}/${fallbackModelId}`]
|
|
165
|
+
: []
|
|
166
|
+
},
|
|
167
|
+
models: Object.fromEntries(
|
|
168
|
+
modelRefs.map((ref, index) => [
|
|
169
|
+
ref,
|
|
170
|
+
{ alias: index === 0 ? "Primary (主模型)" : "Fallback (备用模型)" }
|
|
171
|
+
])
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
models: {
|
|
176
|
+
mode: "merge",
|
|
177
|
+
providers: {
|
|
178
|
+
[providerId]: buildProviderConfig({ interfaceType, baseUrl, providerModels })
|
|
179
|
+
}
|
|
178
180
|
}
|
|
179
181
|
},
|
|
180
|
-
models: {
|
|
181
|
-
mode: "merge",
|
|
182
|
-
providers: {
|
|
183
|
-
[providerId]: buildProviderConfig({
|
|
184
|
-
providerId,
|
|
185
|
-
interfaceType,
|
|
186
|
-
baseUrl,
|
|
187
|
-
providerModels
|
|
188
|
-
})
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
...config,
|
|
195
182
|
envEntries,
|
|
196
183
|
primaryModelRef: `${providerId}/${primaryModelId}`,
|
|
197
184
|
fallbackModelRef:
|
|
@@ -201,7 +188,7 @@ function buildOpenClawConfig({ interfaceType, baseUrl, apiKey, primaryModelId, f
|
|
|
201
188
|
};
|
|
202
189
|
}
|
|
203
190
|
|
|
204
|
-
function buildProviderConfig({
|
|
191
|
+
function buildProviderConfig({ interfaceType, baseUrl, providerModels }) {
|
|
205
192
|
if (interfaceType === "google-gemini") {
|
|
206
193
|
return {
|
|
207
194
|
baseUrl,
|
|
@@ -243,12 +230,6 @@ function buildEnvEntries(interfaceType, apiKey) {
|
|
|
243
230
|
};
|
|
244
231
|
}
|
|
245
232
|
|
|
246
|
-
if (interfaceType === "google-gemini") {
|
|
247
|
-
return {
|
|
248
|
-
OPENCLAW_MODEL_API_KEY: apiKey
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
|
|
252
233
|
return {
|
|
253
234
|
OPENCLAW_MODEL_API_KEY: apiKey
|
|
254
235
|
};
|
|
@@ -257,15 +238,11 @@ function buildEnvEntries(interfaceType, apiKey) {
|
|
|
257
238
|
function buildEnvFile(entries) {
|
|
258
239
|
const lines = Object.entries(entries)
|
|
259
240
|
.filter(([, value]) => typeof value === "string" && value.length > 0)
|
|
260
|
-
.map(([key, value]) => `${key}=${
|
|
241
|
+
.map(([key, value]) => `${key}=${JSON.stringify(String(value))}`);
|
|
261
242
|
|
|
262
243
|
return `${lines.join("\n")}\n`;
|
|
263
244
|
}
|
|
264
245
|
|
|
265
|
-
function escapeEnvValue(value) {
|
|
266
|
-
return JSON.stringify(String(value));
|
|
267
|
-
}
|
|
268
|
-
|
|
269
246
|
function getProviderId(interfaceType) {
|
|
270
247
|
switch (interfaceType) {
|
|
271
248
|
case "anthropic-native":
|
|
@@ -332,7 +309,7 @@ async function tryDetectModelId(baseUrl, interfaceType) {
|
|
|
332
309
|
return tagId.trim();
|
|
333
310
|
}
|
|
334
311
|
} catch {
|
|
335
|
-
// Best
|
|
312
|
+
// Best effort only.
|
|
336
313
|
}
|
|
337
314
|
}
|
|
338
315
|
|
|
@@ -348,10 +325,6 @@ function stripTrailingSlash(value) {
|
|
|
348
325
|
return String(value ?? "").replace(/\/+$/, "");
|
|
349
326
|
}
|
|
350
327
|
|
|
351
|
-
function isOfficialOpenAiBaseUrl(value) {
|
|
352
|
-
return /https:\/\/api\.openai\.com\/?v1?$/i.test(stripTrailingSlash(value));
|
|
353
|
-
}
|
|
354
|
-
|
|
355
328
|
async function promptRequiredText({ message, placeholder, initialValue }) {
|
|
356
329
|
const answer = await p.text({
|
|
357
330
|
message,
|
|
@@ -390,4 +363,4 @@ async function promptOptionalText({ message, placeholder, initialValue }) {
|
|
|
390
363
|
function exitCancelled() {
|
|
391
364
|
p.cancel("Operation cancelled");
|
|
392
365
|
process.exit(0);
|
|
393
|
-
}
|
|
366
|
+
}
|