929 lines · 28.8 KB · TypeScript
RawDownload
import type {  ClaudeSettings,  ClaudeModelSelection,  ModelCapabilities,  ServerProvider,  ServerProviderModel,  ServerProviderAuth,  ServerProviderSlashCommand,  ServerProviderState,} from "@t3tools/contracts";import { Cache, Duration, Effect, Equal, Layer, Option, Result, Schema, Stream } from "effect";import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";import { decodeJsonResult } from "@t3tools/shared/schemaJson";import {  createModelCapabilities,  getModelSelectionStringOptionValue,  getProviderOptionCurrentValue,  getProviderOptionDescriptors,} from "@t3tools/shared/model";import {  query as claudeQuery,  type SlashCommand as ClaudeSlashCommand,  type SDKUserMessage,} from "@anthropic-ai/claude-agent-sdk"; import {  buildBooleanOptionDescriptor,  buildSelectOptionDescriptor,  buildServerProvider,  AUTH_PROBE_TIMEOUT_MS,  DEFAULT_TIMEOUT_MS,  detailFromResult,  extractAuthBoolean,  isCommandMissingCause,  parseGenericCliVersion,  providerModelsFromSettings,  spawnAndCollect,  type CommandResult,} from "../providerSnapshot.ts";import { compareCliVersions } from "../cliVersion.ts";import { makeManagedServerProvider } from "../makeManagedServerProvider.ts";import { ClaudeProvider } from "../Services/ClaudeProvider.ts";import { ServerSettingsService } from "../../serverSettings.ts";import { ServerSettingsError } from "@t3tools/contracts"; const DEFAULT_CLAUDE_MODEL_CAPABILITIES: ModelCapabilities = createModelCapabilities({  optionDescriptors: [],}); const PROVIDER = "claudeAgent" as const;const CLAUDE_PRESENTATION = {  displayName: "Claude",  showInteractionModeToggle: true,} as const;const MINIMUM_CLAUDE_OPUS_4_7_VERSION = "2.1.111";const BUILT_IN_MODELS: ReadonlyArray<ServerProviderModel> = [  {    slug: "claude-opus-4-7",    name: "Claude Opus 4.7",    isCustom: false,    capabilities: createModelCapabilities({      optionDescriptors: [        buildSelectOptionDescriptor({          id: "effort",          label: "Reasoning",          options: [            { value: "low", label: "Low" },            { value: "medium", label: "Medium" },            { value: "high", label: "High" },            { value: "xhigh", label: "Extra High", isDefault: true },            { value: "max", label: "Max" },            { value: "ultrathink", label: "Ultrathink" },          ],          promptInjectedValues: ["ultrathink"],        }),        buildSelectOptionDescriptor({          id: "contextWindow",          label: "Context Window",          options: [            { value: "200k", label: "200k", isDefault: true },            { value: "1m", label: "1M" },          ],        }),      ],    }),  },  {    slug: "claude-opus-4-6",    name: "Claude Opus 4.6",    isCustom: false,    capabilities: createModelCapabilities({      optionDescriptors: [        buildSelectOptionDescriptor({          id: "effort",          label: "Reasoning",          options: [            { value: "low", label: "Low" },            { value: "medium", label: "Medium" },            { value: "high", label: "High", isDefault: true },            { value: "max", label: "Max" },            { value: "ultrathink", label: "Ultrathink" },          ],          promptInjectedValues: ["ultrathink"],        }),        buildBooleanOptionDescriptor({          id: "fastMode",          label: "Fast Mode",        }),        buildSelectOptionDescriptor({          id: "contextWindow",          label: "Context Window",          options: [            { value: "200k", label: "200k", isDefault: true },            { value: "1m", label: "1M" },          ],        }),      ],    }),  },  {    slug: "claude-opus-4-5",    name: "Claude Opus 4.5",    isCustom: false,    capabilities: createModelCapabilities({      optionDescriptors: [        buildSelectOptionDescriptor({          id: "effort",          label: "Reasoning",          options: [            { value: "low", label: "Low" },            { value: "medium", label: "Medium" },            { value: "high", label: "High", isDefault: true },            { value: "max", label: "Max" },          ],        }),        buildBooleanOptionDescriptor({          id: "fastMode",          label: "Fast Mode",        }),      ],    }),  },  {    slug: "claude-sonnet-4-6",    name: "Claude Sonnet 4.6",    isCustom: false,    capabilities: createModelCapabilities({      optionDescriptors: [        buildSelectOptionDescriptor({          id: "effort",          label: "Reasoning",          options: [            { value: "low", label: "Low" },            { value: "medium", label: "Medium" },            { value: "high", label: "High", isDefault: true },            { value: "ultrathink", label: "Ultrathink" },          ],          promptInjectedValues: ["ultrathink"],        }),        buildSelectOptionDescriptor({          id: "contextWindow",          label: "Context Window",          options: [            { value: "200k", label: "200k", isDefault: true },            { value: "1m", label: "1M" },          ],        }),      ],    }),  },  {    slug: "claude-haiku-4-5",    name: "Claude Haiku 4.5",    isCustom: false,    capabilities: createModelCapabilities({      optionDescriptors: [        buildBooleanOptionDescriptor({          id: "thinking",          label: "Thinking",        }),      ],    }),  },]; function supportsClaudeOpus47(version: string | null | undefined): boolean {  return version ? compareCliVersions(version, MINIMUM_CLAUDE_OPUS_4_7_VERSION) >= 0 : false;} function getBuiltInClaudeModelsForVersion(  version: string | null | undefined,): ReadonlyArray<ServerProviderModel> {  if (supportsClaudeOpus47(version)) {    return BUILT_IN_MODELS;  }  return BUILT_IN_MODELS.filter((model) => model.slug !== "claude-opus-4-7");} function formatClaudeOpus47UpgradeMessage(version: string | null): string {  const versionLabel = version ? `v${version}` : "the installed version";  return `Claude Code ${versionLabel} is too old for Claude Opus 4.7. Upgrade to v${MINIMUM_CLAUDE_OPUS_4_7_VERSION} or newer to access it.`;} export function getClaudeModelCapabilities(model: string | null | undefined): ModelCapabilities {  const slug = model?.trim();  return (    BUILT_IN_MODELS.find((candidate) => candidate.slug === slug)?.capabilities ??    DEFAULT_CLAUDE_MODEL_CAPABILITIES  );} export function resolveClaudeEffort(  caps: ModelCapabilities,  raw: string | null | undefined,): string | undefined {  const descriptors = getProviderOptionDescriptors({    caps,    ...(raw ? { selections: [{ id: "effort", value: raw }] } : {}),  });  const effortDescriptor = descriptors.find((descriptor) => descriptor.id === "effort");  const value = getProviderOptionCurrentValue(effortDescriptor);  return typeof value === "string" ? value : undefined;} /** * Normalize a resolved Claude effort value into one suitable for the Claude * CLI's `--effort` flag. * * Mirrors the mapping used when invoking the Claude Agent SDK * ({@link getEffectiveClaudeAgentEffort} in ClaudeAdapter): the Opus 4.7 * capability `"xhigh"` is rewritten to the accepted CLI value `"max"`, and * `"ultrathink"` is filtered out because it is a prompt-prefix mode rather * than a CLI-effort value. Returns `undefined` when no flag should be passed. */export function normalizeClaudeCliEffort(effort: string | null | undefined): string | undefined {  if (!effort || effort === "ultrathink") {    return undefined;  }  if (effort === "xhigh") {    return "max";  }  return effort;} export function resolveClaudeApiModelId(modelSelection: ClaudeModelSelection): string {  switch (getModelSelectionStringOptionValue(modelSelection, "contextWindow")) {    case "1m":      return `${modelSelection.model}[1m]`;    default:      return modelSelection.model;  }}export function parseClaudeAuthStatusFromOutput(result: CommandResult): {  readonly status: Exclude<ServerProviderState, "disabled">;  readonly auth: Pick<ServerProviderAuth, "status">;  readonly message?: string;} {  const lowerOutput = `${result.stdout}\n${result.stderr}`.toLowerCase();   if (    lowerOutput.includes("unknown command") ||    lowerOutput.includes("unrecognized command") ||    lowerOutput.includes("unexpected argument")  ) {    return {      status: "warning",      auth: { status: "unknown" },      message:        "Claude Agent authentication status command is unavailable in this version of Claude.",    };  }   if (    lowerOutput.includes("not logged in") ||    lowerOutput.includes("login required") ||    lowerOutput.includes("authentication required") ||    lowerOutput.includes("run `claude login`") ||    lowerOutput.includes("run claude login")  ) {    return {      status: "error",      auth: { status: "unauthenticated" },      message: "Claude is not authenticated. Run `claude auth login` and try again.",    };  }   const parsedAuth = (() => {    const trimmed = result.stdout.trim();    if (!trimmed || (!trimmed.startsWith("{") && !trimmed.startsWith("["))) {      return { attemptedJsonParse: false as const, auth: undefined as boolean | undefined };    }    try {      return {        attemptedJsonParse: true as const,        auth: extractAuthBoolean(JSON.parse(trimmed)),      };    } catch {      return { attemptedJsonParse: false as const, auth: undefined as boolean | undefined };    }  })();   if (parsedAuth.auth === true) {    return { status: "ready", auth: { status: "authenticated" } };  }  if (parsedAuth.auth === false) {    return {      status: "error",      auth: { status: "unauthenticated" },      message: "Claude is not authenticated. Run `claude auth login` and try again.",    };  }  if (parsedAuth.attemptedJsonParse) {    return {      status: "warning",      auth: { status: "unknown" },      message:        "Could not verify Claude authentication status from JSON output (missing auth marker).",    };  }  if (result.code === 0) {    return { status: "ready", auth: { status: "authenticated" } };  }   const detail = detailFromResult(result);  return {    status: "warning",    auth: { status: "unknown" },    message: detail      ? `Could not verify Claude authentication status. ${detail}`      : "Could not verify Claude authentication status.",  };} // ── Subscription type detection ─────────────────────────────────────//// The SDK probe returns typed `AccountInfo.subscriptionType` directly.// This walker is a best-effort fallback for the `claude auth status`// JSON output whose shape is not guaranteed. /** Keys that directly hold a subscription/plan identifier. */const SUBSCRIPTION_TYPE_KEYS = [  "subscriptionType",  "subscription_type",  "plan",  "tier",  "planType",  "plan_type",] as const; /** Keys whose value may be a nested object containing subscription info. */const SUBSCRIPTION_CONTAINER_KEYS = ["account", "subscription", "user", "billing"] as const;const AUTH_METHOD_KEYS = ["authMethod", "auth_method"] as const;const AUTH_METHOD_CONTAINER_KEYS = ["auth", "account", "session"] as const; /** Lift an unknown value into `Option<string>` if it is a non-empty string. */const asNonEmptyString = (v: unknown): Option.Option<string> =>  typeof v === "string" && v.length > 0 ? Option.some(v) : Option.none(); /** Lift an unknown value into `Option<Record>` if it is a plain object. */const asRecord = (v: unknown): Option.Option<Record<string, unknown>> =>  typeof v === "object" && v !== null && !globalThis.Array.isArray(v)    ? Option.some(v as Record<string, unknown>)    : Option.none(); /** * Walk an unknown parsed JSON value looking for a subscription/plan * identifier, returning the first match as an `Option`. */function findSubscriptionType(value: unknown): Option.Option<string> {  if (globalThis.Array.isArray(value)) {    return Option.firstSomeOf(value.map(findSubscriptionType));  }   return asRecord(value).pipe(    Option.flatMap((record) => {      const direct = Option.firstSomeOf(        SUBSCRIPTION_TYPE_KEYS.map((key) => asNonEmptyString(record[key])),      );      if (Option.isSome(direct)) return direct;       return Option.firstSomeOf(        SUBSCRIPTION_CONTAINER_KEYS.map((key) =>          asRecord(record[key]).pipe(Option.flatMap(findSubscriptionType)),        ),      );    }),  );} function findAuthMethod(value: unknown): Option.Option<string> {  if (globalThis.Array.isArray(value)) {    return Option.firstSomeOf(value.map(findAuthMethod));  }   return asRecord(value).pipe(    Option.flatMap((record) => {      const direct = Option.firstSomeOf(        AUTH_METHOD_KEYS.map((key) => asNonEmptyString(record[key])),      );      if (Option.isSome(direct)) return direct;       return Option.firstSomeOf(        AUTH_METHOD_CONTAINER_KEYS.map((key) =>          asRecord(record[key]).pipe(Option.flatMap(findAuthMethod)),        ),      );    }),  );} /** * Try to extract a subscription type from the `claude auth status` JSON * output. This is a zero-cost operation on data we already have. */const decodeUnknownJson = decodeJsonResult(Schema.Unknown); function extractSubscriptionTypeFromOutput(result: CommandResult): string | undefined {  const parsed = decodeUnknownJson(result.stdout.trim());  if (Result.isFailure(parsed)) return undefined;  return Option.getOrUndefined(findSubscriptionType(parsed.success));} function extractClaudeAuthMethodFromOutput(result: CommandResult): string | undefined {  const parsed = decodeUnknownJson(result.stdout.trim());  if (Result.isFailure(parsed)) return undefined;  return Option.getOrUndefined(findAuthMethod(parsed.success));} function toTitleCaseWords(value: string): string {  return value    .split(/[\s_-]+/g)    .filter(Boolean)    .map((part) => part[0]!.toUpperCase() + part.slice(1).toLowerCase())    .join(" ");} function claudeSubscriptionLabel(subscriptionType: string | undefined): string | undefined {  const normalized = subscriptionType?.toLowerCase().replace(/[\s_-]+/g, "");  if (!normalized) return undefined;   switch (normalized) {    case "max":    case "maxplan":    case "max5":    case "max20":      return "Max";    case "enterprise":      return "Enterprise";    case "team":      return "Team";    case "pro":      return "Pro";    case "free":      return "Free";    default:      return toTitleCaseWords(subscriptionType!);  }} function normalizeClaudeAuthMethod(authMethod: string | undefined): string | undefined {  const normalized = authMethod?.toLowerCase().replace(/[\s_-]+/g, "");  if (!normalized) return undefined;  if (normalized === "apikey") return "apiKey";  return undefined;} function claudeAuthMetadata(input: {  readonly subscriptionType: string | undefined;  readonly authMethod: string | undefined;}): { readonly type: string; readonly label: string } | undefined {  if (normalizeClaudeAuthMethod(input.authMethod) === "apiKey") {    return {      type: "apiKey",      label: "Claude API Key",    };  }   if (input.subscriptionType) {    const subscriptionLabel = claudeSubscriptionLabel(input.subscriptionType);    return {      type: input.subscriptionType,      label: `Claude ${subscriptionLabel ?? toTitleCaseWords(input.subscriptionType)} Subscription`,    };  }   return undefined;} // ── SDK capability probe ──────────────────────────────────────────── const CAPABILITIES_PROBE_TIMEOUT_MS = 8_000; function nonEmptyProbeString(value: string): string | undefined {  const candidate = value.trim();  return candidate ? candidate : undefined;} function parseClaudeInitializationCommands(  commands: ReadonlyArray<ClaudeSlashCommand> | undefined,): ReadonlyArray<ServerProviderSlashCommand> {

Lines 1–500 of 929.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t