@elisym/cli 0.11.0 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -338,6 +338,290 @@ var init_anthropic = __esm({
338
338
  }
339
339
  });
340
340
 
341
+ // src/llm/providers/openai-compatible.ts
342
+ function createOpenAICompatibleProvider(config) {
343
+ const billingMarkers = [...DEFAULT_BILLING_MARKERS, ...config.extraBillingMarkers ?? []];
344
+ function bodyLooksLikeBilling3(body) {
345
+ const lower = body.toLowerCase();
346
+ return billingMarkers.some((marker) => lower.includes(marker));
347
+ }
348
+ async function fetchModels4(apiKey, signal) {
349
+ try {
350
+ const response = await fetchWithTimeout(
351
+ `${config.baseUrl}/models`,
352
+ { method: "GET", headers: { Authorization: `Bearer ${apiKey}` } },
353
+ signal
354
+ );
355
+ if (!response.ok) {
356
+ return config.fallbackModels;
357
+ }
358
+ const data = await response.json();
359
+ const transformed = [];
360
+ for (const entry of data.data ?? []) {
361
+ const mapped = config.mapModelId ? config.mapModelId(entry.id) : entry.id;
362
+ if (mapped) {
363
+ transformed.push(mapped);
364
+ }
365
+ }
366
+ transformed.sort();
367
+ return transformed.length > 0 ? transformed : config.fallbackModels;
368
+ } catch {
369
+ return config.fallbackModels;
370
+ }
371
+ }
372
+ async function verifyKey3(apiKey, signal) {
373
+ try {
374
+ const response = await fetchWithTimeout(
375
+ `${config.baseUrl}/models`,
376
+ { method: "GET", headers: { Authorization: `Bearer ${apiKey}` } },
377
+ signal
378
+ );
379
+ if (response.ok) {
380
+ await response.body?.cancel().catch(() => void 0);
381
+ return { ok: true };
382
+ }
383
+ const body = (await response.text().catch(() => "")).slice(0, 500);
384
+ if (response.status === 401 || response.status === 403) {
385
+ return { ok: false, reason: "invalid", status: response.status, body };
386
+ }
387
+ return {
388
+ ok: false,
389
+ reason: "unavailable",
390
+ error: `HTTP ${response.status}: ${body.slice(0, 200)}`
391
+ };
392
+ } catch (error) {
393
+ const message = error instanceof Error ? error.message : String(error);
394
+ return { ok: false, reason: "unavailable", error: message };
395
+ }
396
+ }
397
+ async function verifyKeyDeep3(apiKey, model, signal) {
398
+ try {
399
+ const response = await fetchWithTimeout(
400
+ `${config.baseUrl}/chat/completions`,
401
+ {
402
+ method: "POST",
403
+ headers: {
404
+ "Content-Type": "application/json",
405
+ Authorization: `Bearer ${apiKey}`
406
+ },
407
+ body: JSON.stringify({
408
+ model,
409
+ max_tokens: 1,
410
+ messages: [{ role: "user", content: "." }]
411
+ })
412
+ },
413
+ signal
414
+ );
415
+ if (response.ok) {
416
+ await response.body?.cancel().catch(() => void 0);
417
+ return { ok: true };
418
+ }
419
+ const body = (await response.text().catch(() => "")).slice(0, 500);
420
+ if (response.status === 401 || response.status === 403) {
421
+ return { ok: false, reason: "invalid", status: response.status, body };
422
+ }
423
+ if (response.status === 402) {
424
+ return { ok: false, reason: "billing", status: response.status, body };
425
+ }
426
+ if ((response.status === 400 || response.status === 429) && bodyLooksLikeBilling3(body)) {
427
+ return { ok: false, reason: "billing", status: response.status, body };
428
+ }
429
+ return {
430
+ ok: false,
431
+ reason: "unavailable",
432
+ error: `HTTP ${response.status}: ${body.slice(0, 200)}`
433
+ };
434
+ } catch (error) {
435
+ const message = error instanceof Error ? error.message : String(error);
436
+ return { ok: false, reason: "unavailable", error: message };
437
+ }
438
+ }
439
+ function createClient(clientConfig) {
440
+ return new OpenAICompatibleClient({
441
+ apiKey: clientConfig.apiKey,
442
+ baseUrl: config.baseUrl,
443
+ model: clientConfig.model ?? config.defaultModel,
444
+ maxTokens: clientConfig.maxTokens ?? DEFAULT_MAX_TOKENS2,
445
+ providerLabel: config.displayName,
446
+ logUsage: clientConfig.logUsage
447
+ });
448
+ }
449
+ return {
450
+ id: config.id,
451
+ displayName: config.displayName,
452
+ envVar: config.envVar,
453
+ defaultModel: config.defaultModel,
454
+ fallbackModels: config.fallbackModels,
455
+ fetchModels: fetchModels4,
456
+ verifyKey: verifyKey3,
457
+ verifyKeyDeep: verifyKeyDeep3,
458
+ createClient
459
+ };
460
+ }
461
+ var DEFAULT_MAX_TOKENS2, DEFAULT_BILLING_MARKERS, OpenAICompatibleClient;
462
+ var init_openai_compatible = __esm({
463
+ "src/llm/providers/openai-compatible.ts"() {
464
+ init_http();
465
+ DEFAULT_MAX_TOKENS2 = 4096;
466
+ DEFAULT_BILLING_MARKERS = ["credit balance", "billing", "insufficient_quota", "insufficient"];
467
+ OpenAICompatibleClient = class {
468
+ constructor(config) {
469
+ this.config = config;
470
+ }
471
+ totalIn = 0;
472
+ totalOut = 0;
473
+ logTokens(usage) {
474
+ if (!this.config.logUsage || !usage) {
475
+ return;
476
+ }
477
+ const inputTokens = usage.prompt_tokens ?? 0;
478
+ const outputTokens = usage.completion_tokens ?? 0;
479
+ this.totalIn += inputTokens;
480
+ this.totalOut += outputTokens;
481
+ console.log(
482
+ ` [LLM] ${this.config.model} tokens: in=${inputTokens} out=${outputTokens} (total: in=${this.totalIn} out=${this.totalOut})`
483
+ );
484
+ }
485
+ async complete(systemPrompt, userInput, signal) {
486
+ const response = await fetchWithRetry(
487
+ `${this.config.baseUrl}/chat/completions`,
488
+ {
489
+ method: "POST",
490
+ headers: {
491
+ "Content-Type": "application/json",
492
+ Authorization: `Bearer ${this.config.apiKey}`
493
+ },
494
+ body: JSON.stringify({
495
+ model: this.config.model,
496
+ max_tokens: this.config.maxTokens,
497
+ messages: [
498
+ { role: "system", content: systemPrompt },
499
+ { role: "user", content: userInput }
500
+ ]
501
+ })
502
+ },
503
+ signal
504
+ );
505
+ if (!response.ok) {
506
+ throw new Error(
507
+ `${this.config.providerLabel} API error: ${response.status} ${await response.text()}`
508
+ );
509
+ }
510
+ const data = await response.json();
511
+ this.logTokens(data.usage);
512
+ return data.choices?.[0]?.message?.content ?? "";
513
+ }
514
+ async completeWithTools(systemPrompt, messages, tools, signal) {
515
+ const openaiTools = tools.map((tool) => ({
516
+ type: "function",
517
+ function: {
518
+ name: tool.name,
519
+ description: tool.description,
520
+ parameters: {
521
+ type: "object",
522
+ properties: Object.fromEntries(
523
+ tool.parameters.map((param) => [
524
+ param.name,
525
+ { type: "string", description: param.description }
526
+ ])
527
+ ),
528
+ required: tool.parameters.filter((param) => param.required).map((param) => param.name)
529
+ }
530
+ }
531
+ }));
532
+ const response = await fetchWithRetry(
533
+ `${this.config.baseUrl}/chat/completions`,
534
+ {
535
+ method: "POST",
536
+ headers: {
537
+ "Content-Type": "application/json",
538
+ Authorization: `Bearer ${this.config.apiKey}`
539
+ },
540
+ body: JSON.stringify({
541
+ model: this.config.model,
542
+ max_tokens: this.config.maxTokens,
543
+ messages: [{ role: "system", content: systemPrompt }, ...messages],
544
+ tools: openaiTools
545
+ })
546
+ },
547
+ signal
548
+ );
549
+ if (!response.ok) {
550
+ throw new Error(
551
+ `${this.config.providerLabel} API error: ${response.status} ${await response.text()}`
552
+ );
553
+ }
554
+ const data = await response.json();
555
+ this.logTokens(data.usage);
556
+ const message = data.choices?.[0]?.message;
557
+ const toolCalls = message?.tool_calls ?? [];
558
+ if (toolCalls.length > 0) {
559
+ const calls = toolCalls.map((call) => {
560
+ let args;
561
+ try {
562
+ args = JSON.parse(call.function?.arguments ?? "{}");
563
+ } catch {
564
+ args = {};
565
+ }
566
+ return { id: call.id ?? "", name: call.function?.name ?? "", arguments: args };
567
+ });
568
+ return { type: "tool_use", calls, assistantMessage: message };
569
+ }
570
+ return { type: "text", text: message?.content ?? "" };
571
+ }
572
+ formatToolResultMessages(results) {
573
+ return results.map((result) => ({
574
+ role: "tool",
575
+ tool_call_id: result.callId,
576
+ content: result.content
577
+ }));
578
+ }
579
+ };
580
+ }
581
+ });
582
+
583
+ // src/llm/providers/deepseek.ts
584
+ var DEFAULT_MODEL2, FALLBACK_MODELS2, DEEPSEEK_PROVIDER;
585
+ var init_deepseek = __esm({
586
+ "src/llm/providers/deepseek.ts"() {
587
+ init_openai_compatible();
588
+ DEFAULT_MODEL2 = "deepseek-chat";
589
+ FALLBACK_MODELS2 = ["deepseek-chat", "deepseek-reasoner"];
590
+ DEEPSEEK_PROVIDER = createOpenAICompatibleProvider({
591
+ id: "deepseek",
592
+ displayName: "DeepSeek",
593
+ envVar: "DEEPSEEK_API_KEY",
594
+ baseUrl: "https://api.deepseek.com/v1",
595
+ defaultModel: DEFAULT_MODEL2,
596
+ fallbackModels: FALLBACK_MODELS2
597
+ });
598
+ }
599
+ });
600
+
601
+ // src/llm/providers/google.ts
602
+ function mapGeminiModelId(id) {
603
+ const stripped = id.startsWith(MODELS_PREFIX) ? id.slice(MODELS_PREFIX.length) : id;
604
+ return stripped.startsWith("gemini") ? stripped : null;
605
+ }
606
+ var DEFAULT_MODEL3, FALLBACK_MODELS3, MODELS_PREFIX, GOOGLE_PROVIDER;
607
+ var init_google = __esm({
608
+ "src/llm/providers/google.ts"() {
609
+ init_openai_compatible();
610
+ DEFAULT_MODEL3 = "gemini-2.5-flash";
611
+ FALLBACK_MODELS3 = ["gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.0-flash"];
612
+ MODELS_PREFIX = "models/";
613
+ GOOGLE_PROVIDER = createOpenAICompatibleProvider({
614
+ id: "google",
615
+ displayName: "Google (Gemini)",
616
+ envVar: "GEMINI_API_KEY",
617
+ baseUrl: "https://generativelanguage.googleapis.com/v1beta/openai",
618
+ defaultModel: DEFAULT_MODEL3,
619
+ fallbackModels: FALLBACK_MODELS3,
620
+ mapModelId: mapGeminiModelId
621
+ });
622
+ }
623
+ });
624
+
341
625
  // src/llm/providers/openai.ts
