@openclawcity/become 1.0.1 → 1.0.3

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.js CHANGED
@@ -53,6 +53,157 @@ var LLM_DEFAULTS = {
53
53
  openrouter: { base_url: "https://openrouter.ai/api" }
54
54
  };
55
55
 
56
+ // src/cli/adapter/openclaw.ts
57
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
58
+ import { join as join2 } from "path";
59
+ import { homedir as homedir2 } from "os";
60
+ import { execSync } from "child_process";
61
+ var OPENCLAW_CONFIG = join2(homedir2(), ".openclaw", "openclaw.json");
62
+ var BACKUP_PATH = join2(homedir2(), ".become", "state", "original_openclaw.json");
63
+ var ORIGINAL_MODEL_PATH = join2(homedir2(), ".become", "state", "original_model.txt");
64
+ var PATCHED_AGENT_PATH = join2(homedir2(), ".become", "state", "patched_agent.txt");
65
+ function patchOpenClaw(config, agentId) {
66
+ if (!existsSync2(OPENCLAW_CONFIG)) {
67
+ throw new Error(`OpenClaw config not found at ${OPENCLAW_CONFIG}`);
68
+ }
69
+ const raw = readFileSync2(OPENCLAW_CONFIG, "utf-8");
70
+ const clawConfig = parseOpenClawConfig(raw);
71
+ if (clawConfig.models?.providers?.become) {
72
+ console.log("become is already connected. Run `become off` first to disconnect.");
73
+ return;
74
+ }
75
+ mkdirSync2(join2(homedir2(), ".become", "state"), { recursive: true });
76
+ writeFileSync2(BACKUP_PATH, raw, "utf-8");
77
+ const agents = clawConfig.agents?.list ?? [];
78
+ let originalModel;
79
+ let patchedAgentId;
80
+ if (agents.length > 0 && agentId) {
81
+ const agent = agents.find((a) => a.id === agentId);
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}`;
92
+ } else {
93
+ originalModel = clawConfig.agents?.defaults?.model?.primary ?? "";
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
+ try {
119
+ execSync("openclaw gateway restart", { stdio: "pipe", timeout: 15e3 });
120
+ } catch {
121
+ console.log("Warning: Could not restart OpenClaw gateway. Restart it manually: openclaw gateway restart");
122
+ }
123
+ }
124
+ function restoreOpenClaw() {
125
+ if (!existsSync2(OPENCLAW_CONFIG)) {
126
+ throw new Error(`OpenClaw config not found at ${OPENCLAW_CONFIG}`);
127
+ }
128
+ if (existsSync2(BACKUP_PATH)) {
129
+ const backup = readFileSync2(BACKUP_PATH, "utf-8");
130
+ const backupConfig = parseOpenClawConfig(backup);
131
+ if (!backupConfig.models?.providers?.become) {
132
+ writeFileSync2(OPENCLAW_CONFIG, backup, "utf-8");
133
+ restartGateway();
134
+ return;
135
+ }
136
+ }
137
+ const raw = readFileSync2(OPENCLAW_CONFIG, "utf-8");
138
+ const config = parseOpenClawConfig(raw);
139
+ const patchedAgentId = readStateFile(PATCHED_AGENT_PATH);
140
+ const originalModel = readStateFile(ORIGINAL_MODEL_PATH);
141
+ if (originalModel) {
142
+ const agents = config.agents?.list ?? [];
143
+ if (patchedAgentId && patchedAgentId !== "_defaults") {
144
+ const agent = agents.find((a) => a.id === patchedAgentId);
145
+ if (agent) {
146
+ agent.model = originalModel;
147
+ }
148
+ } else {
149
+ if (config.agents?.defaults?.model) {
150
+ config.agents.defaults.model.primary = originalModel;
151
+ }
152
+ }
153
+ }
154
+ if (config.models?.providers?.become) {
155
+ delete config.models.providers.become;
156
+ }
157
+ for (const provider of Object.values(config.models?.providers ?? {})) {
158
+ if (provider && typeof provider === "object" && "_originalModel" in provider) {
159
+ delete provider._originalModel;
160
+ }
161
+ }
162
+ writeFileSync2(OPENCLAW_CONFIG, JSON.stringify(config, null, 2), "utf-8");
163
+ restartGateway();
164
+ }
165
+ function listOpenClawAgents() {
166
+ if (!existsSync2(OPENCLAW_CONFIG)) return [];
167
+ try {
168
+ const config = parseOpenClawConfig(readFileSync2(OPENCLAW_CONFIG, "utf-8"));
169
+ const agents = config.agents?.list ?? [];
170
+ const defaultModel = unbecome(config.agents?.defaults?.model?.primary ?? "unknown");
171
+ if (agents.length === 0) {
172
+ return [{ id: "_defaults", model: defaultModel }];
173
+ }
174
+ return agents.map((a) => ({
175
+ id: a.id,
176
+ model: unbecome(a.model ?? defaultModel)
177
+ }));
178
+ } catch {
179
+ return [];
180
+ }
181
+ }
182
+ function stripProvider(model) {
183
+ return model.includes("/") ? model.split("/").slice(1).join("/") : model;
184
+ }
185
+ function unbecome(model) {
186
+ return model.startsWith("become/") ? model.replace("become/", "") : model;
187
+ }
188
+ function parseOpenClawConfig(raw) {
189
+ const stripped = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/,\s*([\]}])/g, "$1");
190
+ return JSON.parse(stripped);
191
+ }
192
+ function readStateFile(path) {
193
+ try {
194
+ return existsSync2(path) ? readFileSync2(path, "utf-8").trim() : "";
195
+ } catch {
196
+ return "";
197
+ }
198
+ }
199
+ function restartGateway() {
200
+ try {
201
+ execSync("openclaw gateway restart", { stdio: "pipe", timeout: 15e3 });
202
+ } catch {
203
+ console.log("Warning: Could not restart OpenClaw gateway. Restart it manually: openclaw gateway restart");
204
+ }
205
+ }
206
+
56
207
  // src/cli/setup.ts
57
208
  var AGENT_TYPES = ["openclaw", "ironclaw", "nanoclaw", "generic"];
58
209
  var LLM_PROVIDERS = ["anthropic", "openai", "ollama", "openrouter", "custom"];
@@ -68,7 +219,25 @@ async function runSetup() {
68
219
  const agentChoice = await ask(rl, "> ");
69
220
  const agentIdx = parseInt(agentChoice, 10) - 1;
70
221
  const agent_type = AGENT_TYPES[agentIdx] ?? "openclaw";
71
- console.log("\nWhich LLM provider?");
222
+ let openclaw_agent_id;
223
+ if (agent_type === "openclaw") {
224
+ const agents = listOpenClawAgents();
225
+ if (agents.length > 1) {
226
+ console.log("\nWhich OpenClaw agent should learn from other agents?");
227
+ agents.forEach((a, i) => console.log(` ${i + 1}. ${a.id} (${a.model})`));
228
+ const agentPick = await ask(rl, "> ");
229
+ const pickIdx = parseInt(agentPick, 10) - 1;
230
+ const picked = agents[pickIdx];
231
+ if (picked && picked.id !== "_defaults") {
232
+ openclaw_agent_id = picked.id;
233
+ }
234
+ } else if (agents.length === 1 && agents[0].id !== "_defaults") {
235
+ openclaw_agent_id = agents[0].id;
236
+ console.log(`
237
+ OpenClaw agent: ${openclaw_agent_id} (${agents[0].model})`);
238
+ }
239
+ }
240
+ console.log("\nWhich LLM provider does your agent use?");
72
241
  LLM_PROVIDERS.forEach((p, i) => console.log(` ${i + 1}. ${p}`));
73
242
  const llmChoice = await ask(rl, "> ");
74
243
  const llmIdx = parseInt(llmChoice, 10) - 1;
@@ -90,6 +259,7 @@ Proxy port (default 30001): `);
90
259
  const dashboard_port = parseInt(dashInput, 10) || 30002;
