clawcompany 0.1.0 → 0.3.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 +800 -114
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import { join } from "path";
14
14
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
15
15
  function banner() {
16
16
  console.log("");
17
- console.log(" \u{1F99E} ClawCompany v0.1.0");
17
+ console.log(" \u{1F99E} ClawCompany v0.3.0");
18
18
  console.log(" Build for OPC. Every human being is a chairman.");
19
19
  console.log("");
20
20
  }
@@ -205,7 +205,7 @@ COST AWARENESS:
205
205
  budgetTier: "earn",
206
206
  budgetMonthly: null,
207
207
  maxTokensPerTask: null,
208
- tools: ["http", "filesystem"],
208
+ tools: ["http", "filesystem", "web_fetch", "web_search"],
209
209
  skills: [],
210
210
  isBuiltin: true,
211
211
  isActive: true,
@@ -276,7 +276,7 @@ Own marketing strategy, content creation, brand voice, growth initiatives. Write
276
276
  budgetTier: "earn",
277
277
  budgetMonthly: null,
278
278
  maxTokensPerTask: null,
279
- tools: ["http", "filesystem"],
279
+ tools: ["http", "filesystem", "web_fetch", "web_search"],
280
280
  skills: [],
281
281
  isBuiltin: true,
282
282
  isActive: true,
@@ -300,7 +300,7 @@ Conduct deep research \u2014 gather information, evaluate sources, analyze compe
300
300
  budgetTier: "earn",
301
301
  budgetMonthly: null,
302
302
  maxTokensPerTask: null,
303
- tools: ["http", "filesystem"],
303
+ tools: ["http", "filesystem", "web_fetch", "web_search"],
304
304
  skills: [],
305
305
  isBuiltin: true,
306
306
  isActive: true,
