File size: 4,760 Bytes
34810d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Capability CLI command registration. Domain implementations live in ./capability-cli/.
import type { Command } from "commander";
import { formatDocsLink } from "../../packages/terminal-core/src/links.js";
import { theme } from "../../packages/terminal-core/src/theme.js";
import { FLAG_TERMINATOR, getCommandArgsWithRootOptions } from "../infra/cli-root-options.js";
import { defaultRuntime } from "../runtime.js";
import { getCommandPathWithRootOptions, normalizeRootLogLevelArgv } from "./argv.js";
import { CAPABILITY_METADATA, findCapabilityMetadata } from "./capability-cli/metadata.js";
import { emitJsonOrText, providerSummaryText } from "./capability-cli/output.js";
import { runCommandWithRuntime } from "./cli-utils.js";
import { removeCommandByName } from "./program/command-tree.js";

const capabilityCommandGroups = [
  [
    "model",
    async () => (await import("./capability-cli/model.js")).registerModelCapabilityCommands,
  ],
  [
    "image",
    async () => (await import("./capability-cli/image.js")).registerImageCapabilityCommands,
  ],
  [
    "audio",
    async () => (await import("./capability-cli/audio.js")).registerAudioCapabilityCommands,
  ],
  ["tts", async () => (await import("./capability-cli/tts.js")).registerTtsCapabilityCommands],
  [
    "video",
    async () => (await import("./capability-cli/video.js")).registerVideoCapabilityCommands,
  ],
  ["web", async () => (await import("./capability-cli/web.js")).registerWebCapabilityCommands],
  [
    "embedding",
    async () => (await import("./capability-cli/embedding.js")).registerEmbeddingCapabilityCommands,
  ],
] as const;

function registerCapabilityListAndInspect(capability: Command): void {
  capability
    .command("list")
    .description("List canonical capability ids and supported transports")
    .option("--json", "Output JSON", false)
    .action(async (opts) => {
      await runCommandWithRuntime(defaultRuntime, async () => {
        const result = CAPABILITY_METADATA.map((entry) => ({
          id: entry.id,
          transports: entry.transports,
          description: entry.description,
        }));
        emitJsonOrText(defaultRuntime, Boolean(opts.json), result, providerSummaryText);
      });
    });

  capability
    .command("inspect")
    .description("Inspect one canonical capability id")
    .requiredOption("--name <capability>", "Capability id")
    .option("--json", "Output JSON", false)
    .action(async (opts) => {
      await runCommandWithRuntime(defaultRuntime, async () => {
        const entry = findCapabilityMetadata(String(opts.name));
        if (!entry) {
          throw new Error(`Unknown capability: ${String(opts.name)}`);
        }
        emitJsonOrText(defaultRuntime, Boolean(opts.json), entry, (value) =>
          JSON.stringify(value, null, 2),
        );
      });
    });
}

async function registerCapabilityDomainCommands(
  capability: Command,
  argv: string[],
): Promise<void> {
  // Root log levels are normalized after registration. Reuse that view for selection,
  // leaving help and unknown options ahead of a domain on the complete-tree path.
  const selectionArgv = normalizeRootLogLevelArgv(argv);
  const commandPath = getCommandPathWithRootOptions(selectionArgv, 2);
  const primary = commandPath[0];
  const commandArgs =
    primary === "infer" || primary === "capability"
      ? getCommandArgsWithRootOptions(selectionArgv, {
          commandPath: [primary],
          mode: "command-path",
        })
      : undefined;
  // The raw tail marks both leading and post-parent `--`; only the former retains a domain.
  const selectedName = commandArgs?.[0] === FLAG_TERMINATOR ? commandPath[1] : commandArgs?.[0];
  if (selectedName === "list" || selectedName === "inspect") {
    return;
  }
  const selected = capabilityCommandGroups.find(([name]) => name === selectedName);
  if (selected) {
    const register = await selected[1]();
    register(capability);
    return;
  }

  const registrars = await Promise.all(capabilityCommandGroups.map(([, load]) => load()));
  for (const register of registrars) {
    register(capability);
  }
}

export async function registerCapabilityCli(
  program: Command,
  argv: string[] = process.argv,
): Promise<void> {
  removeCommandByName(program, "infer");
  removeCommandByName(program, "capability");

  const capability = program
    .command("infer")
    .alias("capability")
    .description("Run provider-backed inference commands through a stable CLI surface")
    .addHelpText(
      "after",
      () =>
        `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/infer", "docs.openclaw.ai/cli/infer")}\n`,
    );

  registerCapabilityListAndInspect(capability);
  await registerCapabilityDomainCommands(capability, argv);
}