91
260
  const config = {
92
261
  agent_type,
262
+ openclaw_agent_id,
93
263
  llm_provider,
94
264
  llm_base_url,
95
265
  llm_api_key: llm_api_key.trim(),
@@ -113,20 +283,20 @@ Proxy port (default 30001): `);
113
283
  import { createServer } from "http";
114
284
 
115
285
  // src/skills/store.ts
116
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, readdirSync, mkdirSync as mkdirSync2, renameSync, unlinkSync, existsSync as existsSync2 } from "fs";
117
- import { join as join2, basename } from "path";
286
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, readdirSync, mkdirSync as mkdirSync3, renameSync, unlinkSync, existsSync as existsSync3 } from "fs";
287
+ import { join as join3, basename } from "path";
118
288
  import { createHash } from "crypto";
119
289
  var FileSkillStore = class {
120
290
  skillsDir;
121
291
  pendingDir;
122
292
  rejectedDir;
123
293
  constructor(config) {
124
- this.skillsDir = join2(config.baseDir, "skills");
125
- this.pendingDir = join2(config.baseDir, "pending");
126
- this.rejectedDir = join2(config.baseDir, "rejected");
127
- mkdirSync2(this.skillsDir, { recursive: true });
128
- mkdirSync2(this.pendingDir, { recursive: true });
129
- mkdirSync2(this.rejectedDir, { recursive: true });
294
+ this.skillsDir = join3(config.baseDir, "skills");
295
+ this.pendingDir = join3(config.baseDir, "pending");
296
+ this.rejectedDir = join3(config.baseDir, "rejected");
297
+ mkdirSync3(this.skillsDir, { recursive: true });
298
+ mkdirSync3(this.pendingDir, { recursive: true });
299
+ mkdirSync3(this.rejectedDir, { recursive: true });
130
300
  }
131
301
  // ── Read ────────────────────────────────────────────────────────────────
132
302
  listApproved() {
@@ -140,7 +310,7 @@ var FileSkillStore = class {
140
310
  }
141
311
  getApproved(id) {
142
312
  this.validateId(id);
143
- return this.readFile(join2(this.skillsDir, `${id}.md`));
313
+ return this.readFile(join3(this.skillsDir, `${id}.md`));
144
314
  }
145
315
  // ── Write ───────────────────────────────────────────────────────────────
146
316
  savePending(lesson) {
@@ -151,42 +321,42 @@ var FileSkillStore = class {
151
321
  }
152
322
  const id = this.generateId(lesson.name);
153
323
  const file = { ...lesson, id, approved_at: void 0 };
154
- this.writeFile(join2(this.pendingDir, `${id}.md`), file);
324
+ this.writeFile(join3(this.pendingDir, `${id}.md`), file);
155
325
  return file;
156
326
  }
157
327
  approve(id) {
158
328
  this.validateId(id);
159
- const src = join2(this.pendingDir, `${id}.md`);
160
- if (!existsSync2(src)) return false;
329
+ const src = join3(this.pendingDir, `${id}.md`);
330
+ if (!existsSync3(src)) return false;
161
331
  const skill = this.readFile(src);
162
332
  if (!skill) return false;
163
333
  skill.approved_at = (/* @__PURE__ */ new Date()).toISOString();
164
- const dest = join2(this.skillsDir, `${id}.md`);
334
+ const dest = join3(this.skillsDir, `${id}.md`);
165
335
  this.writeFile(dest, skill);
166
336
  unlinkSync(src);
167
337
  return true;
168
338
  }
169
339
  reject(id) {
170
340
  this.validateId(id);
171
- const src = join2(this.pendingDir, `${id}.md`);
172
- if (!existsSync2(src)) return false;
173
- const dest = join2(this.rejectedDir, `${id}.md`);
341
+ const src = join3(this.pendingDir, `${id}.md`);
342
+ if (!existsSync3(src)) return false;
343
+ const dest = join3(this.rejectedDir, `${id}.md`);
174
344
  renameSync(src, dest);
175
345
  return true;
176
346
  }
177
347
  disable(id) {
178
348
  this.validateId(id);
179
- const src = join2(this.skillsDir, `${id}.md`);
180
- if (!existsSync2(src)) return false;
181
- const dest = join2(this.rejectedDir, `${id}.md`);
349
+ const src = join3(this.skillsDir, `${id}.md`);
350
+ if (!existsSync3(src)) return false;
351
+ const dest = join3(this.rejectedDir, `${id}.md`);
182
352
  renameSync(src, dest);
183
353
  return true;
184
354
  }
185
355
  remove(id) {
186
356
  this.validateId(id);
187
357
  for (const dir of [this.skillsDir, this.pendingDir, this.rejectedDir]) {
188
- const path = join2(dir, `${id}.md`);
189
- if (existsSync2(path)) {
358
+ const path = join3(dir, `${id}.md`);
359
+ if (existsSync3(path)) {
190
360
  unlinkSync(path);
191
361
  return true;
192
362
  }
@@ -205,19 +375,19 @@ var FileSkillStore = class {
205
375
  }
206
376
  }
207
377
  readDir(dir) {
208
- if (!existsSync2(dir)) return [];
378
+ if (!existsSync3(dir)) return [];
209
379
  const files = readdirSync(dir).filter((f) => f.endsWith(".md"));
210
380
  const skills = [];
211
381
  for (const f of files) {
212
- const skill = this.readFile(join2(dir, f));
382
+ const skill = this.readFile(join3(dir, f));
213
383
  if (skill) skills.push(skill);
214
384
  }
215
385
  return skills.sort((a, b) => b.created_at.localeCompare(a.created_at));
216
386
  }
217
387
  readFile(path) {
218
- if (!existsSync2(path)) return null;
388
+ if (!existsSync3(path)) return null;
219
389
  try {
220
- const content = readFileSync2(path, "utf-8");
390
+ const content = readFileSync3(path, "utf-8");
221
391
  return this.parseSkillFile(content, basename(path, ".md"));
222
392
  } catch {
223
393
  return null;
@@ -225,7 +395,7 @@ var FileSkillStore = class {
225
395
  }
226
396
  writeFile(path, skill) {
227
397
  const content = this.formatSkillFile(skill);
228
- writeFileSync2(path, content, "utf-8");
398
+ writeFileSync3(path, content, "utf-8");
229
399
  }
230
400
  parseSkillFile(content, id) {
231
401
  const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
@@ -274,8 +444,8 @@ var FileSkillStore = class {
274
444
  };
275
445
 
276
446
  // src/skills/trust.ts
277
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3, mkdirSync as mkdirSync3 } from "fs";
278
- import { join as join3, dirname } from "path";
447
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync4, mkdirSync as mkdirSync4 } from "fs";
448
+ import { join as join4, dirname } from "path";
279
449
  var DEFAULT_TRUST = {
280
450
  trusted: [],
281
451
  blocked: [],
@@ -292,9 +462,9 @@ var TrustManager = class {
292
462
  config;
293
463
  dailyCounts;
294
464
  constructor(baseDir) {
295
- this.trustPath = join3(baseDir, "trust.json");
296
- this.statsPath = join3(baseDir, "state", "daily_counts.json");
297
- mkdirSync3(join3(baseDir, "state"), { recursive: true });
465
+ this.trustPath = join4(baseDir, "trust.json");
466
+ this.statsPath = join4(baseDir, "state", "daily_counts.json");
467
+ mkdirSync4(join4(baseDir, "state"), { recursive: true });
298
468
  this.config = this.loadTrust();
299
469
  this.dailyCounts = this.loadDailyCounts();
300
470
  }
@@ -337,9 +507,9 @@ var TrustManager = class {
337
507
  }
338
508
  // ── Private ─────────────────────────────────────────────────────────────
339
509
  loadTrust() {
340
- if (!existsSync3(this.trustPath)) return { ...DEFAULT_TRUST, trusted: [], blocked: [] };
510
+ if (!existsSync4(this.trustPath)) return { ...DEFAULT_TRUST, trusted: [], blocked: [] };
341
511
  try {
342
- const raw = JSON.parse(readFileSync3(this.trustPath, "utf-8"));
512
+ const raw = JSON.parse(readFileSync4(this.trustPath, "utf-8"));
343
513
  return {
344
514
  trusted: Array.isArray(raw.trusted) ? raw.trusted.filter((a) => typeof a === "string") : [],
345
515
  blocked: Array.isArray(raw.blocked) ? raw.blocked.filter((a) => typeof a === "string") : [],
@@ -350,14 +520,14 @@ var TrustManager = class {
350
520
  }
351
521
  }
352
522
  saveTrust() {
353
- mkdirSync3(dirname(this.trustPath), { recursive: true });
354
- writeFileSync3(this.trustPath, JSON.stringify(this.config, null, 2), "utf-8");
523
+ mkdirSync4(dirname(this.trustPath), { recursive: true });
524
+ writeFileSync4(this.trustPath, JSON.stringify(this.config, null, 2), "utf-8");
355
525
  }
356
526
  loadDailyCounts() {
357
527
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
358
- if (!existsSync3(this.statsPath)) return { date: today, total: 0, perAgent: {} };
528
+ if (!existsSync4(this.statsPath)) return { date: today, total: 0, perAgent: {} };
359
529
  try {
360
- const data = JSON.parse(readFileSync3(this.statsPath, "utf-8"));
530
+ const data = JSON.parse(readFileSync4(this.statsPath, "utf-8"));
361
531
  if (data.date !== today) return { date: today, total: 0, perAgent: {} };
362
532
  return data;
363
533
  } catch {
@@ -365,7 +535,7 @@ var TrustManager = class {
365
535
  }
366
536
  }
367
537
  saveDailyCounts() {
368
- writeFileSync3(this.statsPath, JSON.stringify(this.dailyCounts, null, 2), "utf-8");
538
+ writeFileSync4(this.statsPath, JSON.stringify(this.dailyCounts, null, 2), "utf-8");
369
539
  }
370
540
  refreshDailyCountsIfNewDay() {
371
541
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -1212,54 +1382,6 @@ function readBody2(req) {
1212
1382
  });
1213
1383
  }
1214
1384
 
1215
- // src/cli/adapter/openclaw.ts
1216
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync4, mkdirSync as mkdirSync4 } from "fs";
1217
- import { join as join4 } from "path";
1218
- import { homedir as homedir2 } from "os";
1219
- import { execSync } from "child_process";
1220
- var OPENCLAW_CONFIG = join4(homedir2(), ".openclaw", "openclaw.json");
1221
- var BACKUP_PATH = join4(homedir2(), ".become", "state", "original_openclaw.json");
1222
- function patchOpenClaw(config) {
1223
- if (!existsSync4(OPENCLAW_CONFIG)) {
1224
- throw new Error(`OpenClaw config not found at ${OPENCLAW_CONFIG}`);
1225
- }
1226
- const raw = readFileSync4(OPENCLAW_CONFIG, "utf-8");
1227
- const clawConfig = JSON.parse(raw);
1228
- mkdirSync4(join4(homedir2(), ".become", "state"), { recursive: true });
1229
- writeFileSync4(BACKUP_PATH, raw, "utf-8");
1230
- if (!clawConfig.models) clawConfig.models = {};
1231
- if (!clawConfig.models.providers) clawConfig.models.providers = {};
1232
- clawConfig.models.providers.become = {
1233
- api: "anthropic-messages",
1234
- baseUrl: `http://127.0.0.1:${config.proxy_port}`,
1235
- apiKey: config.llm_api_key
1236
- };
1237
- if (clawConfig.agents?.defaults?.model?.primary) {
1238
- const original = clawConfig.agents.defaults.model.primary;
1239
- clawConfig.models.providers.become._originalModel = original;
1240
- const modelId = original.includes("/") ? original.split("/").slice(1).join("/") : original;
1241
- clawConfig.agents.defaults.model.primary = `become/${modelId}`;
1242
- }
1243
- writeFileSync4(OPENCLAW_CONFIG, JSON.stringify(clawConfig, null, 2), "utf-8");
1244
- try {
1245
- execSync("openclaw gateway restart", { stdio: "pipe", timeout: 15e3 });
1246
- } catch {
1247
- console.log("Warning: Could not restart OpenClaw gateway. Restart it manually: openclaw gateway restart");
1248
- }
1249
- }
1250
- function restoreOpenClaw() {
1251
- if (!existsSync4(BACKUP_PATH)) {
1252
- throw new Error("No backup found. Was become ever turned on?");
1253
- }
1254
- const backup = readFileSync4(BACKUP_PATH, "utf-8");
1255
- writeFileSync4(OPENCLAW_CONFIG, backup, "utf-8");
1256
- try {
1257
- execSync("openclaw gateway restart", { stdio: "pipe", timeout: 15e3 });
1258
- } catch {
1259
- console.log("Warning: Could not restart OpenClaw gateway. Restart it manually: openclaw gateway restart");
1260
- }
1261
- }
1262
-
1263
1385
  // src/cli/adapter/ironclaw.ts
1264
1386
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync5, mkdirSync as mkdirSync5, copyFileSync } from "fs";
1265
1387
  import { join as join5 } from "path";
@@ -1443,7 +1565,7 @@ Patching ${config.agent_type} config...`);
1443
1565
  console.log(` baseUrl: ${config.llm_base_url} \u2192 localhost:${config.proxy_port}`);
1444
1566
  switch (config.agent_type) {
1445
1567
  case "openclaw":
1446
- patchOpenClaw(config);
1568
+ patchOpenClaw(config, config.openclaw_agent_id);
1447
1569
  break;
1448
1570
  case "ironclaw":
1449
1571
  patchIronClaw(config);