clawcompany 0.1.0

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.
Files changed (2) hide show
  1. package/dist/index.js +1673 -0
  2. package/package.json +43 -0
package/dist/index.js ADDED
@@ -0,0 +1,1673 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // src/utils.ts
13
+ import { join } from "path";
14
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
15
+ function banner() {
16
+ console.log("");
17
+ console.log(" \u{1F99E} ClawCompany v0.1.0");
18
+ console.log(" Build for OPC. Every human being is a chairman.");
19
+ console.log("");
20
+ }
21
+ function getConfigDir() {
22
+ const dir = join(process.env.HOME ?? "~", ".clawcompany");
23
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
24
+ return dir;
25
+ }
26
+ function getConfigPath() {
27
+ return join(getConfigDir(), "config.json");
28
+ }
29
+ function configExists() {
30
+ return existsSync(getConfigPath());
31
+ }
32
+ function readConfig() {
33
+ if (!configExists()) return null;
34
+ try {
35
+ return JSON.parse(readFileSync(getConfigPath(), "utf-8"));
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+ function writeConfig(config) {
41
+ writeFileSync(getConfigPath(), JSON.stringify(config, null, 2));
42
+ }
43
+ async function apiGet(path, port = 3200) {
44
+ const res = await fetch(`http://localhost:${port}${path}`);
45
+ if (!res.ok) throw new Error(`API error: ${res.status}`);
46
+ return res.json();
47
+ }
48
+ async function apiPost(path, body, port = 3200) {
49
+ const res = await fetch(`http://localhost:${port}${path}`, {
50
+ method: "POST",
51
+ headers: { "Content-Type": "application/json" },
52
+ body: JSON.stringify(body)
53
+ });
54
+ if (!res.ok) {
55
+ const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
56
+ throw new Error(err.error ?? `API error: ${res.status}`);
57
+ }
58
+ return res.json();
59
+ }
60
+ async function validateClawApiKey(key) {
61
+ try {
62
+ const res = await fetch("https://clawapi.org/api/v1/chat/completions", {
63
+ method: "POST",
64
+ headers: {
65
+ "Authorization": `Bearer ${key}`,
66
+ "Content-Type": "application/json"
67
+ },
68
+ body: JSON.stringify({
69
+ model: "gpt-oss-20b",
70
+ messages: [{ role: "user", content: "hi" }],
71
+ max_tokens: 5
72
+ })
73
+ });
74
+ if (res.status === 401) return { valid: false, error: "Invalid key. Check your key at clawapi.org" };
75
+ if (!res.ok) return { valid: false, error: `ClawAPI returned ${res.status}. Try again later.` };
76
+ return { valid: true };
77
+ } catch (err) {
78
+ return { valid: false, error: `Cannot reach ClawAPI: ${err.message}. Check your internet connection.` };
79
+ }
80
+ }
81
+ async function isServerRunning(port = 3200) {
82
+ try {
83
+ const res = await fetch(`http://localhost:${port}/api/health`);
84
+ return res.ok;
85
+ } catch {
86
+ return false;
87
+ }
88
+ }
89
+ var init_utils = __esm({
90
+ "src/utils.ts"() {
91
+ "use strict";
92
+ }
93
+ });
94
+
95
+ // ../packages/shared/src/types.ts
96
+ var init_types = __esm({
97
+ "../packages/shared/src/types.ts"() {
98
+ "use strict";
99
+ }
100
+ });
101
+
102
+ // ../packages/shared/src/defaults.ts
103
+ function getDefaultConfig() {
104
+ const rolesMap = {};
105
+ for (const role2 of BUILTIN_ROLES) {
106
+ rolesMap[role2.id] = { model: role2.model, provider: role2.provider };
107
+ }
108
+ return {
109
+ version: "1.0",
110
+ providers: [DEFAULT_CLAWAPI_PROVIDER],
111
+ roles: rolesMap,
112
+ fallbackChain: DEFAULT_FALLBACK_CHAIN
113
+ };
114
+ }
115
+ function getBuiltinRole(id) {
116
+ return BUILTIN_ROLES.find((r) => r.id === id);
117
+ }
118
+ function resolveRoles(config) {
119
+ const resolved = [];
120
+ for (const [id, overrides] of Object.entries(config.roles)) {
121
+ const builtin = getBuiltinRole(id);
122
+ if (builtin) {
123
+ resolved.push({ ...builtin, ...overrides, id, isBuiltin: true, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
124
+ } else {
125
+ if (!overrides.name || !overrides.model) {
126
+ throw new Error(`Custom role "${id}" must have at least "name" and "model"`);
127
+ }
128
+ resolved.push({
129
+ id,
130
+ name: overrides.name,
131
+ description: overrides.description ?? "",
132
+ systemPrompt: overrides.systemPrompt ?? `You are ${overrides.name}.`,
133
+ model: overrides.model,
134
+ provider: overrides.provider ?? config.providers[0]?.id ?? "clawapi",
135
+ reportsTo: overrides.reportsTo ?? "ceo",
136
+ canDelegateTo: overrides.canDelegateTo ?? [],
137
+ canEscalateTo: overrides.canEscalateTo ?? [overrides.reportsTo ?? "ceo"],
138
+ budgetTier: overrides.budgetTier ?? "save",
139
+ budgetMonthly: overrides.budgetMonthly ?? null,
140
+ maxTokensPerTask: overrides.maxTokensPerTask ?? null,
141
+ tools: overrides.tools ?? ["filesystem", "http"],
142
+ skills: overrides.skills ?? [],
143
+ isBuiltin: false,
144
+ isActive: overrides.isActive ?? true,
145
+ heartbeatInterval: overrides.heartbeatInterval ?? 0,
146
+ createdAt: overrides.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
147
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
148
+ });
149
+ }
150
+ }
151
+ return resolved;
152
+ }
153
+ var DEFAULT_CLAWAPI_PROVIDER, BUILTIN_ROLES, DEFAULT_FALLBACK_CHAIN, MODEL_PRICING;
154
+ var init_defaults = __esm({
155
+ "../packages/shared/src/defaults.ts"() {
156
+ "use strict";
157
+ DEFAULT_CLAWAPI_PROVIDER = {
158
+ id: "clawapi",
159
+ name: "ClawAPI",
160
+ type: "openai-compatible",
161
+ baseUrl: "https://clawapi.org/api/v1",
162
+ apiKey: "${CLAWAPI_KEY}",
163
+ isDefault: true,
164
+ models: "auto",
165
+ features: {
166
+ cryptoPayment: true,
167
+ multiModel: true,
168
+ autoFallback: true
169
+ }
170
+ };
171
+ BUILTIN_ROLES = [
172
+ // ═══ C-Suite ═══
173
+ {
174
+ id: "ceo",
175
+ name: "CEO",
176
+ description: "Top AI executive. Decomposes missions, coordinates departments, final quality gate.",
177
+ systemPrompt: `You are the CEO of this AI company. You report directly to the Chairman (the human).
178
+
179
+ You are the highest-ranking AI executive. When the Chairman gives a mission, you decompose it into work streams, delegate to department heads, collect reports, review quality, and deliver the final result.
180
+
181
+ DECOMPOSITION:
182
+ 1. What types of work does this mission require?
183
+ 2. Which roles are best suited for each work stream?
184
+ 3. What are the dependencies between work streams?
185
+ 4. What can be done in parallel?
186
+ 5. What is the estimated cost?
187
+
188
+ DELEGATION:
189
+ - Technical work \u2192 CTO or Engineer
190
+ - Financial analysis \u2192 CFO or Analyst
191
+ - Marketing, content \u2192 CMO
192
+ - Research \u2192 Researcher
193
+ - Data collection, formatting \u2192 Worker
194
+ - Report formatting \u2192 Secretary
195
+ - Your time is the most expensive \u2014 delegate everything you can
196
+
197
+ COST AWARENESS:
198
+ - A task a Worker can do for $0.003 should NOT be done by you for $0.10
199
+ - Always assign to the cheapest role that can handle the task well`,
200
+ model: "claude-opus-4-6",
201
+ provider: "clawapi",
202
+ reportsTo: null,
203
+ canDelegateTo: ["cto", "cfo", "cmo", "researcher", "analyst", "engineer", "secretary", "worker"],
204
+ canEscalateTo: [],
205
+ budgetTier: "earn",
206
+ budgetMonthly: null,
207
+ maxTokensPerTask: null,
208
+ tools: ["http", "filesystem"],
209
+ skills: [],
210
+ isBuiltin: true,
211
+ isActive: true,
212
+ heartbeatInterval: 0,
213
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
214
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
215
+ },
216
+ {
217
+ id: "cto",
218
+ name: "CTO",
219
+ description: "Technical architecture, code review, system design, technical decisions.",
220
+ systemPrompt: `You are the CTO. You report to the CEO.
221
+
222
+ Own all technical decisions \u2014 architecture, system design, code review, security, performance. Delegate implementation to Engineers, routine data work to Workers.
223
+
224
+ STANDARDS: Clean code, security-first, performance-aware, clear technical explanations.`,
225
+ model: "gpt-5.4",
226
+ provider: "clawapi",
227
+ reportsTo: "ceo",
228
+ canDelegateTo: ["engineer", "worker"],
229
+ canEscalateTo: ["ceo"],
230
+ budgetTier: "earn",
231
+ budgetMonthly: null,
232
+ maxTokensPerTask: null,
233
+ tools: ["shell", "filesystem", "http", "code_interpreter"],
234
+ skills: ["coding"],
235
+ isBuiltin: true,
236
+ isActive: true,
237
+ heartbeatInterval: 0,
238
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
239
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
240
+ },
241
+ {
242
+ id: "cfo",
243
+ name: "CFO",
244
+ description: "Financial analysis, budgets, projections, cost optimization.",
245
+ systemPrompt: `You are the CFO. You report to the CEO.
246
+
247
+ Handle all financial work \u2014 budget analysis, cost projections, financial modeling, ROI calculations. Think step by step through numbers. State assumptions. Present data in clear tables.`,
248
+ model: "gpt-5-mini",
249
+ provider: "clawapi",
250
+ reportsTo: "ceo",
251
+ canDelegateTo: ["analyst", "worker"],
252
+ canEscalateTo: ["ceo"],
253
+ budgetTier: "save",
254
+ budgetMonthly: null,
255
+ maxTokensPerTask: null,
256
+ tools: ["http", "filesystem", "code_interpreter"],
257
+ skills: [],
258
+ isBuiltin: true,
259
+ isActive: true,
260
+ heartbeatInterval: 0,
261
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
262
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
263
+ },
264
+ {
265
+ id: "cmo",
266
+ name: "CMO",
267
+ description: "Marketing strategy, content creation, brand voice, growth.",
268
+ systemPrompt: `You are the CMO. You report to the CEO.
269
+
270
+ Own marketing strategy, content creation, brand voice, growth initiatives. Write compelling copy, design campaigns, analyze market positioning. Punchy, engaging, brand-consistent.`,
271
+ model: "claude-sonnet-4-6",
272
+ provider: "clawapi",
273
+ reportsTo: "ceo",
274
+ canDelegateTo: ["researcher", "worker"],
275
+ canEscalateTo: ["ceo"],
276
+ budgetTier: "earn",
277
+ budgetMonthly: null,
278
+ maxTokensPerTask: null,
279
+ tools: ["http", "filesystem"],
280
+ skills: [],
281
+ isBuiltin: true,
282
+ isActive: true,
283
+ heartbeatInterval: 0,
284
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
285
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
286
+ },
287
+ // ═══ Mid-level ═══
288
+ {
289
+ id: "researcher",
290
+ name: "Researcher",
291
+ description: "Deep research, source evaluation, competitive analysis.",
292
+ systemPrompt: `You are a Researcher. You report to whoever delegates to you.
293
+
294
+ Conduct deep research \u2014 gather information, evaluate sources, analyze competitors, investigate topics thoroughly. Cite sources. Distinguish facts from opinions. Flag data gaps.`,
295
+ model: "claude-sonnet-4-6",
296
+ provider: "clawapi",
297
+ reportsTo: "ceo",
298
+ canDelegateTo: ["worker"],
299
+ canEscalateTo: ["ceo"],
300
+ budgetTier: "earn",
301
+ budgetMonthly: null,
302
+ maxTokensPerTask: null,
303
+ tools: ["http", "filesystem"],
304
+ skills: [],
305
+ isBuiltin: true,
306
+ isActive: true,
307
+ heartbeatInterval: 0,
308
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
309
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
310
+ },
311
+ {
312
+ id: "analyst",
313
+ name: "Analyst",
314
+ description: "Data analysis, pattern detection, metrics, quantitative work.",
315
+ systemPrompt: `You are an Analyst. You report to the CFO or CEO.
316
+
317
+ Analyze data, detect patterns, calculate metrics, build models. Show calculations step by step. Present findings in tables. State assumptions. Quantify confidence levels.`,
318
+ model: "claude-sonnet-4-6",
319
+ provider: "clawapi",
320
+ reportsTo: "cfo",
321
+ canDelegateTo: ["worker"],
322
+ canEscalateTo: ["cfo"],
323
+ budgetTier: "save",
324
+ budgetMonthly: null,
325
+ maxTokensPerTask: null,
326
+ tools: ["http", "filesystem", "code_interpreter"],
327
+ skills: [],
328
+ isBuiltin: true,
329
+ isActive: true,
330
+ heartbeatInterval: 0,
331
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
332
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
333
+ },
334
+ {
335
+ id: "engineer",
336
+ name: "Engineer",
337
+ description: "Code implementation, debugging, testing, feature development.",
338
+ systemPrompt: `You are an Engineer. You report to the CTO.
339
+
340
+ Write code, implement features, fix bugs, write tests. Execute the technical vision set by the CTO. Clean, readable code with error handling.`,
341
+ model: "gpt-5.4",
342
+ provider: "clawapi",
343
+ reportsTo: "cto",
344
+ canDelegateTo: ["worker"],
345
+ canEscalateTo: ["cto"],
346
+ budgetTier: "earn",
347
+ budgetMonthly: null,
348
+ maxTokensPerTask: null,
349
+ tools: ["shell", "filesystem", "http", "code_interpreter"],
350
+ skills: ["coding"],
351
+ isBuiltin: true,
352
+ isActive: true,
353
+ heartbeatInterval: 0,
354
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
355
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
356
+ },
357
+ {
358
+ id: "secretary",
359
+ name: "Secretary",
360
+ description: "Briefings, summaries, report formatting, document preparation.",
361
+ systemPrompt: `You are the Secretary. You report to the CEO.
362
+
363
+ Prepare briefings, format reports, summarize documents, organize information. Make everything presentable for the Chairman (the human). Concise, professional, no fluff.`,
364
+ model: "gemini-3.1-flash-lite",
365
+ provider: "clawapi",
366
+ reportsTo: "ceo",
367
+ canDelegateTo: [],
368
+ canEscalateTo: ["ceo"],
369
+ budgetTier: "save",
370
+ budgetMonthly: null,
371
+ maxTokensPerTask: null,
372
+ tools: ["http", "filesystem"],
373
+ skills: [],
374
+ isBuiltin: true,
375
+ isActive: true,
376
+ heartbeatInterval: 0,
377
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
378
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
379
+ },
380
+ // ═══ Operations ═══
381
+ {
382
+ id: "worker",
383
+ name: "Worker",
384
+ description: "Fast routine tasks, data collection, formatting, translation.",
385
+ systemPrompt: `You are a Worker. Execute routine tasks quickly and reliably. Data collection, formatting, translation, classification, tagging. Focus on speed and accuracy. Keep outputs structured.`,
386
+ model: "gemini-3.1-flash-lite",
387
+ provider: "clawapi",
388
+ reportsTo: "ceo",
389
+ canDelegateTo: [],
390
+ canEscalateTo: ["ceo"],
391
+ budgetTier: "save",
392
+ budgetMonthly: null,
393
+ maxTokensPerTask: null,
394
+ tools: ["filesystem", "http"],
395
+ skills: [],
396
+ isBuiltin: true,
397
+ isActive: true,
398
+ heartbeatInterval: 0,
399
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
400
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
401
+ },
402
+ // ═══ Fallback ═══
403
+ {
404
+ id: "fallback_a",
405
+ name: "Fallback A",
406
+ description: "Low-balance fallback.",
407
+ systemPrompt: `Fallback agent. Low-balance mode. Be concise. Output only what's needed.`,
408
+ model: "gpt-oss-120b",
409
+ provider: "clawapi",
410
+ reportsTo: null,
411
+ canDelegateTo: [],
412
+ canEscalateTo: [],
413
+ budgetTier: "survive",
414
+ budgetMonthly: null,
415
+ maxTokensPerTask: 2e3,
416
+ tools: ["filesystem"],
417
+ skills: [],
418
+ isBuiltin: true,
419
+ isActive: true,
420
+ heartbeatInterval: 0,
421
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
422
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
423
+ },
424
+ {
425
+ id: "fallback_b",
426
+ name: "Fallback B",
427
+ description: "Minimum cost, last resort.",
428
+ systemPrompt: `Last-resort agent. Extremely low balance. Classification, tagging, yes/no only.`,
429
+ model: "gpt-oss-20b",
430
+ provider: "clawapi",
431
+ reportsTo: null,
432
+ canDelegateTo: [],
433
+ canEscalateTo: [],
434
+ budgetTier: "survive",
435
+ budgetMonthly: null,
436
+ maxTokensPerTask: 500,
437
+ tools: [],
438
+ skills: [],
439
+ isBuiltin: true,
440
+ isActive: true,
441
+ heartbeatInterval: 0,
442
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
443
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
444
+ }
445
+ ];
446
+ DEFAULT_FALLBACK_CHAIN = [
447
+ "claude-opus-4-6",
448
+ "claude-sonnet-4-6",
449
+ "gpt-5.4",
450
+ "gpt-5-mini",
451
+ "gemini-3.1-flash-lite",
452
+ "gpt-oss-120b",
453
+ "gpt-oss-20b"
454
+ ];
455
+ MODEL_PRICING = {
456
+ "claude-opus-4-6": { input: 5, output: 25 },
457
+ "claude-sonnet-4-6": { input: 3, output: 15 },
458
+ "gpt-5.4": { input: 2.5, output: 15 },
459
+ "gemini-3.1-pro": { input: 2, output: 12 },
460
+ "gpt-5-mini": { input: 0.25, output: 2 },
461
+ "gemini-3.1-flash-lite": { input: 0.25, output: 1.5 },
462
+ "gpt-oss-120b": { input: 0.05, output: 0.45 },
463
+ "gpt-oss-20b": { input: 0.04, output: 0.18 }
464
+ };
465
+ }
466
+ });
467
+
468
+ // ../packages/shared/src/api-paths.ts
469
+ var init_api_paths = __esm({
470
+ "../packages/shared/src/api-paths.ts"() {
471
+ "use strict";
472
+ }
473
+ });
474
+
475
+ // ../packages/shared/src/index.ts
476
+ var init_src = __esm({
477
+ "../packages/shared/src/index.ts"() {
478
+ "use strict";
479
+ init_types();
480
+ init_defaults();
481
+ init_api_paths();
482
+ }
483
+ });
484
+
485
+ // src/commands/role.ts
486
+ var role_exports = {};
487
+ __export(role_exports, {
488
+ roleCommand: () => roleCommand,
489
+ roleListCommand: () => roleListCommand,
490
+ roleSetCommand: () => roleSetCommand
491
+ });
492
+ async function roleCommand() {
493
+ await roleListCommand();
494
+ }
495
+ async function roleListCommand() {
496
+ banner();
497
+ console.log(" \u{1F464} Chairman = Human (you)\n");
498
+ const config = readConfig();
499
+ const running = config ? await isServerRunning(config.serverPort) : false;
500
+ let roles;
501
+ if (running && config) {
502
+ try {
503
+ roles = await apiGet("/api/roles", config.serverPort);
504
+ } catch {
505
+ roles = BUILTIN_ROLES;
506
+ }
507
+ } else {
508
+ roles = BUILTIN_ROLES;
509
+ }
510
+ const active = roles.filter((r) => r.isActive && r.budgetTier !== "survive");
511
+ const inactive = roles.filter((r) => !r.isActive || r.budgetTier === "survive");
512
+ const maxName = Math.max(...active.map((r) => r.name.length));
513
+ const maxModel = Math.max(...active.map((r) => r.model.length));
514
+ for (const role2 of active) {
515
+ const pricing = MODEL_PRICING[role2.model];
516
+ const cost = pricing ? `$${pricing.input}/$${pricing.output}` : "";
517
+ const reports = role2.reportsTo ? `\u2192 ${role2.reportsTo}` : "\u2192 Human";
518
+ console.log(
519
+ ` ${role2.name.padEnd(maxName + 2)} ${role2.model.padEnd(maxModel + 2)} ${cost.padEnd(12)} reports ${reports}`
520
+ );
521
+ }
522
+ if (inactive.length > 0) {
523
+ console.log(`
524
+ + ${inactive.length} fallback/disabled roles`);
525
+ }
526
+ console.log("");
527
+ console.log(" Customize:");
528
+ console.log(" clawcompany role set cto --model deepseek-coder --provider deepseek");
529
+ console.log("");
530
+ }
531
+ async function roleSetCommand(roleId, opts) {
532
+ banner();
533
+ const role2 = BUILTIN_ROLES.find((r) => r.id === roleId);
534
+ if (!role2) {
535
+ console.log(` \u2717 Role "${roleId}" not found.`);
536
+ console.log(` Available: ${BUILTIN_ROLES.filter((r) => r.budgetTier !== "survive").map((r) => r.id).join(", ")}
537
+ `);
538
+ return;
539
+ }
540
+ console.log(` Role: ${role2.name} (${roleId})`);
541
+ console.log(` Current model: ${role2.model}`);
542
+ if (opts.model) console.log(` \u2192 New model: ${opts.model}`);
543
+ if (opts.provider) console.log(` \u2192 New provider: ${opts.provider}`);
544
+ if (opts.name) console.log(` \u2192 New name: ${opts.name}`);
545
+ console.log("");
546
+ console.log(" \u26A0 Hot-reload coming soon. For now, edit config and restart server.\n");
547
+ }
548
+ var init_role = __esm({
549
+ "src/commands/role.ts"() {
550
+ "use strict";
551
+ init_utils();
552
+ init_src();
553
+ }
554
+ });
555
+
556
+ // src/index.ts
557
+ import { Command } from "commander";
558
+
559
+ // src/commands/init.ts
560
+ init_utils();
561
+ init_src();
562
+ import { input, select, confirm } from "@inquirer/prompts";
563
+ async function initCommand() {
564
+ banner();
565
+ if (configExists()) {
566
+ const config2 = readConfig();
567
+ if (config2) {
568
+ console.log(` \u2713 Company "${config2.companyName}" already exists.`);
569
+ console.log(` \u2713 Config: ${getConfigDir()}/config.json`);
570
+ console.log("");
571
+ const reset = await confirm({
572
+ message: "Reset and start over?",
573
+ default: false
574
+ });
575
+ if (!reset) {
576
+ console.log("");
577
+ showNextSteps(config2.companyName);
578
+ return;
579
+ }
580
+ console.log("");
581
+ }
582
+ }
583
+ console.log(" Welcome! Let's set up your AI company.\n");
584
+ console.log(" Step 1/3: Connect to ClawAPI\n");
585
+ let apiKey = "";
586
+ let keyValid = false;
587
+ while (!keyValid) {
588
+ apiKey = await input({
589
+ message: "Enter your ClawAPI key:",
590
+ validate: (v) => {
591
+ if (!v.trim()) return "Key is required.";
592
+ if (!v.startsWith("sk-claw-")) return "Key should start with sk-claw-";
593
+ return true;
594
+ }
595
+ });
596
+ console.log("");
597
+ console.log(" Verifying key...");
598
+ const result = await validateClawApiKey(apiKey.trim());
599
+ if (result.valid) {
600
+ console.log(" \u2713 Key verified\n");
601
+ keyValid = true;
602
+ } else {
603
+ console.log(` \u2717 ${result.error}`);
604
+ console.log(" Get a key at https://clawapi.org\n");
605
+ }
606
+ }
607
+ console.log(" Step 2/3: Name your company\n");
608
+ const companyName = await input({
609
+ message: "Company name:",
610
+ default: "My AI Company"
611
+ });
612
+ console.log("");
613
+ console.log(" Step 3/3: Choose a template\n");
614
+ const template = await select({
615
+ message: "Template:",
616
+ choices: [
617
+ {
618
+ name: "Default (CEO + CTO + CFO + CMO + Researcher + Analyst + Engineer + Secretary + Worker)",
619
+ value: "default"
620
+ },
621
+ {
622
+ name: "Trading Desk (+ Trader + Data Collector)",
623
+ value: "trading-desk"
624
+ },
625
+ {
626
+ name: "Content Agency (+ Writer + Editor + SEO)",
627
+ value: "content-agency"
628
+ },
629
+ {
630
+ name: "Dev Shop (+ QA + DevOps)",
631
+ value: "dev-shop"
632
+ },
633
+ {
634
+ name: "Solo Founder (CEO + Worker only \u2014 cheapest)",
635
+ value: "solo-founder"
636
+ }
637
+ ]
638
+ });
639
+ console.log("");
640
+ console.log(" Creating company...\n");
641
+ const config = {
642
+ apiKey: apiKey.trim(),
643
+ companyName,
644
+ template,
645
+ serverPort: 3200,
646
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
647
+ };
648
+ writeConfig(config);
649
+ const roles = BUILTIN_ROLES.filter((r) => r.isActive && r.budgetTier !== "survive");
650
+ console.log(` \u2713 Company "${companyName}" created`);
651
+ console.log(` \u2713 ${roles.length} agents hired:
652
+ `);
653
+ const maxName = Math.max(...roles.map((r) => r.name.length));
654
+ for (const role2 of roles) {
655
+ const pricing = MODEL_PRICING[role2.model];
656
+ const cost = pricing ? `$${pricing.input}/$${pricing.output}` : "";
657
+ console.log(` ${role2.name.padEnd(maxName + 2)}\u2192 ${role2.model} (${cost})`);
658
+ }
659
+ console.log("");
660
+ console.log(` \u2713 Config saved to ${getConfigDir()}/config.json`);
661
+ console.log("");
662
+ showNextSteps(companyName);
663
+ }
664
+ function showNextSteps(companyName) {
665
+ console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
666
+ console.log(` "${companyName}" is ready! You are the Chairman.
667
+ `);
668
+ console.log(" Try:\n");
669
+ console.log(' clawcompany mission "Write a competitive analysis of OpenAI vs Anthropic"');
670
+ console.log(" clawcompany status");
671
+ console.log(" clawcompany role list");
672
+ console.log("");
673
+ console.log(" Build for OPC. Every human being is a chairman.");
674
+ console.log("");
675
+ }
676
+
677
+ // src/commands/mission.ts
678
+ init_utils();
679
+ init_src();
680
+
681
+ // ../packages/providers/src/pricing.ts
682
+ init_src();
683
+ function calculateCost(model, inputTokens, outputTokens) {
684
+ const pricing = MODEL_PRICING[model];
685
+ if (!pricing) return 0;
686
+ const inputCost = inputTokens / 1e6 * pricing.input;
687
+ const outputCost = outputTokens / 1e6 * pricing.output;
688
+ return Math.round((inputCost + outputCost) * 1e6) / 1e6;
689
+ }
690
+
691
+ // ../packages/providers/src/openai-compatible.ts
692
+ var OpenAICompatibleProvider = class {
693
+ constructor(config) {
694
+ this.config = config;
695
+ this.id = config.id;
696
+ this.name = config.name;
697
+ this.baseUrl = config.baseUrl ?? "https://api.openai.com/v1";
698
+ this.apiKey = config.apiKey;
699
+ if (Array.isArray(config.models)) {
700
+ for (const m of config.models) {
701
+ this.knownModels.add(m.id);
702
+ }
703
+ }
704
+ }
705
+ id;
706
+ name;
707
+ baseUrl;
708
+ apiKey;
709
+ knownModels = /* @__PURE__ */ new Set();
710
+ async chat(params) {
711
+ const url = `${this.baseUrl}/chat/completions`;
712
+ const isReasoning = this.isReasoningModel(params.model);
713
+ const body = {
714
+ model: params.model,
715
+ messages: params.messages,
716
+ max_tokens: params.maxTokens ?? 4096,
717
+ stream: true
718
+ };
719
+ if (!isReasoning) {
720
+ body.temperature = params.temperature ?? 0.7;
721
+ }
722
+ if (params.tools?.length) {
723
+ body.tools = params.tools;
724
+ }
725
+ const controller = new AbortController();
726
+ const timeout = setTimeout(() => controller.abort(), 3e5);
727
+ try {
728
+ const response = await fetch(url, {
729
+ method: "POST",
730
+ headers: {
731
+ "Authorization": `Bearer ${this.apiKey}`,
732
+ "Content-Type": "application/json"
733
+ },
734
+ body: JSON.stringify(body),
735
+ signal: controller.signal
736
+ });
737
+ clearTimeout(timeout);
738
+ if (!response.ok) {
739
+ const errorBody = await response.text().catch(() => "");
740
+ throw new ProviderError(
741
+ response.status,
742
+ `${this.name} API error ${response.status}: ${errorBody}`,
743
+ this.id
744
+ );
745
+ }
746
+ return await this.collectStream(response, params.model);
747
+ } catch (err) {
748
+ clearTimeout(timeout);
749
+ if (err.name === "AbortError") {
750
+ throw new ProviderError(
751
+ 504,
752
+ `${this.name}: request timed out after 5 minutes for model ${params.model}`,
753
+ this.id
754
+ );
755
+ }
756
+ throw err;
757
+ }
758
+ }
759
+ /**
760
+ * Read SSE stream, accumulate content and tool calls.
761
+ */
762
+ async collectStream(response, model) {
763
+ const reader = response.body?.getReader();
764
+ if (!reader) throw new Error("No response body");
765
+ const decoder = new TextDecoder();
766
+ let content = "";
767
+ let finishReason = "stop";
768
+ let toolCalls = [];
769
+ let inputTokens = 0;
770
+ let outputTokens = 0;
771
+ let actualModel = model;
772
+ let buffer = "";
773
+ while (true) {
774
+ const { done, value } = await reader.read();
775
+ if (done) break;
776
+ buffer += decoder.decode(value, { stream: true });
777
+ const lines = buffer.split("\n");
778
+ buffer = lines.pop() ?? "";
779
+ for (const line of lines) {
780
+ const trimmed = line.trim();
781
+ if (!trimmed || trimmed === "data: [DONE]") continue;
782
+ if (!trimmed.startsWith("data: ")) continue;
783
+ try {
784
+ const json = JSON.parse(trimmed.slice(6));
785
+ const delta = json.choices?.[0]?.delta;
786
+ const choice = json.choices?.[0];
787
+ if (delta?.content) {
788
+ content += delta.content;
789
+ }
790
+ if (delta?.tool_calls) {
791
+ for (const tc of delta.tool_calls) {
792
+ const idx = tc.index ?? 0;
793
+ if (!toolCalls[idx]) {
794
+ toolCalls[idx] = {
795
+ id: tc.id ?? "",
796
+ type: "function",
797
+ function: { name: "", arguments: "" }
798
+ };
799
+ }
800
+ if (tc.id) toolCalls[idx].id = tc.id;
801
+ if (tc.function?.name) toolCalls[idx].function.name += tc.function.name;
802
+ if (tc.function?.arguments) toolCalls[idx].function.arguments += tc.function.arguments;
803
+ }
804
+ }
805
+ if (choice?.finish_reason) {
806
+ finishReason = choice.finish_reason;
807
+ }
808
+ if (json.model) {
809
+ actualModel = json.model;
810
+ }
811
+ if (json.usage) {
812
+ inputTokens = json.usage.prompt_tokens ?? 0;
813
+ outputTokens = json.usage.completion_tokens ?? 0;
814
+ }
815
+ } catch {
816
+ }
817
+ }
818
+ }
819
+ if (outputTokens === 0 && content.length > 0) {
820
+ outputTokens = Math.ceil(content.length / 4);
821
+ }
822
+ const validToolCalls = toolCalls.filter((tc) => tc.id && tc.function.name);
823
+ return {
824
+ content,
825
+ model: actualModel,
826
+ provider: this.id,
827
+ usage: {
828
+ inputTokens,
829
+ outputTokens,
830
+ cost: calculateCost(model, inputTokens, outputTokens)
831
+ },
832
+ toolCalls: validToolCalls.length > 0 ? validToolCalls : void 0,
833
+ finishReason
834
+ };
835
+ }
836
+ /**
837
+ * Reasoning models reject temperature parameter.
838
+ */
839
+ isReasoningModel(model) {
840
+ const reasoning = ["gpt-5-mini", "o1", "o1-mini", "o1-preview", "o3", "o3-mini"];
841
+ return reasoning.some((r) => model.includes(r));
842
+ }
843
+ async listModels() {
844
+ if (Array.isArray(this.config.models)) {
845
+ return this.config.models;
846
+ }
847
+ try {
848
+ const response = await fetch(`${this.baseUrl}/models`, {
849
+ headers: { "Authorization": `Bearer ${this.apiKey}` }
850
+ });
851
+ if (!response.ok) return [];
852
+ const data = await response.json();
853
+ const models = (data.data ?? []).map((m) => ({
854
+ id: m.id,
855
+ name: m.id,
856
+ provider: this.id
857
+ }));
858
+ for (const m of models) {
859
+ this.knownModels.add(m.id);
860
+ }
861
+ return models;
862
+ } catch {
863
+ return [];
864
+ }
865
+ }
866
+ hasModel(modelId) {
867
+ return this.knownModels.has(modelId);
868
+ }
869
+ async healthCheck() {
870
+ try {
871
+ const models = await this.listModels();
872
+ return { ok: models.length > 0 };
873
+ } catch (err) {
874
+ return {
875
+ ok: false,
876
+ error: err instanceof Error ? err.message : String(err)
877
+ };
878
+ }
879
+ }
880
+ };
881
+ var ProviderError = class extends Error {
882
+ constructor(status, message, providerId) {
883
+ super(message);
884
+ this.status = status;
885
+ this.providerId = providerId;
886
+ this.name = "ProviderError";
887
+ }
888
+ };
889
+
890
+ // ../packages/providers/src/registry.ts
891
+ var ProviderRegistry = class {
892
+ providers = /* @__PURE__ */ new Map();
893
+ defaultProviderId = null;
894
+ /**
895
+ * Load providers from config. Call once at startup.
896
+ */
897
+ async loadFromConfig(configs) {
898
+ for (const config of configs) {
899
+ const resolvedConfig = {
900
+ ...config,
901
+ apiKey: this.resolveEnvVar(config.apiKey)
902
+ };
903
+ const provider = this.createProvider(resolvedConfig);
904
+ this.providers.set(config.id, provider);
905
+ if (config.isDefault) {
906
+ this.defaultProviderId = config.id;
907
+ }
908
+ if (config.models === "auto") {
909
+ await provider.listModels();
910
+ }
911
+ }
912
+ if (!this.defaultProviderId && configs.length > 0) {
913
+ this.defaultProviderId = configs[0].id;
914
+ }
915
+ }
916
+ /**
917
+ * Get a specific provider by ID, or the default.
918
+ */
919
+ get(id) {
920
+ if (id) {
921
+ const p = this.providers.get(id);
922
+ if (p) return p;
923
+ }
924
+ if (this.defaultProviderId) {
925
+ const p = this.providers.get(this.defaultProviderId);
926
+ if (p) return p;
927
+ }
928
+ throw new Error("No providers configured. Run `clawcompany init` first.");
929
+ }
930
+ /**
931
+ * Get the default provider (ClawAPI if configured).
932
+ */
933
+ getDefault() {
934
+ return this.get(this.defaultProviderId ?? void 0);
935
+ }
936
+ /**
937
+ * Find the first provider that serves a given model.
938
+ * Prefers the default provider if it has the model.
939
+ */
940
+ findProviderForModel(modelId) {
941
+ if (this.defaultProviderId) {
942
+ const def = this.providers.get(this.defaultProviderId);
943
+ if (def?.hasModel(modelId)) return def;
944
+ }
945
+ for (const [, provider] of this.providers) {
946
+ if (provider.hasModel(modelId)) return provider;
947
+ }
948
+ return null;
949
+ }
950
+ /**
951
+ * Hot-add a provider without restart.
952
+ */
953
+ async add(config) {
954
+ const resolvedConfig = {
955
+ ...config,
956
+ apiKey: this.resolveEnvVar(config.apiKey)
957
+ };
958
+ const provider = this.createProvider(resolvedConfig);
959
+ if (config.models === "auto") {
960
+ await provider.listModels();
961
+ }
962
+ this.providers.set(config.id, provider);
963
+ }
964
+ /**
965
+ * Remove a provider.
966
+ */
967
+ remove(id) {
968
+ if (id === this.defaultProviderId) {
969
+ throw new Error("Cannot remove the default provider. Set another as default first.");
970
+ }
971
+ return this.providers.delete(id);
972
+ }
973
+ /**
974
+ * List all registered providers.
975
+ */
976
+ list() {
977
+ return Array.from(this.providers.entries()).map(([id, p]) => ({
978
+ id,
979
+ name: p.name,
980
+ isDefault: id === this.defaultProviderId
981
+ }));
982
+ }
983
+ /**
984
+ * Iterate over all provider entries.
985
+ */
986
+ entries() {
987
+ return this.providers.entries();
988
+ }
989
+ // ──────────────────────────────────────────
990
+ // Internal
991
+ // ──────────────────────────────────────────
992
+ createProvider(config) {
993
+ switch (config.type) {
994
+ case "openai-compatible":
995
+ case "openai":
996
+ case "anthropic":
997
+ case "google-genai":
998
+ return new OpenAICompatibleProvider(config);
999
+ default:
1000
+ return new OpenAICompatibleProvider(config);
1001
+ }
1002
+ }
1003
+ /**
1004
+ * Resolve ${ENV_VAR} patterns in config values.
1005
+ */
1006
+ resolveEnvVar(value) {
1007
+ const match = value.match(/^\$\{(\w+)\}$/);
1008
+ if (match) {
1009
+ return process.env[match[1]] ?? value;
1010
+ }
1011
+ return value;
1012
+ }
1013
+ };
1014
+
1015
+ // ../packages/model-router/src/index.ts
1016
+ init_src();
1017
+ var ModelRouter = class {
1018
+ constructor(registry, config) {
1019
+ this.registry = registry;
1020
+ this.config = config;
1021
+ this.roles = resolveRoles(config);
1022
+ this.fallbackChain = config.fallbackChain ?? DEFAULT_FALLBACK_CHAIN;
1023
+ }
1024
+ roles;
1025
+ fallbackChain;
1026
+ /**
1027
+ * Send a chat request as a specific role.
1028
+ * Automatically prepends the role's system prompt.
1029
+ */
1030
+ async chatAsRole(roleId, messages, tools) {
1031
+ const role2 = this.roles.find((r) => r.id === roleId);
1032
+ if (!role2) throw new Error(`Role "${roleId}" not found`);
1033
+ if (!role2.isActive) throw new Error(`Role "${roleId}" is disabled`);
1034
+ const hasSystem = messages.some((m) => m.role === "system");
1035
+ const fullMessages = hasSystem ? messages : [{ role: "system", content: role2.systemPrompt }, ...messages];
1036
+ const provider = this.registry.get(role2.provider);
1037
+ try {
1038
+ return await provider.chat({
1039
+ model: role2.model,
1040
+ messages: fullMessages,
1041
+ tools
1042
+ });
1043
+ } catch (err) {
1044
+ if (err && typeof err === "object" && "status" in err) {
1045
+ if (err.status === 402) {
1046
+ console.warn(`[ModelRouter] ${role2.name}: balance empty, falling back`);
1047
+ return this.fallback(fullMessages, tools);
1048
+ }
1049
+ if (err.status === 429) {
1050
+ console.warn(`[ModelRouter] ${role2.name}: rate limited, retrying in 2s`);
1051
+ await sleep(2e3);
1052
+ return provider.chat({ model: role2.model, messages: fullMessages, tools });
1053
+ }
1054
+ if (err.status === 502 || err.status === 503) {
1055
+ console.warn(`[ModelRouter] ${role2.name}: upstream down, retrying in 3s`);
1056
+ await sleep(3e3);
1057
+ return provider.chat({ model: role2.model, messages: fullMessages, tools });
1058
+ }
1059
+ }
1060
+ throw err;
1061
+ }
1062
+ }
1063
+ async fallback(messages, tools) {
1064
+ for (const modelId of this.fallbackChain) {
1065
+ const provider = this.registry.findProviderForModel(modelId);
1066
+ if (!provider) continue;
1067
+ try {
1068
+ const result = await provider.chat({ model: modelId, messages, tools });
1069
+ console.log(`[ModelRouter] Fell back to ${modelId} via ${provider.name}`);
1070
+ return result;
1071
+ } catch {
1072
+ continue;
1073
+ }
1074
+ }
1075
+ throw new Error(
1076
+ "All models in the fallback chain failed. Please check your balance at clawapi.org or add another provider."
1077
+ );
1078
+ }
1079
+ getStrategyMode(balance) {
1080
+ if (balance > 5) return "earn";
1081
+ if (balance > 1) return "save";
1082
+ return "survive";
1083
+ }
1084
+ setRoleModel(roleId, model, provider) {
1085
+ const role2 = this.roles.find((r) => r.id === roleId);
1086
+ if (!role2) throw new Error(`Role "${roleId}" not found`);
1087
+ role2.model = model;
1088
+ if (provider) role2.provider = provider;
1089
+ role2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1090
+ }
1091
+ getRoles() {
1092
+ return this.roles;
1093
+ }
1094
+ getRole(id) {
1095
+ return this.roles.find((r) => r.id === id);
1096
+ }
1097
+ };
1098
+ function sleep(ms) {
1099
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
1100
+ }
1101
+
1102
+ // ../packages/tools/src/executor.ts
1103
+ import { exec } from "child_process";
1104
+ import { readFile, writeFile, readdir, unlink } from "fs/promises";
1105
+ import { promisify } from "util";
1106
+ var execAsync = promisify(exec);
1107
+ var ToolExecutor = class {
1108
+ async execute(toolName, args) {
1109
+ switch (toolName) {
1110
+ case "filesystem":
1111
+ return this.execFilesystem(args);
1112
+ case "shell":
1113
+ return this.execShell(args);
1114
+ case "http":
1115
+ return this.execHttp(args);
1116
+ case "code_interpreter":
1117
+ return this.execCode(args);
1118
+ default:
1119
+ return `Unknown tool: ${toolName}`;
1120
+ }
1121
+ }
1122
+ async execFilesystem(args) {
1123
+ const { action, path, content } = args;
1124
+ switch (action) {
1125
+ case "read":
1126
+ return readFile(path, "utf-8");
1127
+ case "write":
1128
+ await writeFile(path, content ?? "");
1129
+ return `Written to ${path}`;
1130
+ case "list":
1131
+ return (await readdir(path)).join("\n");
1132
+ case "delete":
1133
+ await unlink(path);
1134
+ return `Deleted ${path}`;
1135
+ default:
1136
+ return `Unknown filesystem action: ${action}`;
1137
+ }
1138
+ }
1139
+ async execShell(args) {
1140
+ const { command, cwd } = args;
1141
+ try {
1142
+ const { stdout, stderr } = await execAsync(command, {
1143
+ cwd,
1144
+ timeout: 3e4
1145
+ });
1146
+ return stdout + (stderr ? `
1147
+ STDERR: ${stderr}` : "");
1148
+ } catch (err) {
1149
+ return `Error: ${err.message}`;
1150
+ }
1151
+ }
1152
+ async execHttp(args) {
1153
+ const { method, url, headers, body } = args;
1154
+ const response = await fetch(url, {
1155
+ method,
1156
+ headers,
1157
+ body: method !== "GET" ? body : void 0
1158
+ });
1159
+ const text = await response.text();
1160
+ return `${response.status} ${response.statusText}
1161
+ ${text.slice(0, 5e3)}`;
1162
+ }
1163
+ async execCode(args) {
1164
+ const { language, code } = args;
1165
+ if (language === "javascript") {
1166
+ try {
1167
+ const result = new Function(code)();
1168
+ return String(result ?? "undefined");
1169
+ } catch (err) {
1170
+ return `Error: ${err.message}`;
1171
+ }
1172
+ }
1173
+ if (language === "python") {
1174
+ return this.execShell({ command: `python3 -c ${JSON.stringify(code)}` });
1175
+ }
1176
+ return `Unsupported language: ${language}`;
1177
+ }
1178
+ };
1179
+
1180
+ // ../packages/tools/src/index.ts
1181
+ var BUILTIN_TOOLS = {
1182
+ filesystem: {
1183
+ type: "function",
1184
+ function: {
1185
+ name: "filesystem",
1186
+ description: "Read or write files. Actions: read, write, list, delete.",
1187
+ parameters: {
1188
+ type: "object",
1189
+ properties: {
1190
+ action: { type: "string", enum: ["read", "write", "list", "delete"] },
1191
+ path: { type: "string", description: "File or directory path" },
1192
+ content: { type: "string", description: "Content to write (for write action)" }
1193
+ },
1194
+ required: ["action", "path"]
1195
+ }
1196
+ }
1197
+ },
1198
+ shell: {
1199
+ type: "function",
1200
+ function: {
1201
+ name: "shell",
1202
+ description: "Execute a shell command and return stdout/stderr.",
1203
+ parameters: {
1204
+ type: "object",
1205
+ properties: {
1206
+ command: { type: "string", description: "Shell command to execute" },
1207
+ cwd: { type: "string", description: "Working directory (optional)" }
1208
+ },
1209
+ required: ["command"]
1210
+ }
1211
+ }
1212
+ },
1213
+ http: {
1214
+ type: "function",
1215
+ function: {
1216
+ name: "http",
1217
+ description: "Make an HTTP request. Supports GET, POST, PUT, DELETE.",
1218
+ parameters: {
1219
+ type: "object",
1220
+ properties: {
1221
+ method: { type: "string", enum: ["GET", "POST", "PUT", "DELETE"] },
1222
+ url: { type: "string", description: "Request URL" },
1223
+ headers: { type: "object", description: "Request headers" },
1224
+ body: { type: "string", description: "Request body (for POST/PUT)" }
1225
+ },
1226
+ required: ["method", "url"]
1227
+ }
1228
+ }
1229
+ },
1230
+ code_interpreter: {
1231
+ type: "function",
1232
+ function: {
1233
+ name: "code_interpreter",
1234
+ description: "Execute JavaScript or Python code in a sandboxed environment.",
1235
+ parameters: {
1236
+ type: "object",
1237
+ properties: {
1238
+ language: { type: "string", enum: ["javascript", "python"] },
1239
+ code: { type: "string", description: "Code to execute" }
1240
+ },
1241
+ required: ["language", "code"]
1242
+ }
1243
+ }
1244
+ }
1245
+ };
1246
+ function getToolsForRole(toolNames) {
1247
+ return toolNames.map((name) => BUILTIN_TOOLS[name]).filter((t) => t !== void 0);
1248
+ }
1249
+
1250
+ // ../packages/agent-runtime/src/executor.ts
1251
+ var AgentExecutor = class {
1252
+ constructor(router, toolExecutor) {
1253
+ this.router = router;
1254
+ this.toolExecutor = toolExecutor;
1255
+ }
1256
+ async execute(role2, task) {
1257
+ const tools = getToolsForRole(role2.tools);
1258
+ let totalIn = 0;
1259
+ let totalOut = 0;
1260
+ let totalCost = 0;
1261
+ let toolCallCount = 0;
1262
+ const messages = [
1263
+ { role: "system", content: role2.systemPrompt },
1264
+ {
1265
+ role: "user",
1266
+ content: this.buildTaskPrompt(task)
1267
+ }
1268
+ ];
1269
+ const MAX_TURNS = 10;
1270
+ for (let turn = 0; turn < MAX_TURNS; turn++) {
1271
+ const response = await this.router.chatAsRole(
1272
+ role2.id,
1273
+ messages,
1274
+ tools.length > 0 ? tools : void 0
1275
+ );
1276
+ totalIn += response.usage.inputTokens;
1277
+ totalOut += response.usage.outputTokens;
1278
+ totalCost += response.usage.cost;
1279
+ if (!response.toolCalls?.length || response.finishReason === "stop") {
1280
+ return {
1281
+ output: response.content,
1282
+ tokensIn: totalIn,
1283
+ tokensOut: totalOut,
1284
+ cost: totalCost,
1285
+ modelUsed: response.model,
1286
+ toolCallCount
1287
+ };
1288
+ }
1289
+ messages.push({
1290
+ role: "assistant",
1291
+ content: response.content ?? "",
1292
+ toolCalls: response.toolCalls
1293
+ });
1294
+ for (const toolCall of response.toolCalls) {
1295
+ toolCallCount++;
1296
+ const args = JSON.parse(toolCall.function.arguments);
1297
+ const result = await this.toolExecutor.execute(
1298
+ toolCall.function.name,
1299
+ args
1300
+ );
1301
+ messages.push({
1302
+ role: "tool",
1303
+ content: result,
1304
+ toolCallId: toolCall.id
1305
+ });
1306
+ }
1307
+ }
1308
+ return {
1309
+ output: "[Agent reached maximum turns without completing]",
1310
+ tokensIn: totalIn,
1311
+ tokensOut: totalOut,
1312
+ cost: totalCost,
1313
+ modelUsed: role2.model,
1314
+ toolCallCount
1315
+ };
1316
+ }
1317
+ buildTaskPrompt(task) {
1318
+ let prompt = `## Task: ${task.title}
1319
+
1320
+ ${task.description}`;
1321
+ if (task.context) {
1322
+ prompt += `
1323
+
1324
+ ## Context
1325
+ ${JSON.stringify(task.context, null, 2)}`;
1326
+ }
1327
+ prompt += `
1328
+
1329
+ ## Instructions
1330
+ Complete this task. If you need to use tools, use them.
1331
+ When done, provide your final output clearly.
1332
+ Report any issues or blockers you encountered.`;
1333
+ return prompt;
1334
+ }
1335
+ };
1336
+
1337
+ // ../packages/task-orchestrator/src/index.ts
1338
+ var TaskOrchestrator = class {
1339
+ constructor(router) {
1340
+ this.router = router;
1341
+ this.executor = new AgentExecutor(router, new ToolExecutor());
1342
+ }
1343
+ executor;
1344
+ /**
1345
+ * Phase 2: CEO decomposes mission into work streams.
1346
+ * Human (Chairman) gives the mission → CEO breaks it down.
1347
+ */
1348
+ async decompose(mission) {
1349
+ const ceo = this.router.getRole("ceo");
1350
+ if (!ceo) throw new Error("CEO role not configured");
1351
+ const roles = this.router.getRoles().filter((r) => r.isActive && r.id !== "ceo" && r.budgetTier !== "survive");
1352
+ const roleList = roles.map((r) => `- ${r.id}: ${r.name} (${r.model}) \u2014 ${r.description}`).join("\n");
1353
+ const response = await this.router.chatAsRole("ceo", [
1354
+ {
1355
+ role: "user",
1356
+ content: `Mission from the Chairman (human):
1357
+ "${mission.content}"
1358
+
1359
+ Priority: ${mission.priority}
1360
+ ${mission.deadline ? `Deadline: ${mission.deadline}` : ""}
1361
+ ${mission.budgetLimit ? `Budget limit: $${mission.budgetLimit}` : ""}
1362
+
1363
+ Your team:
1364
+ ${roleList}
1365
+
1366
+ Decompose into work streams. For each, specify:
1367
+ - id (ws_1, ws_2, ...)
1368
+ - title
1369
+ - description (specific deliverable)
1370
+ - assignTo (role id from list)
1371
+ - dependencies (array of ws IDs this depends on)
1372
+ - estimatedComplexity (low/medium/high)
1373
+ - requiredTools (array)
1374
+
1375
+ IMPORTANT: Assign grunt work to worker (cheap). Technical to cto/engineer. Financial to cfo/analyst. Marketing to cmo. Research to researcher. Format to secretary.
1376
+
1377
+ Respond ONLY with JSON:
1378
+ {
1379
+ "workStreams": [ { "id": "ws_1", "title": "...", "description": "...", "assignTo": "worker", "dependencies": [], "estimatedComplexity": "low", "requiredTools": ["http"] } ]
1380
+ }`
1381
+ }
1382
+ ]);
1383
+ try {
1384
+ const cleaned = response.content.replace(/```json\n?/g, "").replace(/```/g, "").trim();
1385
+ const parsed = JSON.parse(cleaned);
1386
+ return (parsed.workStreams ?? []).map((ws) => ({
1387
+ ...ws,
1388
+ missionId: mission.id,
1389
+ status: "pending"
1390
+ }));
1391
+ } catch {
1392
+ return [{
1393
+ id: "ws_1",
1394
+ missionId: mission.id,
1395
+ title: mission.content.slice(0, 100),
1396
+ description: mission.content,
1397
+ assignTo: "researcher",
1398
+ dependencies: [],
1399
+ estimatedComplexity: "medium",
1400
+ requiredTools: [],
1401
+ status: "pending"
1402
+ }];
1403
+ }
1404
+ }
1405
+ async executeMission(mission, workStreams) {
1406
+ const results = /* @__PURE__ */ new Map();
1407
+ const totalStart = Date.now();
1408
+ let totalCost = 0;
1409
+ const order = this.topologicalSort(workStreams);
1410
+ console.log(`
1411
+ \u{1F4CB} Executing ${order.length} work streams...
1412
+ `);
1413
+ for (const ws of order) {
1414
+ const depOutputs = {};
1415
+ for (const depId of ws.dependencies) {
1416
+ const dep = results.get(depId);
1417
+ if (dep && dep.status === "completed") depOutputs[depId] = dep.output;
1418
+ }
1419
+ const role2 = this.router.getRole(ws.assignTo);
1420
+ const roleName = role2?.name ?? ws.assignTo;
1421
+ const modelName = role2?.model ?? "unknown";
1422
+ console.log(` \u26A1 ${ws.id}: ${ws.title}`);
1423
+ console.log(` \u2192 ${roleName} (${modelName})`);
1424
+ const startTime = Date.now();
1425
+ try {
1426
+ const output = await this.executeWorkStream(ws, depOutputs);
1427
+ const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
1428
+ results.set(ws.id, {
1429
+ workStreamId: ws.id,
1430
+ title: ws.title,
1431
+ assignedTo: ws.assignTo,
1432
+ output: output.content,
1433
+ cost: output.cost,
1434
+ tokensIn: output.tokensIn,
1435
+ tokensOut: output.tokensOut,
1436
+ model: output.model,
1437
+ status: "completed"
1438
+ });
1439
+ totalCost += output.cost;
1440
+ console.log(` \u2705 Done (${elapsed}s, $${output.cost.toFixed(4)})
1441
+ `);
1442
+ } catch (err) {
1443
+ console.log(` \u274C Failed: ${err.message}
1444
+ `);
1445
+ results.set(ws.id, {
1446
+ workStreamId: ws.id,
1447
+ title: ws.title,
1448
+ assignedTo: ws.assignTo,
1449
+ output: `Error: ${err.message}`,
1450
+ cost: 0,
1451
+ tokensIn: 0,
1452
+ tokensOut: 0,
1453
+ model: "none",
1454
+ status: "failed"
1455
+ });
1456
+ }
1457
+ }
1458
+ const totalElapsed = ((Date.now() - totalStart) / 1e3).toFixed(1);
1459
+ console.log(` \u{1F4CA} All work streams complete: ${totalElapsed}s, $${totalCost.toFixed(4)} total
1460
+ `);
1461
+ return {
1462
+ missionId: mission.id,
1463
+ mission: mission.content,
1464
+ workStreams: Array.from(results.values()),
1465
+ totalCost,
1466
+ totalTimeSeconds: parseFloat(totalElapsed)
1467
+ };
1468
+ }
1469
+ async executeWorkStream(ws, dependencyOutputs) {
1470
+ let context = "";
1471
+ if (Object.keys(dependencyOutputs).length > 0) {
1472
+ context = "\n\n## Previous work stream outputs:\n";
1473
+ for (const [id, output] of Object.entries(dependencyOutputs)) {
1474
+ const truncated = output.length > 500 ? output.slice(0, 500) + "\n...(truncated)" : output;
1475
+ context += `
1476
+ ### ${id}:
1477
+ ${truncated}
1478
+ `;
1479
+ }
1480
+ }
1481
+ const response = await this.router.chatAsRole(ws.assignTo, [
1482
+ {
1483
+ role: "user",
1484
+ content: `## Task: ${ws.title}
1485
+
1486
+ ${ws.description}
1487
+
1488
+ Complexity: ${ws.estimatedComplexity}${context}
1489
+
1490
+ Today's date is ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}. Complete this task. Provide your output clearly and concisely.`
1491
+ }
1492
+ ]);
1493
+ return {
1494
+ content: response.content,
1495
+ cost: response.usage.cost,
1496
+ tokensIn: response.usage.inputTokens,
1497
+ tokensOut: response.usage.outputTokens,
1498
+ model: response.model
1499
+ };
1500
+ }
1501
+ topologicalSort(workStreams) {
1502
+ const sorted = [];
1503
+ const visited = /* @__PURE__ */ new Set();
1504
+ const wsMap = new Map(workStreams.map((ws) => [ws.id, ws]));
1505
+ const visit = (ws) => {
1506
+ if (visited.has(ws.id)) return;
1507
+ visited.add(ws.id);
1508
+ for (const depId of ws.dependencies) {
1509
+ const dep = wsMap.get(depId);
1510
+ if (dep) visit(dep);
1511
+ }
1512
+ sorted.push(ws);
1513
+ };
1514
+ for (const ws of workStreams) visit(ws);
1515
+ return sorted;
1516
+ }
1517
+ };
1518
+
1519
+ // src/commands/mission.ts
1520
+ async function missionCommand(goal) {
1521
+ banner();
1522
+ const config = readConfig();
1523
+ if (!config) {
1524
+ console.log(" \u2717 No company set up yet.");
1525
+ console.log(" Fix: Run `npx clawcompany` to get started.\n");
1526
+ return;
1527
+ }
1528
+ console.log(` \u{1F3AF} Mission: "${goal}"
1529
+ `);
1530
+ if (await isServerRunning(config.serverPort)) {
1531
+ await runViaServer(goal, config.serverPort);
1532
+ } else {
1533
+ await runInProcess(goal, config.apiKey);
1534
+ }
1535
+ }
1536
+ async function runViaServer(goal, port) {
1537
+ console.log(" Sending to CEO...\n");
1538
+ try {
1539
+ const result = await apiPost("/api/mission/run", { mission: goal }, port);
1540
+ if (result.status !== "completed") {
1541
+ console.log(` \u2717 Mission failed: ${result.error ?? "Unknown error"}`);
1542
+ console.log(" Fix: Check server logs or retry.\n");
1543
+ return;
1544
+ }
1545
+ printResults(result);
1546
+ } catch (err) {
1547
+ console.log(` \u2717 ${err.message}
1548
+ `);
1549
+ }
1550
+ }
1551
+ async function runInProcess(goal, apiKey) {
1552
+ console.log(" Running in-process (no server needed)...\n");
1553
+ try {
1554
+ const clawConfig = getDefaultConfig();
1555
+ clawConfig.providers[0].apiKey = apiKey;
1556
+ const registry = new ProviderRegistry();
1557
+ await registry.loadFromConfig(clawConfig.providers);
1558
+ const router = new ModelRouter(registry, clawConfig);
1559
+ const orchestrator = new TaskOrchestrator(router);
1560
+ console.log(" Phase 2: CEO decomposing...");
1561
+ const mission = {
1562
+ id: `mission-${Date.now()}`,
1563
+ companyId: "default",
1564
+ content: goal,
1565
+ status: "decomposing",
1566
+ priority: "normal",
1567
+ approvalRequired: false,
1568
+ totalCost: 0,
1569
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1570
+ };
1571
+ const workStreams = await orchestrator.decompose(mission);
1572
+ console.log(` \u2705 Decomposed into ${workStreams.length} work streams
1573
+ `);
1574
+ console.log(" Phase 3-5: Executing...");
1575
+ const report = await orchestrator.executeMission(mission, workStreams);
1576
+ console.log(" Phase 6: Delivering to Chairman\n");
1577
+ const result = {
1578
+ status: "completed",
1579
+ mission: report.mission,
1580
+ totalCost: `$${report.totalCost.toFixed(4)}`,
1581
+ totalTime: `${report.totalTimeSeconds}s`,
1582
+ workStreams: report.workStreams.map((ws) => ({
1583
+ id: ws.workStreamId,
1584
+ title: ws.title,
1585
+ role: ws.assignedTo,
1586
+ model: ws.model,
1587
+ status: ws.status,
1588
+ cost: `$${ws.cost.toFixed(4)}`,
1589
+ outputPreview: ws.output.slice(0, 300) + (ws.output.length > 300 ? "..." : "")
1590
+ }))
1591
+ };
1592
+ printResults(result);
1593
+ } catch (err) {
1594
+ console.log(` \u2717 ${err.message}`);
1595
+ console.log(" Fix: Check your ClawAPI key or internet connection.\n");
1596
+ }
1597
+ }
1598
+ function printResults(result) {
1599
+ console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
1600
+ console.log(" \u{1F4CB} Mission complete!\n");
1601
+ for (const ws of result.workStreams) {
1602
+ const icon = ws.status === "completed" ? "\u2705" : "\u274C";
1603
+ console.log(` ${icon} ${ws.title}`);
1604
+ console.log(` Role: ${ws.role} (${ws.model})`);
1605
+ console.log(` Cost: ${ws.cost}`);
1606
+ if (ws.outputPreview) {
1607
+ const preview = ws.outputPreview.slice(0, 120).replace(/\n/g, " ");
1608
+ console.log(` Preview: ${preview}...`);
1609
+ }
1610
+ console.log("");
1611
+ }
1612
+ console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
1613
+ console.log(` Total cost: ${result.totalCost}`);
1614
+ console.log(` Total time: ${result.totalTime}`);
1615
+ console.log(` Work streams: ${result.workStreams.length}`);
1616
+ console.log("");
1617
+ console.log(" You are the Chairman. Approve, revise, or override.");
1618
+ console.log("");
1619
+ }
1620
+
1621
+ // src/commands/status.ts
1622
+ init_utils();
1623
+ async function statusCommand() {
1624
+ banner();
1625
+ const config = readConfig();
1626
+ if (!config) {
1627
+ console.log(" \u2717 No company set up yet.");
1628
+ console.log(" Fix: Run `npx clawcompany` to get started.\n");
1629
+ return;
1630
+ }
1631
+ console.log(` Company: ${config.companyName}`);
1632
+ console.log(` Template: ${config.template}`);
1633
+ console.log(` Created: ${new Date(config.createdAt).toLocaleDateString()}`);
1634
+ console.log("");
1635
+ const running = await isServerRunning(config.serverPort);
1636
+ if (running) {
1637
+ console.log(` \u2713 Server running at http://localhost:${config.serverPort}`);
1638
+ try {
1639
+ const roles = await apiGet("/api/roles", config.serverPort);
1640
+ const active = roles.filter((r) => r.isActive && r.budgetTier !== "survive");
1641
+ console.log(` \u2713 ${active.length} active roles
1642
+ `);
1643
+ const maxName = Math.max(...active.map((r) => r.name.length));
1644
+ for (const role2 of active) {
1645
+ console.log(` ${role2.name.padEnd(maxName + 2)}\u2192 ${role2.model}`);
1646
+ }
1647
+ console.log("");
1648
+ } catch {
1649
+ console.log(" \u26A0 Could not fetch roles.\n");
1650
+ }
1651
+ } else {
1652
+ console.log(` \u2717 Server not running.`);
1653
+ console.log(` Fix: Run \`clawcompany\` to start.
1654
+ `);
1655
+ }
1656
+ }
1657
+
1658
+ // src/index.ts
1659
+ var program = new Command();
1660
+ program.name("clawcompany").description("Build for OPC. Every human being is a chairman.").version("0.1.0");
1661
+ program.command("init", { isDefault: true }).description("Set up your AI company (runs automatically on first use)").action(initCommand);
1662
+ program.command("mission <goal>").description("Give your company a mission").option("-t, --template <name>", "Override decomposition template").action(missionCommand);
1663
+ program.command("status").description("Check company status and active missions").action(statusCommand);
1664
+ var role = program.command("role").description("Manage roles");
1665
+ role.command("list").description("List all roles and their model bindings").action(async () => {
1666
+ const { roleListCommand: roleListCommand2 } = await Promise.resolve().then(() => (init_role(), role_exports));
1667
+ await roleListCommand2();
1668
+ });
1669
+ role.command("set <roleId>").description("Modify a role").option("-m, --model <model>", "Set model").option("-p, --provider <provider>", "Set provider").option("-n, --name <name>", "Set display name").action(async (roleId, opts) => {
1670
+ const { roleSetCommand: roleSetCommand2 } = await Promise.resolve().then(() => (init_role(), role_exports));
1671
+ await roleSetCommand2(roleId, opts);
1672
+ });
1673
+ program.parse();