@openclawcity/become 1.0.12 → 1.0.18
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/dist/cli.cjs +438 -157
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +438 -157
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -59,146 +59,147 @@ import { join as join2 } from "path";
|
|
|
59
59
|
import { homedir as homedir2 } from "os";
|
|
60
60
|
import { execSync } from "child_process";
|
|
61
61
|
var OPENCLAW_CONFIG = join2(homedir2(), ".openclaw", "openclaw.json");
|
|
62
|
-
var
|
|
63
|
-
var
|
|
64
|
-
var
|
|
65
|
-
|
|
62
|
+
var STATE_DIR = join2(homedir2(), ".become", "state");
|
|
63
|
+
var BACKUP_PATH = join2(STATE_DIR, "original_openclaw.json");
|
|
64
|
+
var ORIGINAL_URL_PATH = join2(STATE_DIR, "original_base_url.txt");
|
|
65
|
+
var PATCHED_PROVIDER_PATH = join2(STATE_DIR, "patched_provider.txt");
|
|
66
|
+
function patchOpenClaw(config) {
|
|
66
67
|
if (!existsSync2(OPENCLAW_CONFIG)) {
|
|
67
68
|
throw new Error(`OpenClaw config not found at ${OPENCLAW_CONFIG}`);
|
|
68
69
|
}
|
|
69
70
|
const raw = readFileSync2(OPENCLAW_CONFIG, "utf-8");
|
|
70
|
-
const clawConfig =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
const clawConfig = parseConfig(raw);
|
|
72
|
+
mkdirSync2(STATE_DIR, { recursive: true });
|
|
73
|
+
const primaryModel = clawConfig.agents?.defaults?.model?.primary ?? "";
|
|
74
|
+
if (!primaryModel) {
|
|
75
|
+
throw new Error("No default model configured in openclaw.json (agents.defaults.model.primary)");
|
|
76
|
+
}
|
|
77
|
+
const providerName = primaryModel.split("/")[0];
|
|
78
|
+
if (!providerName) {
|
|
79
|
+
throw new Error(`Cannot determine provider from model: ${primaryModel}`);
|
|
80
|
+
}
|
|
81
|
+
if (existsSync2(ORIGINAL_URL_PATH)) {
|
|
82
|
+
const existingUrl = readFileSync2(ORIGINAL_URL_PATH, "utf-8").trim();
|
|
83
|
+
if (existingUrl) {
|
|
84
|
+
console.log("become is already connected. Run `become off` first to disconnect.");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const modelsJsonPath = getModelsJsonPath(clawConfig);
|
|
89
|
+
let modelsConfig = null;
|
|
90
|
+
let modelsSource = "openclaw.json";
|
|
91
|
+
if (modelsJsonPath && existsSync2(modelsJsonPath)) {
|
|
92
|
+
modelsConfig = JSON.parse(readFileSync2(modelsJsonPath, "utf-8"));
|
|
93
|
+
modelsSource = "models.json";
|
|
94
|
+
}
|
|
95
|
+
let provider = null;
|
|
96
|
+
let providerLocation = null;
|
|
97
|
+
if (modelsConfig?.providers?.[providerName]) {
|
|
98
|
+
provider = modelsConfig.providers[providerName];
|
|
99
|
+
providerLocation = modelsConfig.providers;
|
|
100
|
+
} else if (clawConfig.models?.providers?.[providerName]) {
|
|
101
|
+
provider = clawConfig.models.providers[providerName];
|
|
102
|
+
providerLocation = clawConfig.models.providers;
|
|
103
|
+
modelsSource = "openclaw.json";
|
|
104
|
+
}
|
|
105
|
+
if (!provider) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Provider "${providerName}" not found in models.json or openclaw.json. Your model is "${primaryModel}" which needs a "${providerName}" provider.`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
const originalUrl = provider.baseUrl;
|
|
111
|
+
if (!originalUrl) {
|
|
112
|
+
throw new Error(`Provider "${providerName}" has no baseUrl`);
|
|
74
113
|
}
|
|
75
|
-
mkdirSync2(join2(homedir2(), ".become", "state"), { recursive: true });
|
|
76
114
|
writeFileSync2(BACKUP_PATH, raw, "utf-8");
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
if (!agent) {
|
|
83
|
-
throw new Error(`Agent "${agentId}" not found in agents.list. Available: ${agents.map((a) => a.id).join(", ")}`);
|
|
84
|
-
}
|
|
85
|
-
originalModel = agent.model ?? clawConfig.agents?.defaults?.model?.primary ?? "";
|
|
86
|
-
patchedAgentId = agentId;
|
|
87
|
-
const modelId2 = stripProvider(originalModel);
|
|
88
|
-
if (!modelId2) {
|
|
89
|
-
throw new Error("No model configured for this agent. Set a model in openclaw.json first.");
|
|
90
|
-
}
|
|
91
|
-
agent.model = `become/${modelId2}`;
|
|
115
|
+
writeFileSync2(ORIGINAL_URL_PATH, originalUrl, "utf-8");
|
|
116
|
+
writeFileSync2(PATCHED_PROVIDER_PATH, `${providerName}:${modelsSource}`, "utf-8");
|
|
117
|
+
provider.baseUrl = `http://127.0.0.1:${config.proxy_port}`;
|
|
118
|
+
if (modelsSource === "models.json" && modelsJsonPath) {
|
|
119
|
+
writeFileSync2(modelsJsonPath, JSON.stringify(modelsConfig, null, 2), "utf-8");
|
|
92
120
|
} else {
|
|
93
|
-
|
|
94
|
-
patchedAgentId = "_defaults";
|
|
95
|
-
const modelId2 = stripProvider(originalModel);
|
|
96
|
-
if (!modelId2) {
|
|
97
|
-
throw new Error("No default model configured. Set agents.defaults.model.primary in openclaw.json first.");
|
|
98
|
-
}
|
|
99
|
-
if (!clawConfig.agents) clawConfig.agents = {};
|
|
100
|
-
if (!clawConfig.agents.defaults) clawConfig.agents.defaults = {};
|
|
101
|
-
if (!clawConfig.agents.defaults.model) clawConfig.agents.defaults.model = {};
|
|
102
|
-
clawConfig.agents.defaults.model.primary = `become/${modelId2}`;
|
|
103
|
-
}
|
|
104
|
-
writeFileSync2(ORIGINAL_MODEL_PATH, originalModel, "utf-8");
|
|
105
|
-
writeFileSync2(PATCHED_AGENT_PATH, patchedAgentId, "utf-8");
|
|
106
|
-
const modelId = stripProvider(originalModel);
|
|
107
|
-
if (!clawConfig.models) clawConfig.models = {};
|
|
108
|
-
if (!clawConfig.models.providers) clawConfig.models.providers = {};
|
|
109
|
-
clawConfig.models.providers.become = {
|
|
110
|
-
api: config.llm_provider === "openai" || config.llm_provider === "openrouter" ? "openai-completions" : "anthropic-messages",
|
|
111
|
-
baseUrl: `http://127.0.0.1:${config.proxy_port}`,
|
|
112
|
-
apiKey: config.llm_api_key,
|
|
113
|
-
models: [
|
|
114
|
-
{ id: modelId, name: `${modelId} via become` }
|
|
115
|
-
]
|
|
116
|
-
};
|
|
117
|
-
writeFileSync2(OPENCLAW_CONFIG, JSON.stringify(clawConfig, null, 2), "utf-8");
|
|
118
|
-
console.log("Restarting OpenClaw gateway...");
|
|
119
|
-
try {
|
|
120
|
-
execSync("openclaw gateway restart", { stdio: "pipe", timeout: 15e3 });
|
|
121
|
-
console.log("OpenClaw gateway restarted. Your agent is now routing through become.");
|
|
122
|
-
} catch {
|
|
123
|
-
console.log("\n*** OpenClaw gateway needs a manual restart to pick up the new config. ***");
|
|
124
|
-
console.log("*** Run: openclaw gateway restart ***\n");
|
|
121
|
+
writeFileSync2(OPENCLAW_CONFIG, JSON.stringify(clawConfig, null, 2), "utf-8");
|
|
125
122
|
}
|
|
123
|
+
console.log(` provider: ${providerName}`);
|
|
124
|
+
console.log(` baseUrl: ${originalUrl} -> localhost:${config.proxy_port}`);
|
|
125
|
+
restartGateway();
|
|
126
126
|
}
|
|
127
127
|
function restoreOpenClaw() {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const backupConfig = parseOpenClawConfig(backup);
|
|
134
|
-
if (!backupConfig.models?.providers?.become) {
|
|
135
|
-
writeFileSync2(OPENCLAW_CONFIG, backup, "utf-8");
|
|
136
|
-
restartGateway();
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
128
|
+
const originalUrl = readSafe(ORIGINAL_URL_PATH);
|
|
129
|
+
const patchInfo = readSafe(PATCHED_PROVIDER_PATH);
|
|
130
|
+
if (!originalUrl || !patchInfo) {
|
|
131
|
+
cleanState();
|
|
132
|
+
return;
|
|
139
133
|
}
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
agent.model = originalModel;
|
|
150
|
-
}
|
|
151
|
-
} else {
|
|
152
|
-
if (config.agents?.defaults?.model) {
|
|
153
|
-
config.agents.defaults.model.primary = originalModel;
|
|
134
|
+
const [providerName, source] = patchInfo.split(":");
|
|
135
|
+
if (source === "models.json") {
|
|
136
|
+
const clawConfig = parseConfig(readFileSync2(OPENCLAW_CONFIG, "utf-8"));
|
|
137
|
+
const modelsJsonPath = getModelsJsonPath(clawConfig);
|
|
138
|
+
if (modelsJsonPath && existsSync2(modelsJsonPath)) {
|
|
139
|
+
const modelsConfig = JSON.parse(readFileSync2(modelsJsonPath, "utf-8"));
|
|
140
|
+
if (modelsConfig.providers?.[providerName]) {
|
|
141
|
+
modelsConfig.providers[providerName].baseUrl = originalUrl;
|
|
142
|
+
writeFileSync2(modelsJsonPath, JSON.stringify(modelsConfig, null, 2), "utf-8");
|
|
154
143
|
}
|
|
155
144
|
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
145
|
+
} else {
|
|
146
|
+
if (existsSync2(OPENCLAW_CONFIG)) {
|
|
147
|
+
const clawConfig = parseConfig(readFileSync2(OPENCLAW_CONFIG, "utf-8"));
|
|
148
|
+
if (clawConfig.models?.providers?.[providerName]) {
|
|
149
|
+
clawConfig.models.providers[providerName].baseUrl = originalUrl;
|
|
150
|
+
writeFileSync2(OPENCLAW_CONFIG, JSON.stringify(clawConfig, null, 2), "utf-8");
|
|
151
|
+
}
|
|
163
152
|
}
|
|
164
153
|
}
|
|
165
|
-
|
|
154
|
+
cleanState();
|
|
166
155
|
restartGateway();
|
|
167
156
|
}
|
|
168
157
|
function listOpenClawAgents() {
|
|
169
158
|
if (!existsSync2(OPENCLAW_CONFIG)) return [];
|
|
170
159
|
try {
|
|
171
|
-
const config =
|
|
160
|
+
const config = parseConfig(readFileSync2(OPENCLAW_CONFIG, "utf-8"));
|
|
172
161
|
const agents = config.agents?.list ?? [];
|
|
173
|
-
const defaultModel =
|
|
162
|
+
const defaultModel = config.agents?.defaults?.model?.primary ?? "unknown";
|
|
174
163
|
if (agents.length === 0) {
|
|
175
164
|
return [{ id: "_defaults", model: defaultModel }];
|
|
176
165
|
}
|
|
177
166
|
return agents.map((a) => ({
|
|
178
167
|
id: a.id,
|
|
179
|
-
model:
|
|
168
|
+
model: a.model ?? defaultModel
|
|
180
169
|
}));
|
|
181
170
|
} catch {
|
|
182
171
|
return [];
|
|
183
172
|
}
|
|
184
173
|
}
|
|
185
|
-
function
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
174
|
+
function getModelsJsonPath(clawConfig) {
|
|
175
|
+
const agentList = clawConfig.agents?.list ?? [];
|
|
176
|
+
const defaultAgent = agentList.find((a) => a.default) ?? agentList[0];
|
|
177
|
+
if (defaultAgent?.agentDir) {
|
|
178
|
+
return join2(defaultAgent.agentDir.replace("~", homedir2()), "models.json");
|
|
179
|
+
}
|
|
180
|
+
const mainPath = join2(homedir2(), ".openclaw", "agents", "main", "agent", "models.json");
|
|
181
|
+
if (existsSync2(mainPath)) return mainPath;
|
|
182
|
+
return null;
|
|
190
183
|
}
|
|
191
|
-
function
|
|
184
|
+
function parseConfig(raw) {
|
|
192
185
|
const stripped = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/,\s*([\]}])/g, "$1");
|
|
193
186
|
return JSON.parse(stripped);
|
|
194
187
|
}
|
|
195
|
-
function
|
|
188
|
+
function readSafe(path) {
|
|
196
189
|
try {
|
|
197
190
|
return existsSync2(path) ? readFileSync2(path, "utf-8").trim() : "";
|
|
198
191
|
} catch {
|
|
199
192
|
return "";
|
|
200
193
|
}
|
|
201
194
|
}
|
|
195
|
+
function cleanState() {
|
|
196
|
+
for (const f of [ORIGINAL_URL_PATH, PATCHED_PROVIDER_PATH]) {
|
|
197
|
+
try {
|
|
198
|
+
writeFileSync2(f, "", "utf-8");
|
|
199
|
+
} catch {
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
202
203
|
function restartGateway() {
|
|
203
204
|
console.log("Restarting OpenClaw gateway...");
|
|
204
205
|
try {
|
|
@@ -284,6 +285,11 @@ Proxy port (default 30001): `);
|
|
|
284
285
|
}
|
|
285
286
|
}
|
|
286
287
|
|
|
288
|
+
// src/cli/commands.ts
|
|
289
|
+
import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
|
|
290
|
+
import { join as join7 } from "path";
|
|
291
|
+
import { homedir as homedir5 } from "os";
|
|
292
|
+
|
|
287
293
|
// src/proxy/server.ts
|
|
288
294
|
import { createServer } from "http";
|
|
289
295
|
|
|
@@ -710,7 +716,7 @@ Rules:
|
|
|
710
716
|
|
|
711
717
|
// src/proxy/server.ts
|
|
712
718
|
var SKILL_CACHE_TTL_MS = 5e3;
|
|
713
|
-
function createProxyServer(config, analyzer) {
|
|
719
|
+
function createProxyServer(config, analyzer, overrideUpstreamUrl) {
|
|
714
720
|
const store = new FileSkillStore({ baseDir: config.baseDir });
|
|
715
721
|
const trust = new TrustManager(config.baseDir);
|
|
716
722
|
const extractor = analyzer ? new LessonExtractor(store, trust, analyzer) : null;
|
|
@@ -757,7 +763,7 @@ function createProxyServer(config, analyzer) {
|
|
|
757
763
|
stats.skills_injected++;
|
|
758
764
|
}
|
|
759
765
|
}
|
|
760
|
-
const upstreamUrl = buildUpstreamUrl(config, req.url);
|
|
766
|
+
const upstreamUrl = buildUpstreamUrl(overrideUpstreamUrl ?? config.llm_base_url, req.url);
|
|
761
767
|
const upstreamHeaders = buildUpstreamHeaders(config, req.headers);
|
|
762
768
|
const isStreaming = body.stream === true;
|
|
763
769
|
const modifiedBody = JSON.stringify(body);
|
|
@@ -841,8 +847,8 @@ function readBody(req) {
|
|
|
841
847
|
req.on("error", reject);
|
|
842
848
|
});
|
|
843
849
|
}
|
|
844
|
-
function buildUpstreamUrl(
|
|
845
|
-
const base =
|
|
850
|
+
function buildUpstreamUrl(baseUrl, path) {
|
|
851
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
846
852
|
return `${base}${path}`;
|
|
847
853
|
}
|
|
848
854
|
function buildUpstreamHeaders(config, incomingHeaders) {
|
|
@@ -1390,98 +1396,185 @@ function readBody2(req) {
|
|
|
1390
1396
|
}
|
|
1391
1397
|
|
|
1392
1398
|
// src/cli/adapter/ironclaw.ts
|
|
1393
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync5, mkdirSync as mkdirSync5, copyFileSync } from "fs";
|
|
1399
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync5, mkdirSync as mkdirSync5, copyFileSync, unlinkSync as unlinkSync2 } from "fs";
|
|
1394
1400
|
import { join as join5 } from "path";
|
|
1395
1401
|
import { homedir as homedir3 } from "os";
|
|
1396
1402
|
import { execSync as execSync2 } from "child_process";
|
|
1397
|
-
var IRONCLAW_ENV = join5(homedir3(), ".ironclaw", ".env");
|
|
1403
|
+
var IRONCLAW_ENV = join5(process.env.IRONCLAW_BASE_DIR ?? join5(homedir3(), ".ironclaw"), ".env");
|
|
1398
1404
|
var BACKUP_PATH2 = join5(homedir3(), ".become", "state", "original_ironclaw.env");
|
|
1399
1405
|
function patchIronClaw(config) {
|
|
1400
1406
|
if (!existsSync5(IRONCLAW_ENV)) {
|
|
1401
1407
|
throw new Error(`IronClaw .env not found at ${IRONCLAW_ENV}`);
|
|
1402
1408
|
}
|
|
1409
|
+
if (existsSync5(BACKUP_PATH2)) {
|
|
1410
|
+
console.log("become is already connected to IronClaw. Run `become off` first.");
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1403
1413
|
mkdirSync5(join5(homedir3(), ".become", "state"), { recursive: true });
|
|
1404
1414
|
copyFileSync(IRONCLAW_ENV, BACKUP_PATH2);
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
+
const content = readFileSync5(IRONCLAW_ENV, "utf-8");
|
|
1416
|
+
const backendMatch = content.match(/^LLM_BACKEND=(.+)$/m);
|
|
1417
|
+
const backend = backendMatch?.[1]?.trim().toLowerCase() ?? "openai_compatible";
|
|
1418
|
+
const proxyUrl = `http://127.0.0.1:${config.proxy_port}`;
|
|
1419
|
+
const vars = {};
|
|
1420
|
+
switch (backend) {
|
|
1421
|
+
case "anthropic":
|
|
1422
|
+
vars["ANTHROPIC_BASE_URL"] = proxyUrl;
|
|
1423
|
+
break;
|
|
1424
|
+
case "ollama":
|
|
1425
|
+
vars["OLLAMA_BASE_URL"] = proxyUrl;
|
|
1426
|
+
break;
|
|
1427
|
+
case "nearai":
|
|
1428
|
+
case "near_ai":
|
|
1429
|
+
case "near":
|
|
1430
|
+
vars["NEARAI_BASE_URL"] = proxyUrl;
|
|
1431
|
+
break;
|
|
1432
|
+
default:
|
|
1433
|
+
vars["LLM_BASE_URL"] = proxyUrl;
|
|
1434
|
+
break;
|
|
1415
1435
|
}
|
|
1436
|
+
patchDotEnv(IRONCLAW_ENV, vars);
|
|
1437
|
+
console.log(` backend: ${backend}`);
|
|
1438
|
+
console.log(` patched: ${Object.keys(vars).join(", ")} -> localhost:${config.proxy_port}`);
|
|
1439
|
+
restartIronClaw();
|
|
1416
1440
|
}
|
|
1417
1441
|
function restoreIronClaw() {
|
|
1418
1442
|
if (!existsSync5(BACKUP_PATH2)) {
|
|
1419
|
-
|
|
1443
|
+
return;
|
|
1420
1444
|
}
|
|
1421
1445
|
copyFileSync(BACKUP_PATH2, IRONCLAW_ENV);
|
|
1446
|
+
try {
|
|
1447
|
+
unlinkSync2(BACKUP_PATH2);
|
|
1448
|
+
} catch {
|
|
1449
|
+
}
|
|
1450
|
+
restartIronClaw();
|
|
1451
|
+
}
|
|
1452
|
+
function restartIronClaw() {
|
|
1422
1453
|
console.log("Restarting IronClaw...");
|
|
1423
1454
|
try {
|
|
1424
|
-
execSync2("ironclaw service
|
|
1455
|
+
execSync2("ironclaw service stop", { stdio: "pipe", timeout: 1e4 });
|
|
1456
|
+
execSync2("ironclaw service start", { stdio: "pipe", timeout: 1e4 });
|
|
1425
1457
|
console.log("IronClaw restarted.");
|
|
1458
|
+
return;
|
|
1459
|
+
} catch {
|
|
1460
|
+
}
|
|
1461
|
+
try {
|
|
1462
|
+
execSync2("launchctl kickstart -k gui/$(id -u)/com.ironclaw.daemon", { stdio: "pipe", timeout: 1e4 });
|
|
1463
|
+
console.log("IronClaw restarted via launchctl.");
|
|
1464
|
+
return;
|
|
1465
|
+
} catch {
|
|
1466
|
+
}
|
|
1467
|
+
try {
|
|
1468
|
+
execSync2("systemctl --user restart ironclaw", { stdio: "pipe", timeout: 1e4 });
|
|
1469
|
+
console.log("IronClaw restarted via systemd.");
|
|
1470
|
+
return;
|
|
1426
1471
|
} catch {
|
|
1427
|
-
console.log("\n*** IronClaw needs a manual restart. ***");
|
|
1428
|
-
console.log("*** Run: ironclaw service restart ***\n");
|
|
1429
1472
|
}
|
|
1473
|
+
console.log("\n*** IronClaw needs a manual restart. ***");
|
|
1474
|
+
console.log("*** Run: ironclaw service stop && ironclaw service start ***\n");
|
|
1430
1475
|
}
|
|
1431
1476
|
function patchDotEnv(path, vars) {
|
|
1432
|
-
let content = readFileSync5(path, "utf-8");
|
|
1477
|
+
let content = existsSync5(path) ? readFileSync5(path, "utf-8") : "";
|
|
1433
1478
|
for (const [key, value] of Object.entries(vars)) {
|
|
1434
1479
|
const regex = new RegExp(`^${key}=.*$`, "m");
|
|
1435
1480
|
if (regex.test(content)) {
|
|
1436
1481
|
content = content.replace(regex, `${key}=${value}`);
|
|
1437
1482
|
} else {
|
|
1438
|
-
content
|
|
1439
|
-
|
|
1483
|
+
content = content.trimEnd() + (content.length > 0 ? "\n" : "") + `${key}=${value}
|
|
1484
|
+
`;
|
|
1440
1485
|
}
|
|
1441
1486
|
}
|
|
1442
1487
|
writeFileSync5(path, content, "utf-8");
|
|
1443
1488
|
}
|
|
1444
1489
|
|
|
1445
1490
|
// src/cli/adapter/nanoclaw.ts
|
|
1446
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, existsSync as existsSync6, mkdirSync as mkdirSync6, copyFileSync as copyFileSync2 } from "fs";
|
|
1491
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, existsSync as existsSync6, mkdirSync as mkdirSync6, copyFileSync as copyFileSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
1447
1492
|
import { join as join6 } from "path";
|
|
1448
1493
|
import { homedir as homedir4 } from "os";
|
|
1449
1494
|
import { execSync as execSync3 } from "child_process";
|
|
1450
1495
|
var BACKUP_PATH3 = join6(homedir4(), ".become", "state", "original_nanoclaw.env");
|
|
1496
|
+
var PATCHED_ENV_PATH_FILE = join6(homedir4(), ".become", "state", "nanoclaw_env_path.txt");
|
|
1497
|
+
var NANOCLAW_URL_VAR = "ANTHROPIC_BASE_URL";
|
|
1451
1498
|
function patchNanoClaw(config) {
|
|
1452
1499
|
const envPath = findNanoClawEnv();
|
|
1453
1500
|
if (!envPath) {
|
|
1454
|
-
throw new Error(
|
|
1501
|
+
throw new Error(
|
|
1502
|
+
`Could not find NanoClaw .env file.
|
|
1503
|
+
NanoClaw stores .env in its project root (where you cloned it).
|
|
1504
|
+
Set ${NANOCLAW_URL_VAR}=http://127.0.0.1:${config.proxy_port} manually in your NanoClaw .env file.`
|
|
1505
|
+
);
|
|
1506
|
+
}
|
|
1507
|
+
if (existsSync6(BACKUP_PATH3)) {
|
|
1508
|
+
console.log("become is already connected to NanoClaw. Run `become off` first.");
|
|
1509
|
+
return;
|
|
1455
1510
|
}
|
|
1456
1511
|
mkdirSync6(join6(homedir4(), ".become", "state"), { recursive: true });
|
|
1457
1512
|
copyFileSync2(envPath, BACKUP_PATH3);
|
|
1513
|
+
writeFileSync6(PATCHED_ENV_PATH_FILE, envPath, "utf-8");
|
|
1458
1514
|
patchDotEnv2(envPath, {
|
|
1459
|
-
|
|
1515
|
+
[NANOCLAW_URL_VAR]: `http://127.0.0.1:${config.proxy_port}`
|
|
1460
1516
|
});
|
|
1517
|
+
console.log(` env file: ${envPath}`);
|
|
1518
|
+
console.log(` patched: ${NANOCLAW_URL_VAR} -> localhost:${config.proxy_port}`);
|
|
1461
1519
|
restartNanoClaw();
|
|
1462
1520
|
}
|
|
1463
1521
|
function restoreNanoClaw() {
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1522
|
+
if (!existsSync6(BACKUP_PATH3)) {
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
let envPath = null;
|
|
1526
|
+
if (existsSync6(PATCHED_ENV_PATH_FILE)) {
|
|
1527
|
+
envPath = readFileSync6(PATCHED_ENV_PATH_FILE, "utf-8").trim();
|
|
1528
|
+
}
|
|
1529
|
+
if (!envPath) {
|
|
1530
|
+
envPath = findNanoClawEnv();
|
|
1531
|
+
}
|
|
1532
|
+
if (!envPath) {
|
|
1533
|
+
console.log("Warning: Cannot find NanoClaw .env to restore. Backup is at " + BACKUP_PATH3);
|
|
1534
|
+
return;
|
|
1467
1535
|
}
|
|
1468
1536
|
copyFileSync2(BACKUP_PATH3, envPath);
|
|
1537
|
+
try {
|
|
1538
|
+
unlinkSync3(BACKUP_PATH3);
|
|
1539
|
+
} catch {
|
|
1540
|
+
}
|
|
1541
|
+
try {
|
|
1542
|
+
unlinkSync3(PATCHED_ENV_PATH_FILE);
|
|
1543
|
+
} catch {
|
|
1544
|
+
}
|
|
1469
1545
|
restartNanoClaw();
|
|
1470
1546
|
}
|
|
1471
1547
|
function findNanoClawEnv() {
|
|
1472
|
-
const candidates = [
|
|
1473
|
-
|
|
1474
|
-
join6(homedir4(), ".config", "nanoclaw", ".env")
|
|
1475
|
-
];
|
|
1476
|
-
const plistPath = join6(homedir4(), "Library", "LaunchAgents", "ai.nanoclaw.agent.plist");
|
|
1548
|
+
const candidates = [];
|
|
1549
|
+
const plistPath = join6(homedir4(), "Library", "LaunchAgents", "com.nanoclaw.plist");
|
|
1477
1550
|
if (existsSync6(plistPath)) {
|
|
1478
1551
|
try {
|
|
1479
1552
|
const plist = readFileSync6(plistPath, "utf-8");
|
|
1480
|
-
const match = plist.match(/<string>([^<]
|
|
1481
|
-
if (match) candidates.
|
|
1553
|
+
const match = plist.match(/<key>WorkingDirectory<\/key>\s*<string>([^<]+)<\/string>/);
|
|
1554
|
+
if (match) candidates.push(join6(match[1], ".env"));
|
|
1555
|
+
} catch {
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
const userUnit = join6(homedir4(), ".config", "systemd", "user", "nanoclaw.service");
|
|
1559
|
+
if (existsSync6(userUnit)) {
|
|
1560
|
+
try {
|
|
1561
|
+
const unit = readFileSync6(userUnit, "utf-8");
|
|
1562
|
+
const match = unit.match(/WorkingDirectory=(.+)/);
|
|
1563
|
+
if (match) candidates.push(join6(match[1].trim(), ".env"));
|
|
1564
|
+
} catch {
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
const rootUnit = "/etc/systemd/system/nanoclaw.service";
|
|
1568
|
+
if (existsSync6(rootUnit)) {
|
|
1569
|
+
try {
|
|
1570
|
+
const unit = readFileSync6(rootUnit, "utf-8");
|
|
1571
|
+
const match = unit.match(/WorkingDirectory=(.+)/);
|
|
1572
|
+
if (match) candidates.push(join6(match[1].trim(), ".env"));
|
|
1482
1573
|
} catch {
|
|
1483
1574
|
}
|
|
1484
1575
|
}
|
|
1576
|
+
candidates.push(join6(homedir4(), "nanoclaw", ".env"));
|
|
1577
|
+
candidates.push("/opt/nanoclaw/.env");
|
|
1485
1578
|
for (const path of candidates) {
|
|
1486
1579
|
if (existsSync6(path)) return path;
|
|
1487
1580
|
}
|
|
@@ -1489,35 +1582,207 @@ function findNanoClawEnv() {
|
|
|
1489
1582
|
}
|
|
1490
1583
|
function restartNanoClaw() {
|
|
1491
1584
|
console.log("Restarting NanoClaw...");
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
console.log("NanoClaw restarted.");
|
|
1495
|
-
} catch {
|
|
1585
|
+
const plistPath = join6(homedir4(), "Library", "LaunchAgents", "com.nanoclaw.plist");
|
|
1586
|
+
if (existsSync6(plistPath)) {
|
|
1496
1587
|
try {
|
|
1497
|
-
execSync3(
|
|
1588
|
+
execSync3(`launchctl unload "${plistPath}"`, { stdio: "pipe", timeout: 1e4 });
|
|
1589
|
+
execSync3(`launchctl load "${plistPath}"`, { stdio: "pipe", timeout: 1e4 });
|
|
1498
1590
|
console.log("NanoClaw restarted.");
|
|
1591
|
+
return;
|
|
1499
1592
|
} catch {
|
|
1500
|
-
console.log("\n*** NanoClaw needs a manual restart. ***");
|
|
1501
|
-
console.log("*** macOS: launchctl kickstart -k gui/$(id -u)/ai.nanoclaw.agent ***");
|
|
1502
|
-
console.log("*** Linux: systemctl --user restart nanoclaw ***\n");
|
|
1503
1593
|
}
|
|
1504
1594
|
}
|
|
1595
|
+
try {
|
|
1596
|
+
execSync3("systemctl --user restart nanoclaw", { stdio: "pipe", timeout: 1e4 });
|
|
1597
|
+
console.log("NanoClaw restarted.");
|
|
1598
|
+
return;
|
|
1599
|
+
} catch {
|
|
1600
|
+
}
|
|
1601
|
+
console.log("\n*** NanoClaw needs a manual restart. ***");
|
|
1602
|
+
console.log("*** macOS: launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist && launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist ***");
|
|
1603
|
+
console.log("*** Linux: systemctl --user restart nanoclaw ***\n");
|
|
1505
1604
|
}
|
|
1506
1605
|
function patchDotEnv2(path, vars) {
|
|
1507
|
-
let content = readFileSync6(path, "utf-8");
|
|
1606
|
+
let content = existsSync6(path) ? readFileSync6(path, "utf-8") : "";
|
|
1508
1607
|
for (const [key, value] of Object.entries(vars)) {
|
|
1509
1608
|
const regex = new RegExp(`^${key}=.*$`, "m");
|
|
1510
1609
|
if (regex.test(content)) {
|
|
1511
1610
|
content = content.replace(regex, `${key}=${value}`);
|
|
1512
1611
|
} else {
|
|
1513
|
-
content
|
|
1514
|
-
|
|
1612
|
+
content = content.trimEnd() + (content.length > 0 ? "\n" : "") + `${key}=${value}
|
|
1613
|
+
`;
|
|
1515
1614
|
}
|
|
1516
1615
|
}
|
|
1517
1616
|
writeFileSync6(path, content, "utf-8");
|
|
1518
1617
|
}
|
|
1519
1618
|
|
|
1619
|
+
// src/adapters/llm.ts
|
|
1620
|
+
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
1621
|
+
var OpenAIAdapter = class {
|
|
1622
|
+
apiKey;
|
|
1623
|
+
baseUrl;
|
|
1624
|
+
defaultModel;
|
|
1625
|
+
constructor(config) {
|
|
1626
|
+
if (!config.apiKey) throw new Error("OpenAI API key is required");
|
|
1627
|
+
this.apiKey = config.apiKey;
|
|
1628
|
+
this.baseUrl = (config.baseUrl ?? "https://api.openai.com").replace(/\/+$/, "");
|
|
1629
|
+
this.defaultModel = config.model ?? "gpt-4o-mini";
|
|
1630
|
+
}
|
|
1631
|
+
async complete(prompt, opts) {
|
|
1632
|
+
const response = await this.request({
|
|
1633
|
+
model: opts?.model ?? this.defaultModel,
|
|
1634
|
+
messages: [{ role: "user", content: prompt }],
|
|
1635
|
+
max_tokens: opts?.maxTokens ?? 2e3,
|
|
1636
|
+
temperature: opts?.temperature ?? 0.7
|
|
1637
|
+
}, opts?.timeoutMs);
|
|
1638
|
+
return response.choices?.[0]?.message?.content ?? "";
|
|
1639
|
+
}
|
|
1640
|
+
async json(prompt, opts) {
|
|
1641
|
+
const response = await this.request({
|
|
1642
|
+
model: opts?.model ?? this.defaultModel,
|
|
1643
|
+
messages: [{ role: "user", content: prompt }],
|
|
1644
|
+
max_tokens: opts?.maxTokens ?? 2e3,
|
|
1645
|
+
temperature: opts?.temperature ?? 0.3,
|
|
1646
|
+
response_format: { type: "json_object" }
|
|
1647
|
+
}, opts?.timeoutMs);
|
|
1648
|
+
const text = response.choices?.[0]?.message?.content ?? "{}";
|
|
1649
|
+
return JSON.parse(text);
|
|
1650
|
+
}
|
|
1651
|
+
async request(body, timeoutMs) {
|
|
1652
|
+
const res = await fetch(`${this.baseUrl}/v1/chat/completions`, {
|
|
1653
|
+
method: "POST",
|
|
1654
|
+
headers: {
|
|
1655
|
+
"Content-Type": "application/json",
|
|
1656
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
1657
|
+
},
|
|
1658
|
+
body: JSON.stringify(body),
|
|
1659
|
+
signal: AbortSignal.timeout(timeoutMs ?? DEFAULT_TIMEOUT_MS)
|
|
1660
|
+
});
|
|
1661
|
+
if (!res.ok) {
|
|
1662
|
+
const text = await res.text().catch(() => "unknown error");
|
|
1663
|
+
throw new Error(`OpenAI API error ${res.status}: ${text.slice(0, 200)}`);
|
|
1664
|
+
}
|
|
1665
|
+
return res.json();
|
|
1666
|
+
}
|
|
1667
|
+
};
|
|
1668
|
+
var AnthropicAdapter = class {
|
|
1669
|
+
apiKey;
|
|
1670
|
+
defaultModel;
|
|
1671
|
+
constructor(config) {
|
|
1672
|
+
if (!config.apiKey) throw new Error("Anthropic API key is required");
|
|
1673
|
+
this.apiKey = config.apiKey;
|
|
1674
|
+
this.defaultModel = config.model ?? "claude-sonnet-4-20250514";
|
|
1675
|
+
}
|
|
1676
|
+
async complete(prompt, opts) {
|
|
1677
|
+
const res = await fetch("https://api.anthropic.com/v1/messages", {
|
|
1678
|
+
method: "POST",
|
|
1679
|
+
headers: {
|
|
1680
|
+
"Content-Type": "application/json",
|
|
1681
|
+
"x-api-key": this.apiKey,
|
|
1682
|
+
"anthropic-version": "2023-06-01"
|
|
1683
|
+
},
|
|
1684
|
+
body: JSON.stringify({
|
|
1685
|
+
model: opts?.model ?? this.defaultModel,
|
|
1686
|
+
max_tokens: opts?.maxTokens ?? 2e3,
|
|
1687
|
+
messages: [{ role: "user", content: prompt }]
|
|
1688
|
+
}),
|
|
1689
|
+
signal: AbortSignal.timeout(opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS)
|
|
1690
|
+
});
|
|
1691
|
+
if (!res.ok) {
|
|
1692
|
+
const text = await res.text().catch(() => "unknown error");
|
|
1693
|
+
throw new Error(`Anthropic API error ${res.status}: ${text.slice(0, 200)}`);
|
|
1694
|
+
}
|
|
1695
|
+
const data = await res.json();
|
|
1696
|
+
return data.content?.[0]?.text ?? "";
|
|
1697
|
+
}
|
|
1698
|
+
async json(prompt, opts) {
|
|
1699
|
+
const text = await this.complete(
|
|
1700
|
+
`${prompt}
|
|
1701
|
+
|
|
1702
|
+
Respond with valid JSON only, no other text.`,
|
|
1703
|
+
{ ...opts, temperature: opts?.temperature ?? 0.3 }
|
|
1704
|
+
);
|
|
1705
|
+
try {
|
|
1706
|
+
return JSON.parse(text.trim());
|
|
1707
|
+
} catch {
|
|
1708
|
+
const match = text.match(/\{[\s\S]*?\}(?=\s*$|\s*[^}\]])/);
|
|
1709
|
+
const arrMatch = text.match(/\[[\s\S]*?\](?=\s*$|\s*[^}\]])/);
|
|
1710
|
+
const candidate = match?.[0] ?? arrMatch?.[0];
|
|
1711
|
+
if (!candidate) throw new Error("No JSON found in response");
|
|
1712
|
+
return JSON.parse(candidate);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
1716
|
+
var OllamaAdapter = class {
|
|
1717
|
+
baseUrl;
|
|
1718
|
+
defaultModel;
|
|
1719
|
+
constructor(config) {
|
|
1720
|
+
this.baseUrl = (config?.baseUrl ?? "http://localhost:11434").replace(/\/+$/, "");
|
|
1721
|
+
this.defaultModel = config?.model ?? "llama3.1";
|
|
1722
|
+
}
|
|
1723
|
+
async complete(prompt, opts) {
|
|
1724
|
+
const res = await fetch(`${this.baseUrl}/api/generate`, {
|
|
1725
|
+
method: "POST",
|
|
1726
|
+
headers: { "Content-Type": "application/json" },
|
|
1727
|
+
body: JSON.stringify({
|
|
1728
|
+
model: opts?.model ?? this.defaultModel,
|
|
1729
|
+
prompt,
|
|
1730
|
+
stream: false,
|
|
1731
|
+
options: {
|
|
1732
|
+
num_predict: opts?.maxTokens ?? 2e3,
|
|
1733
|
+
temperature: opts?.temperature ?? 0.7
|
|
1734
|
+
}
|
|
1735
|
+
}),
|
|
1736
|
+
signal: AbortSignal.timeout(opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS)
|
|
1737
|
+
});
|
|
1738
|
+
if (!res.ok) {
|
|
1739
|
+
const text = await res.text().catch(() => "unknown error");
|
|
1740
|
+
throw new Error(`Ollama error ${res.status}: ${text.slice(0, 200)}`);
|
|
1741
|
+
}
|
|
1742
|
+
const data = await res.json();
|
|
1743
|
+
return data.response ?? "";
|
|
1744
|
+
}
|
|
1745
|
+
async json(prompt, opts) {
|
|
1746
|
+
const res = await fetch(`${this.baseUrl}/api/generate`, {
|
|
1747
|
+
method: "POST",
|
|
1748
|
+
headers: { "Content-Type": "application/json" },
|
|
1749
|
+
body: JSON.stringify({
|
|
1750
|
+
model: opts?.model ?? this.defaultModel,
|
|
1751
|
+
prompt: `${prompt}
|
|
1752
|
+
|
|
1753
|
+
Respond with valid JSON only.`,
|
|
1754
|
+
stream: false,
|
|
1755
|
+
format: "json",
|
|
1756
|
+
options: {
|
|
1757
|
+
num_predict: opts?.maxTokens ?? 2e3,
|
|
1758
|
+
temperature: opts?.temperature ?? 0.3
|
|
1759
|
+
}
|
|
1760
|
+
}),
|
|
1761
|
+
signal: AbortSignal.timeout(opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS)
|
|
1762
|
+
});
|
|
1763
|
+
if (!res.ok) {
|
|
1764
|
+
const text = await res.text().catch(() => "unknown error");
|
|
1765
|
+
throw new Error(`Ollama error ${res.status}: ${text.slice(0, 200)}`);
|
|
1766
|
+
}
|
|
1767
|
+
const data = await res.json();
|
|
1768
|
+
return JSON.parse(data.response ?? "{}");
|
|
1769
|
+
}
|
|
1770
|
+
};
|
|
1771
|
+
|
|
1520
1772
|
// src/cli/commands.ts
|
|
1773
|
+
function createAnalyzer(config) {
|
|
1774
|
+
switch (config.llm_provider) {
|
|
1775
|
+
case "anthropic":
|
|
1776
|
+
return { analyze: (p) => new AnthropicAdapter({ apiKey: config.llm_api_key }).complete(p) };
|
|
1777
|
+
case "ollama":
|
|
1778
|
+
return { analyze: (p) => new OllamaAdapter({ baseUrl: config.llm_base_url }).complete(p) };
|
|
1779
|
+
case "openai":
|
|
1780
|
+
case "openrouter":
|
|
1781
|
+
case "custom":
|
|
1782
|
+
default:
|
|
1783
|
+
return { analyze: (p) => new OpenAIAdapter({ apiKey: config.llm_api_key, baseUrl: config.llm_base_url }).complete(p) };
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1521
1786
|
async function start() {
|
|
1522
1787
|
const config = loadConfig();
|
|
1523
1788
|
const baseDir = getBecomeDir();
|
|
@@ -1530,7 +1795,14 @@ async function start() {
|
|
|
1530
1795
|
max_skills_per_call: config.max_skills_per_call,
|
|
1531
1796
|
auto_extract: config.auto_extract
|
|
1532
1797
|
};
|
|
1533
|
-
const
|
|
1798
|
+
const originalUrlPath = join7(homedir5(), ".become", "state", "original_base_url.txt");
|
|
1799
|
+
let originalUpstreamUrl;
|
|
1800
|
+
if (existsSync7(originalUrlPath)) {
|
|
1801
|
+
const saved = readFileSync7(originalUrlPath, "utf-8").trim();
|
|
1802
|
+
if (saved) originalUpstreamUrl = saved;
|
|
1803
|
+
}
|
|
1804
|
+
const analyzer = createAnalyzer(config);
|
|
1805
|
+
const proxy = createProxyServer(proxyConfig, analyzer, originalUpstreamUrl);
|
|
1534
1806
|
await proxy.listen();
|
|
1535
1807
|
const dashboard = createDashboardServer({
|
|
1536
1808
|
store: proxy.store,
|
|
@@ -1585,15 +1857,24 @@ Connected to: ${config.agent_type}${config.openclaw_agent_id ? ` (agent: ${confi
|
|
|
1585
1857
|
console.log(`[become] ${s.requests_forwarded} requests forwarded, ${s.skills_injected} skills injected, ${s.lessons_extracted} lessons extracted`);
|
|
1586
1858
|
}
|
|
1587
1859
|
}, 6e4);
|
|
1588
|
-
|
|
1860
|
+
let shuttingDown = false;
|
|
1861
|
+
const shutdown = () => {
|
|
1862
|
+
if (shuttingDown) {
|
|
1863
|
+
process.exit(0);
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
shuttingDown = true;
|
|
1589
1867
|
clearInterval(activityInterval);
|
|
1590
1868
|
console.log("\nShutting down...");
|
|
1591
1869
|
try {
|
|
1592
1870
|
turnOff();
|
|
1593
1871
|
} catch {
|
|
1594
1872
|
}
|
|
1595
|
-
|
|
1596
|
-
|
|
1873
|
+
proxy.close().catch(() => {
|
|
1874
|
+
});
|
|
1875
|
+
dashboard.close().catch(() => {
|
|
1876
|
+
});
|
|
1877
|
+
setTimeout(() => process.exit(0), 500);
|
|
1597
1878
|
};
|
|
1598
1879
|
process.on("SIGINT", shutdown);
|
|
1599
1880
|
process.on("SIGTERM", shutdown);
|
|
@@ -1605,7 +1886,7 @@ Patching ${config.agent_type} config...`);
|
|
|
1605
1886
|
console.log(` baseUrl: ${config.llm_base_url} \u2192 localhost:${config.proxy_port}`);
|
|
1606
1887
|
switch (config.agent_type) {
|
|
1607
1888
|
case "openclaw":
|
|
1608
|
-
patchOpenClaw(config
|
|
1889
|
+
patchOpenClaw(config);
|
|
1609
1890
|
break;
|
|
1610
1891
|
case "ironclaw":
|
|
1611
1892
|
patchIronClaw(config);
|
|
@@ -1674,20 +1955,20 @@ Skills: ${approved} approved, ${pending} pending, ${rejected} rejected`);
|
|
|
1674
1955
|
}
|
|
1675
1956
|
|
|
1676
1957
|
// src/cli/init.ts
|
|
1677
|
-
import { readFileSync as
|
|
1678
|
-
import { join as
|
|
1958
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
1959
|
+
import { join as join8, dirname as dirname2 } from "path";
|
|
1679
1960
|
import { fileURLToPath } from "url";
|
|
1680
1961
|
var command = process.argv[2];
|
|
1681
1962
|
var VERSION = "unknown";
|
|
1682
1963
|
try {
|
|
1683
1964
|
const dir = dirname2(fileURLToPath(import.meta.url));
|
|
1684
|
-
const pkgPath =
|
|
1685
|
-
VERSION = JSON.parse(
|
|
1965
|
+
const pkgPath = join8(dir, "..", "package.json");
|
|
1966
|
+
VERSION = JSON.parse(readFileSync8(pkgPath, "utf-8")).version;
|
|
1686
1967
|
} catch {
|
|
1687
1968
|
try {
|
|
1688
1969
|
const dir = dirname2(fileURLToPath(import.meta.url));
|
|
1689
|
-
const pkgPath =
|
|
1690
|
-
VERSION = JSON.parse(
|
|
1970
|
+
const pkgPath = join8(dir, "..", "..", "package.json");
|
|
1971
|
+
VERSION = JSON.parse(readFileSync8(pkgPath, "utf-8")).version;
|
|
1691
1972
|
} catch {
|
|
1692
1973
|
}
|
|
1693
1974
|
}
|