@@ -315,7 +315,7 @@ Conduct deep research \u2014 gather information, evaluate sources, analyze compe
315
315
  systemPrompt: `You are an Analyst. You report to the CFO or CEO.
316
316
 
317
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",
318
+ model: "gpt-5-mini",
319
319
  provider: "clawapi",
320
320
  reportsTo: "cfo",
321
321
  canDelegateTo: ["worker"],
@@ -323,7 +323,7 @@ Analyze data, detect patterns, calculate metrics, build models. Show calculation
323
323
  budgetTier: "save",
324
324
  budgetMonthly: null,
325
325
  maxTokensPerTask: null,
326
- tools: ["http", "filesystem", "code_interpreter"],
326
+ tools: ["http", "filesystem", "code_interpreter", "web_fetch", "web_search"],
327
327
  skills: [],
328
328
  isBuiltin: true,
329
329
  isActive: true,
@@ -391,7 +391,7 @@ Prepare briefings, format reports, summarize documents, organize information. Ma
391
391
  budgetTier: "save",
392
392
  budgetMonthly: null,
393
393
  maxTokensPerTask: null,
394
- tools: ["filesystem", "http"],
394
+ tools: ["filesystem", "http", "web_fetch", "web_search"],
395
395
  skills: [],
396
396
  isBuiltin: true,
397
397
  isActive: true,
@@ -472,6 +472,390 @@ var init_api_paths = __esm({
472
472
  }
473
473
  });
474
474
 
475
+ // ../packages/shared/src/market.ts
476
+ function searchMarket(query, category) {
477
+ const q = query.toLowerCase();
478
+ return MARKET_CATALOG.filter((item) => {
479
+ if (category && item.category !== category) return false;
480
+ return item.name.toLowerCase().includes(q) || item.description.toLowerCase().includes(q) || item.tags.some((t) => t.includes(q));
481
+ });
482
+ }
483
+ function getMarketItem(id) {
484
+ return MARKET_CATALOG.find((item) => item.id === id);
485
+ }
486
+ function listCategory(category) {
487
+ return MARKET_CATALOG.filter((item) => item.category === category);
488
+ }
489
+ function getTemplateRoles(templateId) {
490
+ return TEMPLATE_ROLES[templateId];
491
+ }
492
+ var MARKET_CATALOG, TEMPLATE_ROLES;
493
+ var init_market = __esm({
494
+ "../packages/shared/src/market.ts"() {
495
+ "use strict";
496
+ MARKET_CATALOG = [
497
+ // ═══ Templates ═══
498
+ {
499
+ id: "default",
500
+ name: "Default Company",
501
+ category: "template",
502
+ description: "Full AI company. CEO + CTO + CFO + CMO + Researcher + Analyst + Engineer + Secretary + Worker.",
503
+ author: "clawcompany",
504
+ version: "1.0.0",
505
+ tags: ["general", "full-team"],
506
+ license: "MIT",
507
+ tier: "free",
508
+ rolesCount: 9,
509
+ estimatedCost: "$0.03-0.08 per mission"
510
+ },
511
+ {
512
+ id: "trading-desk",
513
+ name: "Trading Desk",
514
+ category: "template",
515
+ description: "Crypto/DeFi trading with market analysis, execution, and risk management.",
516
+ author: "clawcompany",
517
+ version: "1.0.0",
518
+ tags: ["crypto", "defi", "trading", "finance"],
519
+ license: "MIT",
520
+ tier: "free",
521
+ rolesCount: 11,
522
+ estimatedCost: "$0.05-0.15 per mission"
523
+ },
524
+ {
525
+ id: "content-agency",
526
+ name: "Content Agency",
527
+ category: "template",
528
+ description: "Content production with writers, editors, and SEO specialists.",
529
+ author: "clawcompany",
530
+ version: "1.0.0",
531
+ tags: ["content", "writing", "marketing", "seo"],
532
+ license: "MIT",
533
+ tier: "free",
534
+ rolesCount: 10,
535
+ estimatedCost: "$0.04-0.10 per mission"
536
+ },
537
+ {
538
+ id: "dev-shop",
539
+ name: "Dev Shop",
540
+ category: "template",
541
+ description: "Software development with CTO, engineers, QA, and DevOps.",
542
+ author: "clawcompany",
543
+ version: "1.0.0",
544
+ tags: ["software", "development", "engineering", "devops"],
545
+ license: "MIT",
546
+ tier: "free",
547
+ rolesCount: 10,
548
+ estimatedCost: "$0.05-0.12 per mission"
549
+ },
550
+ {
551
+ id: "solo-founder",
552
+ name: "Solo Founder",
553
+ category: "template",
554
+ description: "Maximum efficiency. CEO + Worker only. Cheapest option.",
555
+ author: "clawcompany",
556
+ version: "1.0.0",
557
+ tags: ["solo", "minimal", "cheap", "bootstrap"],
558
+ license: "MIT",
559
+ tier: "free",
560
+ rolesCount: 2,
561
+ estimatedCost: "$0.01-0.03 per mission"
562
+ },
563
+ {
564
+ id: "legal-firm",
565
+ name: "Legal Firm",
566
+ category: "template",
567
+ description: "Contract analysis, legal research, compliance review, document drafting.",
568
+ author: "clawcompany",
569
+ version: "1.0.0",
570
+ tags: ["legal", "contracts", "compliance", "law"],
571
+ license: "MIT",
572
+ tier: "free",
573
+ rolesCount: 8,
574
+ estimatedCost: "$0.05-0.12 per mission"
575
+ },
576
+ {
577
+ id: "ecommerce",
578
+ name: "E-commerce Operator",
579
+ category: "template",
580
+ description: "Product listings, customer support, inventory management, ad optimization.",
581
+ author: "clawcompany",
582
+ version: "1.0.0",
583
+ tags: ["ecommerce", "retail", "shopify", "amazon"],
584
+ license: "MIT",
585
+ tier: "free",
586
+ rolesCount: 9,
587
+ estimatedCost: "$0.04-0.10 per mission"
588
+ },
589
+ {
590
+ id: "consulting",
591
+ name: "Consulting Firm",
592
+ category: "template",
593
+ description: "Client research, proposal writing, deliverable production, project management.",
594
+ author: "clawcompany",
595
+ version: "1.0.0",
596
+ tags: ["consulting", "proposals", "deliverables", "client"],
597
+ license: "MIT",
598
+ tier: "free",
599
+ rolesCount: 8,
600
+ estimatedCost: "$0.04-0.10 per mission"
601
+ },
602
+ // ═══ Skills ═══
603
+ {
604
+ id: "skill:web-scraper",
605
+ name: "Web Scraper",
606
+ category: "skill",
607
+ description: "Extract structured data from web pages. Supports pagination and dynamic content.",
608
+ author: "clawcompany",
609
+ version: "1.0.0",
610
+ tags: ["web", "scraping", "data", "extraction"],
611
+ license: "MIT",
612
+ tier: "free"
613
+ },
614
+ {
615
+ id: "skill:pdf-generator",
616
+ name: "PDF Generator",
617
+ category: "skill",
618
+ description: "Generate professional PDF reports from markdown or structured data.",
619
+ author: "clawcompany",
620
+ version: "1.0.0",
621
+ tags: ["pdf", "report", "document", "export"],
622
+ license: "MIT",
623
+ tier: "free"
624
+ },
625
+ {
626
+ id: "skill:price-feed",
627
+ name: "Crypto Price Feed",
628
+ category: "skill",
629
+ description: "Real-time cryptocurrency prices from multiple exchanges.",
630
+ author: "clawcompany",
631
+ version: "1.0.0",
632
+ tags: ["crypto", "price", "exchange", "data"],
633
+ license: "MIT",
634
+ tier: "free"
635
+ },
636
+ {
637
+ id: "skill:email-sender",
638
+ name: "Email Sender",
639
+ category: "skill",
640
+ description: "Send emails via SMTP or API (SendGrid, Resend, Mailgun).",
641
+ author: "clawcompany",
642
+ version: "1.0.0",
643
+ tags: ["email", "smtp", "sendgrid", "notification"],
644
+ license: "MIT",
645
+ tier: "free"
646
+ },
647
+ {
648
+ id: "skill:github-ops",
649
+ name: "GitHub Operations",
650
+ category: "skill",
651
+ description: "Create issues, PRs, review code, manage repos via GitHub API.",
652
+ author: "clawcompany",
653
+ version: "1.0.0",
654
+ tags: ["github", "git", "code", "devops"],
655
+ license: "MIT",
656
+ tier: "free"
657
+ }
658
+ ];
659
+ TEMPLATE_ROLES = {
660
+ "default": {
661
+ ceo: {},
662
+ cto: {},
663
+ cfo: {},
664
+ cmo: {},
665
+ researcher: {},
666
+ analyst: {},
667
+ engineer: {},
668
+ secretary: {},
669
+ worker: {},
670
+ fallback_a: {},
671
+ fallback_b: {}
672
+ },
673
+ "trading-desk": {
674
+ ceo: {},
675
+ cto: {},
676
+ cfo: {},
677
+ analyst: {},
678
+ researcher: {},
679
+ engineer: { isActive: false },
680
+ cmo: { isActive: false },
681
+ secretary: {},
682
+ worker: {},
683
+ custom_trader: {
684
+ name: "Trader",
685
+ description: "Execute trades, manage positions, risk control",
686
+ model: "claude-sonnet-4-6",
687
+ reportsTo: "cto",
688
+ tools: ["http", "shell"]
689
+ },
690
+ custom_data_collector: {
691
+ name: "Data Collector",
692
+ description: "Fetch on-chain data, aggregate feeds",
693
+ model: "gemini-3.1-flash-lite",
694
+ reportsTo: "analyst",
695
+ tools: ["http", "filesystem"]
696
+ },
697
+ fallback_a: {},
698
+ fallback_b: {}
699
+ },
700
+ "content-agency": {
701
+ ceo: {},
702
+ cmo: {},
703
+ cto: { isActive: false },
704
+ cfo: { isActive: false },
705
+ researcher: {},
706
+ secretary: {},
707
+ worker: {},
708
+ analyst: { isActive: false },
709
+ engineer: { isActive: false },
710
+ custom_writer: {
711
+ name: "Writer",
712
+ description: "Draft articles, blog posts, social media content",
713
+ model: "claude-sonnet-4-6",
714
+ reportsTo: "cmo",
715
+ tools: ["http", "filesystem"]
716
+ },
717
+ custom_editor: {
718
+ name: "Editor",
719
+ description: "Review and improve content, fact-check, style guide",
720
+ model: "claude-opus-4-6",
721
+ reportsTo: "cmo",
722
+ tools: ["filesystem"]
723
+ },
724
+ custom_seo: {
725
+ name: "SEO Specialist",
726
+ description: "Keyword research, meta descriptions, optimization",
727
+ model: "gpt-5-mini",
728
+ reportsTo: "cmo",
729
+ tools: ["http"]
730
+ },
731
+ fallback_a: {},
732
+ fallback_b: {}
733
+ },
734
+ "dev-shop": {
735
+ ceo: {},
736
+ cto: {},
737
+ engineer: {},
738
+ cfo: { isActive: false },
739
+ cmo: { isActive: false },
740
+ researcher: {},
741
+ analyst: { isActive: false },
742
+ secretary: {},
743
+ worker: {},
744
+ custom_qa: {
745
+ name: "QA Engineer",
746
+ description: "Write and run tests, report bugs, verify fixes",
747
+ model: "gpt-5-mini",
748
+ reportsTo: "cto",
749
+ tools: ["shell", "filesystem"]
750
+ },
751
+ custom_devops: {
752
+ name: "DevOps",
753
+ description: "CI/CD, deployment, infrastructure",
754
+ model: "gpt-5.4",
755
+ reportsTo: "cto",
756
+ tools: ["shell", "filesystem", "http"]
757
+ },
758
+ fallback_a: {},
759
+ fallback_b: {}
760
+ },
761
+ "solo-founder": {
762
+ ceo: {},
763
+ cto: { isActive: false },
764
+ cfo: { isActive: false },
765
+ cmo: { isActive: false },
766
+ researcher: { isActive: false },
767
+ analyst: { isActive: false },
768
+ engineer: { isActive: false },
769
+ secretary: { isActive: false },
770
+ worker: { reportsTo: "ceo" },
771
+ fallback_a: {},
772
+ fallback_b: {}
773
+ },
774
+ "legal-firm": {
775
+ ceo: {},
776
+ cfo: {},
777
+ cto: { isActive: false },
778
+ cmo: { isActive: false },
779
+ researcher: {},
780
+ analyst: {},
781
+ engineer: { isActive: false },
782
+ secretary: {},
783
+ worker: {},
784
+ custom_paralegal: {
785
+ name: "Paralegal",
786
+ description: "Document review, case research, filing preparation",
787
+ model: "claude-sonnet-4-6",
788
+ reportsTo: "ceo",
789
+ tools: ["http", "filesystem"]
790
+ },
791
+ custom_contract_analyst: {
792
+ name: "Contract Analyst",
793
+ description: "Clause analysis, risk assessment, redlining",
794
+ model: "gpt-5.4",
795
+ reportsTo: "ceo",
796
+ tools: ["filesystem"]
797
+ },
798
+ fallback_a: {},
799
+ fallback_b: {}
800
+ },
801
+ "ecommerce": {
802
+ ceo: {},
803
+ cfo: {},
804
+ cmo: {},
805
+ cto: { isActive: false },
806
+ researcher: {},
807
+ analyst: {},
808
+ engineer: { isActive: false },
809
+ secretary: {},
810
+ worker: {},
811
+ custom_listing_manager: {
812
+ name: "Listing Manager",
813
+ description: "Product listings, descriptions, pricing optimization",
814
+ model: "claude-sonnet-4-6",
815
+ reportsTo: "cmo",
816
+ tools: ["http", "filesystem"]
817
+ },
818
+ custom_support_agent: {
819
+ name: "Support Agent",
820
+ description: "Customer inquiries, returns, order issues",
821
+ model: "gemini-3.1-flash-lite",
822
+ reportsTo: "ceo",
823
+ tools: ["http"]
824
+ },
825
+ fallback_a: {},
826
+ fallback_b: {}
827
+ },
828
+ "consulting": {
829
+ ceo: {},
830
+ cfo: {},
831
+ cto: { isActive: false },
832
+ cmo: { isActive: false },
833
+ researcher: {},
834
+ analyst: {},
835
+ engineer: { isActive: false },
836
+ secretary: {},
837
+ worker: {},
838
+ custom_consultant: {
839
+ name: "Consultant",
840
+ description: "Client analysis, strategy recommendations, deliverables",
841
+ model: "claude-sonnet-4-6",
842
+ reportsTo: "ceo",
843
+ tools: ["http", "filesystem"]
844
+ },
845
+ custom_proposal_writer: {
846
+ name: "Proposal Writer",
847
+ description: "RFP responses, pitch decks, project scoping",
848
+ model: "claude-sonnet-4-6",
849
+ reportsTo: "ceo",
850
+ tools: ["filesystem"]
851
+ },
852
+ fallback_a: {},
853
+ fallback_b: {}
854
+ }
855
+ };
856
+ }
857
+ });
858
+
475
859
  // ../packages/shared/src/index.ts
476
860
  var init_src = __esm({
477
861
  "../packages/shared/src/index.ts"() {
@@ -479,6 +863,7 @@ var init_src = __esm({
479
863
  init_types();
480
864
  init_defaults();
481
865
  init_api_paths();
866
+ init_market();
482
867
  }
483
868
  });
484
869
 
@@ -553,6 +938,153 @@ var init_role = __esm({
553
938
  }
554
939
  });
555
940
 
941
+ // src/commands/market.ts
942
+ var market_exports = {};
943
+ __export(market_exports, {
944
+ marketInstallCommand: () => marketInstallCommand,
945
+ marketListCommand: () => marketListCommand,
946
+ marketSearchCommand: () => marketSearchCommand
947
+ });
948
+ async function marketListCommand(category) {
949
+ banner();
950
+ console.log(" \u{1F99E} ClawMarket \u2014 Download a company. One click. It runs.\n");
951
+ const validCategories = ["template", "skill", "tool", "service"];
952
+ if (category && validCategories.includes(category)) {
953
+ const items = listCategory(category);
954
+ printItems(items, category);
955
+ } else {
956
+ for (const cat of validCategories) {
957
+ const items = listCategory(cat);
958
+ if (items.length > 0) {
959
+ printItems(items, cat);
960
+ }
961
+ }
962
+ }
963
+ console.log(" Commands:");
964
+ console.log(' clawcompany market search "trading"');
965
+ console.log(" clawcompany market install trading-desk");
966
+ console.log(" clawcompany market install skill:web-scraper");
967
+ console.log("");
968
+ }
969
+ async function marketSearchCommand(query) {
970
+ banner();
971
+ console.log(` \u{1F50D} Searching ClawMarket for "${query}"...
972
+ `);
973
+ const results = searchMarket(query);
974
+ if (results.length === 0) {
975
+ console.log(" No results found.\n");
976
+ console.log(" Browse all: clawcompany market list");
977
+ console.log("");
978
+ return;
979
+ }
980
+ printItems(results, "results");
981
+ console.log(` ${results.length} result${results.length > 1 ? "s" : ""} found.
982
+ `);
983
+ }
984
+ async function marketInstallCommand(itemId) {
985
+ banner();
986
+ const item = getMarketItem(itemId);
987
+ if (!item) {
988
+ console.log(` \u2717 "${itemId}" not found in ClawMarket.
989
+ `);
990
+ console.log(" Browse available items: clawcompany market list");
991
+ console.log("");
992
+ return;
993
+ }
994
+ const config = readConfig();
995
+ if (!config) {
996
+ console.log(" \u2717 No company set up yet.");
997
+ console.log(" Fix: Run `npx clawcompany` to get started.\n");
998
+ return;
999
+ }
1000
+ if (item.category === "template") {
1001
+ const templateRoles = getTemplateRoles(itemId);
1002
+ if (!templateRoles) {
1003
+ console.log(` \u2717 Template "${itemId}" has no role definitions.
1004
+ `);
1005
+ return;
1006
+ }
1007
+ console.log(` Installing template: ${item.name}
1008
+ `);
1009
+ console.log(` ${item.description}`);
1010
+ if (item.estimatedCost) console.log(` Est. cost: ${item.estimatedCost}`);
1011
+ console.log("");
1012
+ config.template = item.id;
1013
+ config.roles = templateRoles;
1014
+ writeConfig(config);
1015
+ console.log(" \u2713 Template installed. New org chart:\n");
1016
+ console.log(" \u{1F464} Chairman = Human (you)\n");
1017
+ const activeRoles = [];
1018
+ const disabledRoles = [];
1019
+ for (const [id, overrides] of Object.entries(templateRoles)) {
1020
+ if (id === "fallback_a" || id === "fallback_b") continue;
1021
+ if (overrides.isActive === false) {
1022
+ const builtin2 = BUILTIN_ROLES.find((r) => r.id === id);
1023
+ disabledRoles.push(builtin2?.name ?? id);
1024
+ continue;
1025
+ }
1026
+ const builtin = BUILTIN_ROLES.find((r) => r.id === id);
1027
+ const name = overrides.name ?? builtin?.name ?? id;
1028
+ const model = overrides.model ?? builtin?.model ?? "default";
1029
+ const reportsTo = overrides.reportsTo ?? builtin?.reportsTo ?? "ceo";
1030
+ const pricing = MODEL_PRICING[model];
1031
+ const cost = pricing ? `$${pricing.input}/$${pricing.output}` : "";
1032
+ activeRoles.push({ id, name, model, reportsTo });
1033
+ const reports = reportsTo ? `\u2192 ${reportsTo}` : "\u2192 Human";
1034
+ const maxN = 18;
1035
+ console.log(` ${name.padEnd(maxN)} ${model.padEnd(24)} ${cost.padEnd(12)} reports ${reports}`);
1036
+ }
1037
+ console.log("");
1038
+ if (disabledRoles.length > 0) {
1039
+ console.log(` Disabled: ${disabledRoles.join(", ")}`);
1040
+ console.log("");
1041
+ }
1042
+ console.log(` Total: ${activeRoles.length} active roles
1043
+ `);
1044
+ console.log(` "${config.companyName}" is now running the ${item.name} template.`);
1045
+ console.log(' Run a mission: clawcompany mission "your goal here"');
1046
+ console.log("");
1047
+ } else if (item.category === "skill") {
1048
+ console.log(` Installing skill: ${item.name}
1049
+ `);
1050
+ console.log(` ${item.description}`);
1051
+ console.log("");
1052
+ console.log(` \u2713 Skill "${item.name}" installed.`);
1053
+ console.log(" Agents can now use this skill in missions.\n");
1054
+ } else {
1055
+ console.log(` \u2713 ${item.name} noted. Full install coming in Phase 2.
1056
+ `);
1057
+ }
1058
+ }
1059
+ function printItems(items, label) {
1060
+ const categoryEmoji = {
1061
+ template: "\u{1F3E2}",
1062
+ skill: "\u26A1",
1063
+ tool: "\u{1F527}",
1064
+ service: "\u{1F517}",
1065
+ results: "\u{1F50D}"
1066
+ };
1067
+ const emoji = categoryEmoji[label] ?? "\u{1F4E6}";
1068
+ const title = label.charAt(0).toUpperCase() + label.slice(1) + "s";
1069
+ console.log(` ${emoji} ${title}:
1070
+ `);
1071
+ const maxName = Math.max(...items.map((i) => i.name.length));
1072
+ for (const item of items) {
1073
+ const tier = item.tier === "premium" ? " [PRO]" : "";
1074
+ const roles = item.rolesCount ? ` (${item.rolesCount} roles)` : "";
1075
+ const cost = item.estimatedCost ? ` \xB7 ${item.estimatedCost}` : "";
1076
+ console.log(` ${item.name.padEnd(maxName + 2)} ${item.description.slice(0, 60)}${tier}${roles}${cost}`);
1077
+ }
1078
+ console.log("");
1079
+ }
1080
+ var init_market2 = __esm({
1081
+ "src/commands/market.ts"() {
1082
+ "use strict";
1083
+ init_utils();
1084
+ init_src();
1085
+ }
1086
+ });
1087
+
556
1088
  // src/index.ts
557
1089
  import { Command } from "commander";
558
1090
 
@@ -689,7 +1221,7 @@ function calculateCost(model, inputTokens, outputTokens) {
689
1221
  }
690
1222
 
691
1223
  // ../packages/providers/src/openai-compatible.ts
692
- var OpenAICompatibleProvider = class {
1224
+ var OpenAICompatibleProvider = class _OpenAICompatibleProvider {
693
1225
  constructor(config) {
694
1226
  this.config = config;
695
1227
  this.id = config.id;
@@ -707,68 +1239,71 @@ var OpenAICompatibleProvider = class {
707
1239
  baseUrl;
708
1240
  apiKey;
709
1241
  knownModels = /* @__PURE__ */ new Set();
1242
+ /**
1243
+ * Model fallback map: if a model fails with 504 (timeout),
1244
+ * automatically retry with a cheaper/faster model.
1245
+ */
1246
+ static MODEL_FALLBACK = {
1247
+ "gpt-5-mini": "gemini-3.1-flash-lite",
1248
+ // reasoning model timeout → fast model
1249
+ "gpt-5.4": "claude-sonnet-4-6",
1250
+ // GPT timeout → Sonnet
1251
+ "claude-opus-4-6": "claude-sonnet-4-6"
1252
+ // Opus timeout → Sonnet
1253
+ };
710
1254
  async chat(params) {
1255
+ try {
1256
+ return await this._doChat(params);
1257
+ } catch (err) {
1258
+ if (err instanceof ProviderError && (err.status === 504 || err.status === 400)) {
1259
+ const fallbackModel = _OpenAICompatibleProvider.MODEL_FALLBACK[params.model];
1260
+ if (fallbackModel) {
1261
+ console.log(` \u26A0 ${params.model} timed out, falling back to ${fallbackModel}`);
1262
+ return await this._doChat({ ...params, model: fallbackModel });
1263
+ }
1264
+ }
1265
+ throw err;
1266
+ }
1267
+ }
1268
+ async _doChat(params) {
711
1269
  const url = `${this.baseUrl}/chat/completions`;
712
- const isReasoning = this.isReasoningModel(params.model);
713
1270
  const body = {
714
1271
  model: params.model,
715
- messages: params.messages,
1272
+ messages: this.toApiMessages(params.messages),
1273
+ temperature: params.temperature ?? 0.7,
716
1274
  max_tokens: params.maxTokens ?? 4096,
717
1275
  stream: true
718
1276
  };
719
- if (!isReasoning) {
720
- body.temperature = params.temperature ?? 0.7;
1277
+ if (this.isReasoningModel(params.model)) {
1278
+ delete body.temperature;
721
1279
  }
722
1280
  if (params.tools?.length) {
723
1281
  body.tools = params.tools;
724
1282
  }
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;
1283
+ const response = await fetch(url, {
1284
+ method: "POST",
1285
+ headers: {
1286
+ "Authorization": `Bearer ${this.apiKey}`,
1287
+ "Content-Type": "application/json"
1288
+ },
1289
+ body: JSON.stringify(body)
1290
+ });
1291
+ if (!response.ok) {
1292
+ const errorBody = await response.text().catch(() => "");
1293
+ throw new ProviderError(
1294
+ response.status,
1295
+ `${this.name} API error ${response.status}: ${errorBody}`,
1296
+ this.id
1297
+ );
757
1298
  }
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
1299
  let content = "";
1300
+ let model = params.model;
767
1301
  let finishReason = "stop";
768
- let toolCalls = [];
769
- let inputTokens = 0;
770
- let outputTokens = 0;
771
- let actualModel = model;
1302
+ let promptTokens = 0;
1303
+ let completionTokens = 0;
1304
+ const toolCallBuffers = /* @__PURE__ */ new Map();
1305
+ const reader = response.body.getReader();
1306
+ const decoder = new TextDecoder();
772
1307
  let buffer = "";
773
1308
  while (true) {
774
1309
  const { done, value } = await reader.read();
@@ -777,68 +1312,71 @@ var OpenAICompatibleProvider = class {
777
1312
  const lines = buffer.split("\n");
778
1313
  buffer = lines.pop() ?? "";
779
1314
  for (const line of lines) {
780
- const trimmed = line.trim();
781
- if (!trimmed || trimmed === "data: [DONE]") continue;
782
- if (!trimmed.startsWith("data: ")) continue;
1315
+ if (!line.startsWith("data: ") || line === "data: [DONE]") continue;
783
1316
  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
- }
1317
+ const chunk = JSON.parse(line.slice(6));
1318
+ const delta = chunk.choices?.[0]?.delta;
1319
+ if (delta?.content) content += delta.content;
1320
+ if (chunk.model) model = chunk.model;
1321
+ const fr = chunk.choices?.[0]?.finish_reason;
1322
+ if (fr) finishReason = fr;
790
1323
  if (delta?.tool_calls) {
791
1324
  for (const tc of delta.tool_calls) {
792
1325
  const idx = tc.index ?? 0;
793
- if (!toolCalls[idx]) {
794
- toolCalls[idx] = {
795
- id: tc.id ?? "",
796
- type: "function",
797
- function: { name: "", arguments: "" }
798
- };
1326
+ if (!toolCallBuffers.has(idx)) {
1327
+ toolCallBuffers.set(idx, { id: tc.id ?? `call_${idx}`, name: "", args: "" });
799
1328
  }
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;
1329
+ const buf = toolCallBuffers.get(idx);
1330
+ if (tc.id) buf.id = tc.id;
1331
+ if (tc.function?.name) buf.name = tc.function.name;
1332
+ if (tc.function?.arguments) buf.args += tc.function.arguments;
803
1333
  }
804
1334
  }
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;
1335
+ if (chunk.usage) {
1336
+ promptTokens = chunk.usage.prompt_tokens ?? 0;
1337
+ completionTokens = chunk.usage.completion_tokens ?? 0;
814
1338
  }
815
1339
  } catch {
816
1340
  }
817
1341
  }
818
1342
  }
819
- if (outputTokens === 0 && content.length > 0) {
820
- outputTokens = Math.ceil(content.length / 4);
1343
+ const toolCalls = [];
1344
+ for (const [, buf] of [...toolCallBuffers.entries()].sort((a, b) => a[0] - b[0])) {
1345
+ toolCalls.push({ id: buf.id, type: "function", function: { name: buf.name, arguments: buf.args } });
821
1346
  }
822
- const validToolCalls = toolCalls.filter((tc) => tc.id && tc.function.name);
823
1347
  return {
824
1348
  content,
825
- model: actualModel,
1349
+ model,
826
1350
  provider: this.id,
827
1351
  usage: {
828
- inputTokens,
829
- outputTokens,
830
- cost: calculateCost(model, inputTokens, outputTokens)
1352
+ inputTokens: promptTokens,
1353
+ outputTokens: completionTokens,
1354
+ cost: calculateCost(params.model, promptTokens, completionTokens)
831
1355
  },
832
- toolCalls: validToolCalls.length > 0 ? validToolCalls : void 0,
1356
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
833
1357
  finishReason
834
1358
  };
835
1359
  }
836
- /**
837
- * Reasoning models reject temperature parameter.
838
- */
1360
+ /** Convert internal message format to OpenAI API format */
1361
+ toApiMessages(messages) {
1362
+ return messages.map((msg) => {
1363
+ const out = {
1364
+ role: msg.role,
1365
+ content: msg.content
1366
+ };
1367
+ if (msg.toolCalls) {
1368
+ out.tool_calls = msg.toolCalls;
1369
+ if (!out.content) out.content = null;
1370
+ }
1371
+ if (msg.role === "tool" && msg.toolCallId) {
1372
+ out.tool_call_id = msg.toolCallId;
1373
+ }
1374
+ if (msg.name) out.name = msg.name;
1375
+ return out;
1376
+ });
1377
+ }
839
1378
  isReasoningModel(model) {
840
- const reasoning = ["gpt-5-mini", "o1", "o1-mini", "o1-preview", "o3", "o3-mini"];
841
- return reasoning.some((r) => model.includes(r));
1379
+ return /^(o1|o3|gpt-5-mini)/.test(model);
842
1380
  }
843
1381
  async listModels() {
844
1382
  if (Array.isArray(this.config.models)) {
@@ -1115,6 +1653,10 @@ var ToolExecutor = class {
1115
1653
  return this.execHttp(args);
1116
1654
  case "code_interpreter":
1117
1655
  return this.execCode(args);
1656
+ case "web_fetch":
1657
+ return this.execWebFetch(args);
1658
+ case "web_search":
1659
+ return this.execWebSearch(args);
1118
1660
  default:
1119
1661
  return `Unknown tool: ${toolName}`;
1120
1662
  }
@@ -1175,6 +1717,88 @@ ${text.slice(0, 5e3)}`;
1175
1717
  }