342
626
  function isOpenAIReasoningModel(model) {
343
627
  return /^o\d/.test(model) || /^gpt-5(\b|[-.])/.test(model);
@@ -353,15 +637,15 @@ async function fetchModels2(apiKey, signal) {
353
637
  signal
354
638
  );
355
639
  if (!response.ok) {
356
- return FALLBACK_MODELS2;
640
+ return FALLBACK_MODELS4;
357
641
  }
358
642
  const data = await response.json();
359
643
  const models = (data.data ?? []).map((entry) => entry.id).filter(
360
644
  (id) => (id.startsWith("gpt-") || id.startsWith("o1") || id.startsWith("o3") || id.startsWith("o4") || id.startsWith("chatgpt-")) && !id.includes("instruct") && !id.includes("realtime") && !id.includes("audio") && !id.includes("tts") && !id.includes("whisper")
361
645
  ).sort();
362
- return models.length > 0 ? models : FALLBACK_MODELS2;
646
+ return models.length > 0 ? models : FALLBACK_MODELS4;
363
647
  } catch {
364
- return FALLBACK_MODELS2;
648
+ return FALLBACK_MODELS4;
365
649
  }
366
650
  }
367
651
  async function verifyKey2(apiKey, signal) {
@@ -442,13 +726,13 @@ async function verifyKeyDeep2(apiKey, model, signal) {
442
726
  return { ok: false, reason: "unavailable", error: message };
443
727
  }
444
728
  }