1176
1718
  return `Unsupported language: ${language}`;
1177
1719
  }
1720
+ async execWebFetch(args) {
1721
+ const { url, maxLength } = args;
1722
+ const limit = maxLength ?? 8e3;
1723
+ try {
1724
+ const response = await fetch(url, {
1725
+ headers: {
1726
+ "User-Agent": "ClawCompany/1.0 (AI Agent)",
1727
+ "Accept": "text/html,application/xhtml+xml,text/plain,application/json"
1728
+ },
1729
+ redirect: "follow",
1730
+ signal: AbortSignal.timeout(15e3)
1731
+ });
1732
+ if (!response.ok) {
1733
+ return `Error: HTTP ${response.status} ${response.statusText}`;
1734
+ }
1735
+ const contentType = response.headers.get("content-type") ?? "";
1736
+ const raw = await response.text();
1737
+ if (contentType.includes("application/json")) {
1738
+ return raw.slice(0, limit);
1739
+ }
1740
+ if (contentType.includes("text/html")) {
1741
+ const text = raw.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<nav[^>]*>[\s\S]*?<\/nav>/gi, "").replace(/<footer[^>]*>[\s\S]*?<\/footer>/gi, "").replace(/<header[^>]*>[\s\S]*?<\/header>/gi, "").replace(/<[^>]+>/g, " ").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/\s+/g, " ").trim();
1742
+ return text.slice(0, limit);
1743
+ }
1744
+ return raw.slice(0, limit);
1745
+ } catch (err) {
1746
+ if (err.name === "TimeoutError") return "Error: Request timed out (15s)";
1747
+ return `Error: ${err.message}`;
1748
+ }
1749
+ }
1750
+ async execWebSearch(args) {
1751
+ const { query, maxResults } = args;
1752
+ const limit = Math.min(maxResults ?? 5, 10);
1753
+ try {
1754
+ const encoded = encodeURIComponent(query);
1755
+ const response = await fetch(`https://html.duckduckgo.com/html/?q=${encoded}`, {
1756
+ headers: {
1757
+ "User-Agent": "ClawCompany/1.0 (AI Agent)"
1758
+ },
1759
+ signal: AbortSignal.timeout(1e4)
1760
+ });
1761
+ if (!response.ok) {
1762
+ return `Error: Search failed with HTTP ${response.status}`;
1763
+ }
1764
+ const html = await response.text();
1765
+ const results = [];
1766
+ const resultBlocks = html.split('class="result__body"');
1767
+ for (let i = 1; i < resultBlocks.length && results.length < limit; i++) {
1768
+ const block = resultBlocks[i];
1769
+ const titleMatch = block.match(/class="result__a"[^>]*>([^<]+)</);
1770
+ const title = titleMatch?.[1]?.trim() ?? "";
1771
+ const urlMatch = block.match(/href="\/\/duckduckgo\.com\/l\/\?[^"]*uddg=([^&"]+)/);
1772
+ const url = urlMatch?.[1] ? decodeURIComponent(urlMatch[1]) : "";
1773
+ const snippetMatch = block.match(/class="result__snippet"[^>]*>([\s\S]*?)<\/a>/);
1774
+ const snippet = snippetMatch?.[1]?.replace(/<[^>]+>/g, "")?.replace(/\s+/g, " ")?.trim() ?? "";
1775
+ if (title && url) {
1776
+ results.push({ title, url, snippet });
1777
+ }
1778
+ }
1779
+ if (results.length === 0) {
1780
+ return `No results found for "${query}"`;
1781
+ }
1782
+ let output = `Search results for "${query}":
1783
+
1784
+ `;
1785
+ for (let i = 0; i < results.length; i++) {
1786
+ output += `${i + 1}. ${results[i].title}
1787
+ `;
1788
+ output += ` URL: ${results[i].url}
1789
+ `;
1790
+ if (results[i].snippet) {
1791
+ output += ` ${results[i].snippet}
1792
+ `;
1793
+ }
1794
+ output += "\n";
1795
+ }
1796
+ return output;
1797
+ } catch (err) {
1798
+ if (err.name === "TimeoutError") return "Error: Search timed out (10s)";
1799
+ return `Error: ${err.message}`;
1800
+ }
1801
+ }
1178
1802
  };
1179
1803
 
1180
1804
  // ../packages/tools/src/index.ts
@@ -1241,6 +1865,36 @@ var BUILTIN_TOOLS = {
1241
1865
  required: ["language", "code"]
1242
1866
  }
1243
1867
  }
1868
+ },
1869
+ web_fetch: {
1870
+ type: "function",
1871
+ function: {
1872
+ name: "web_fetch",
1873
+ description: "Fetch a web page and extract its text content. Use this to read articles, documentation, blog posts, or any public web page. Returns cleaned text without HTML tags.",
1874
+ parameters: {
1875
+ type: "object",
1876
+ properties: {
1877
+ url: { type: "string", description: "URL of the web page to fetch" },
1878
+ maxLength: { type: "number", description: "Max characters to return (default: 8000)" }
1879
+ },
1880
+ required: ["url"]
1881
+ }
1882
+ }
1883
+ },
1884
+ web_search: {
1885
+ type: "function",
1886
+ function: {
1887
+ name: "web_search",
1888
+ description: "Search the web for current information. Returns a list of results with titles, URLs, and snippets. Use this to find up-to-date facts, news, prices, or research topics.",
1889
+ parameters: {
1890
+ type: "object",
1891
+ properties: {
1892
+ query: { type: "string", description: "Search query" },
1893
+ maxResults: { type: "number", description: "Max results to return (default: 5, max: 10)" }
1894
+ },
1895
+ required: ["query"]
1896
+ }
1897
+ }
1244
1898
  }
1245
1899
  };
1246
1900
  function getToolsForRole(toolNames) {
@@ -1266,7 +1920,7 @@ var AgentExecutor = class {
1266
1920
  content: this.buildTaskPrompt(task)
1267
1921
  }
1268
1922
  ];