445
- var DEFAULT_MODEL2, DEFAULT_MAX_TOKENS2, FALLBACK_MODELS2, OpenAIClient, BILLING_BODY_MARKERS2, OPENAI_PROVIDER;
729
+ var DEFAULT_MODEL4, DEFAULT_MAX_TOKENS3, FALLBACK_MODELS4, OpenAIClient, BILLING_BODY_MARKERS2, OPENAI_PROVIDER;
446
730
  var init_openai = __esm({
447
731
  "src/llm/providers/openai.ts"() {
448
732
  init_http();
449
- DEFAULT_MODEL2 = "gpt-4o-mini";
450
- DEFAULT_MAX_TOKENS2 = 4096;
451
- FALLBACK_MODELS2 = ["gpt-4o", "gpt-4o-mini", "o3-mini"];
733
+ DEFAULT_MODEL4 = "gpt-4o-mini";
734
+ DEFAULT_MAX_TOKENS3 = 4096;
735
+ FALLBACK_MODELS4 = ["gpt-4o", "gpt-4o-mini", "o3-mini"];
452
736
  OpenAIClient = class {
453
737
  constructor(config) {
454
738
  this.config = config;
@@ -571,15 +855,15 @@ var init_openai = __esm({
571
855
  id: "openai",
572
856
  displayName: "OpenAI (GPT)",
573
857
  envVar: "OPENAI_API_KEY",
574
- defaultModel: DEFAULT_MODEL2,
575
- fallbackModels: FALLBACK_MODELS2,
858
+ defaultModel: DEFAULT_MODEL4,
859
+ fallbackModels: FALLBACK_MODELS4,
576
860
  fetchModels: fetchModels2,
577
861
  verifyKey: verifyKey2,
578
862
  verifyKeyDeep: verifyKeyDeep2,
579
863
  createClient: (config) => new OpenAIClient({
580
864
  apiKey: config.apiKey,
581
- model: config.model ?? DEFAULT_MODEL2,
582
- maxTokens: config.maxTokens ?? DEFAULT_MAX_TOKENS2,
865
+ model: config.model ?? DEFAULT_MODEL4,
866
+ maxTokens: config.maxTokens ?? DEFAULT_MAX_TOKENS3,
583
867
  logUsage: config.logUsage
584
868
  }),
585
869
  isReasoningModel: isOpenAIReasoningModel
@@ -587,6 +871,24 @@ var init_openai = __esm({
587
871
  }
588
872
  });
589
873
 
874
+ // src/llm/providers/xai.ts
875
+ var DEFAULT_MODEL5, FALLBACK_MODELS5, XAI_PROVIDER;
876
+ var init_xai = __esm({
877
+ "src/llm/providers/xai.ts"() {
878
+ init_openai_compatible();
879
+ DEFAULT_MODEL5 = "grok-3-mini";
880
+ FALLBACK_MODELS5 = ["grok-4", "grok-3", "grok-3-mini"];
881
+ XAI_PROVIDER = createOpenAICompatibleProvider({
882
+ id: "xai",
883
+ displayName: "xAI (Grok)",
884
+ envVar: "XAI_API_KEY",
885
+ baseUrl: "https://api.x.ai/v1",
886
+ defaultModel: DEFAULT_MODEL5,
887
+ fallbackModels: FALLBACK_MODELS5
888
+ });
889
+ }
890
+ });
891
+
590
892
  // src/llm/registry.ts
591
893
  function registerLlmProvider(descriptor) {
592
894
  REGISTRY.set(descriptor.id, descriptor);
@@ -604,10 +906,16 @@ var REGISTRY;
604
906
  var init_registry = __esm({
605
907
  "src/llm/registry.ts"() {
606
908
  init_anthropic();
909
+ init_deepseek();
910
+ init_google();
607
911
  init_openai();
912
+ init_xai();
608
913
  REGISTRY = /* @__PURE__ */ new Map();
609
914
  registerLlmProvider(ANTHROPIC_PROVIDER);
610
915
  registerLlmProvider(OPENAI_PROVIDER);
916
+ registerLlmProvider(XAI_PROVIDER);
917
+ registerLlmProvider(GOOGLE_PROVIDER);
918
+ registerLlmProvider(DEEPSEEK_PROVIDER);
611
919
  }
612
920
  });
613
921
 
@@ -1510,7 +1818,7 @@ var JobLedger = class {
1510
1818
  init_llm();
1511
1819
 
1512
1820
  // src/llm/resolve.ts
1513
- var DEFAULT_MAX_TOKENS3 = 4096;
1821
+ var DEFAULT_MAX_TOKENS4 = 4096;
1514
1822
  function resolveSkillLlm(input, agentDefault) {
1515
1823
  const override = input.llmOverride;
1516
1824
  const overridePairSet = override?.provider !== void 0 && override.model !== void 0;
@@ -1528,7 +1836,7 @@ function resolveSkillLlm(input, agentDefault) {
1528
1836
  error: `Skill "${input.skillName}" at ${input.skillMdPath}: LLM model is required - declare "provider" + "model" in the SKILL.md frontmatter or set agent-level llm via 'npx @elisym/cli profile <agent>'.`
1529
1837
  };
1530
1838
  }
1531
- const maxTokens = override?.maxTokens ?? agentDefault?.max_tokens ?? DEFAULT_MAX_TOKENS3;
1839
+ const maxTokens = override?.maxTokens ?? agentDefault?.max_tokens ?? DEFAULT_MAX_TOKENS4;
1532
1840
  return { provider, model, maxTokens };
1533
1841
  }
1534
1842
 
@@ -1554,7 +1862,7 @@ function resolveTripleForOverride(override, agentDefault) {
1554
1862
  if (!provider || !model) {
1555
1863
  return void 0;
1556
1864
  }
1557
- const maxTokens = override?.maxTokens ?? agentDefault?.max_tokens ?? DEFAULT_MAX_TOKENS3;
1865
+ const maxTokens = override?.maxTokens ?? agentDefault?.max_tokens ?? DEFAULT_MAX_TOKENS4;
1558
1866
  return { provider, model, maxTokens };
1559
1867
  }
1560
1868
 
@@ -2951,7 +3259,7 @@ async function cmdStart(nameArg, options = {}) {
2951
3259
  const hasPaid = allSkills.some((s) => s.priceSubunits > 0);
2952
3260
  if (hasPaid && !solanaAddress) {
2953
3261
  console.error(
2954
- " ! Paid skills require a Solana address. Run `npx @elisym/cli init` to configure.\n"
3262
+ " ! Paid skills require a Solana address. Run `npx @elisym/cli profile` to configure.\n"
2955
3263
  );
2956
3264
  process.exit(1);
2957
3265
  }
@@ -3163,7 +3471,7 @@ async function cmdStart(nameArg, options = {}) {
3163
3471
  try {
3164
3472
  await client.discovery.publishProfile(
3165
3473
  identity,
3166
- agentName,
3474
+ loaded.yaml.display_name ?? agentName,
3167
3475
  loaded.yaml.description ?? "",
3168
3476
  pictureUrl,
3169
3477
  bannerUrl