1269
- const MAX_TURNS = 10;
1923
+ const MAX_TURNS = 15;
1270
1924
  for (let turn = 0; turn < MAX_TURNS; turn++) {
1271
1925
  const response = await this.router.chatAsRole(
1272
1926
  role2.id,
@@ -1478,24 +2132,40 @@ ${truncated}
1478
2132
  `;
1479
2133
  }
1480
2134
  }
1481
- const response = await this.router.chatAsRole(ws.assignTo, [
1482
- {
1483
- role: "user",
1484
- content: `## Task: ${ws.title}
1485
-
1486
- ${ws.description}
2135
+ const role2 = this.router.getRole(ws.assignTo);
2136
+ if (!role2) throw new Error(`Role "${ws.assignTo}" not found`);
2137
+ const result = await this.executor.execute(role2, {
2138
+ id: ws.id,
2139
+ companyId: "default",
2140
+ missionId: ws.missionId,
2141
+ workStreamId: ws.id,
2142
+ title: ws.title,
2143
+ description: `${ws.description}
1487
2144
 
1488
2145
  Complexity: ${ws.estimatedComplexity}${context}
1489
2146
 
1490
- Today's date is ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}. Complete this task. Provide your output clearly and concisely.`
1491
- }
1492
- ]);
2147
+ Today's date is ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.`,
2148
+ assignedTo: ws.assignTo,
2149
+ createdBy: "ceo",
2150
+ reportTo: "ceo",
2151
+ status: "in_progress",
2152
+ priority: 1,
2153
+ tokensIn: 0,
2154
+ tokensOut: 0,
2155
+ cost: 0,
2156
+ modelUsed: role2.model,
2157
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2158
+ });
2159
+ if (result.toolCallCount > 0) {
2160
+ console.log(` \u{1F527} ${result.toolCallCount} tool calls executed`);
2161
+ }
1493
2162
  return {
1494
- content: response.content,
1495
- cost: response.usage.cost,
1496
- tokensIn: response.usage.inputTokens,
1497
- tokensOut: response.usage.outputTokens,
1498
- model: response.model
2163
+ content: result.output,
2164
+ cost: result.cost,
2165
+ tokensIn: result.tokensIn,
2166
+ tokensOut: result.tokensOut,
2167
+ model: result.modelUsed,
2168
+ toolCallCount: result.toolCallCount
1499
2169
  };
1500
2170
  }
1501
2171
  topologicalSort(workStreams) {
@@ -1530,7 +2200,7 @@ async function missionCommand(goal) {
1530
2200
  if (await isServerRunning(config.serverPort)) {
1531
2201
  await runViaServer(goal, config.serverPort);
1532
2202
  } else {
1533
- await runInProcess(goal, config.apiKey);
2203
+ await runInProcess(goal, config);
1534
2204
  }
1535
2205
  }
1536
2206
  async function runViaServer(goal, port) {
@@ -1548,11 +2218,14 @@ async function runViaServer(goal, port) {
1548
2218
  `);
1549
2219
  }
1550
2220
  }
1551
- async function runInProcess(goal, apiKey) {
2221
+ async function runInProcess(goal, userConfig) {
1552
2222
  console.log(" Running in-process (no server needed)...\n");
1553
2223
  try {
1554
2224
  const clawConfig = getDefaultConfig();
1555
- clawConfig.providers[0].apiKey = apiKey;
2225
+ clawConfig.providers[0].apiKey = userConfig.apiKey;
2226
+ if (userConfig.roles) {
2227
+ clawConfig.roles = userConfig.roles;
2228
+ }
1556
2229
  const registry = new ProviderRegistry();
1557
2230
  await registry.loadFromConfig(clawConfig.providers);
1558
2231
  const router = new ModelRouter(registry, clawConfig);
@@ -1670,4 +2343,17 @@ role.command("set <roleId>").description("Modify a role").option("-m, --model <m
1670
2343
  const { roleSetCommand: roleSetCommand2 } = await Promise.resolve().then(() => (init_role(), role_exports));
1671
2344
  await roleSetCommand2(roleId, opts);
1672
2345
  });
2346
+ var market = program.command("market").description("Browse and install from ClawMarket");
2347
+ market.command("list [category]").description("Browse marketplace (category: template, skill, tool, service)").action(async (category) => {
2348
+ const { marketListCommand: marketListCommand2 } = await Promise.resolve().then(() => (init_market2(), market_exports));
2349
+ await marketListCommand2(category);
2350
+ });
2351
+ market.command("search <query>").description("Search the marketplace").action(async (query) => {
2352
+ const { marketSearchCommand: marketSearchCommand2 } = await Promise.resolve().then(() => (init_market2(), market_exports));
2353
+ await marketSearchCommand2(query);
2354
+ });
2355
+ market.command("install <itemId>").description("Install a template, skill, or tool").action(async (itemId) => {
2356
+ const { marketInstallCommand: marketInstallCommand2 } = await Promise.resolve().then(() => (init_market2(), market_exports));
2357
+ await marketInstallCommand2(itemId);
2358
+ });
1673
2359
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawcompany",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Build for OPC. Every human being is a chairman. AI company infrastructure — one key, 9 roles, 4 models.",
5
5
  "type": "module",
6
6
  "bin": { "clawcompany": "dist/index.js" },