@cleocode/cleo 2026.6.1 → 2026.6.3

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/cli/index.js CHANGED
@@ -55892,7 +55892,7 @@ import {
55892
55892
  refreshPkceToken
55893
55893
  } from "@cleocode/core/llm/oauth/pkce.js";
55894
55894
  import { getKimiCodeMshHeaders } from "@cleocode/core/llm/provider-registry/builtin/kimi-code.js";
55895
- import { getProviderProfile } from "@cleocode/core/llm/provider-registry/index.js";
55895
+ import { getProviderProfile, listProviders } from "@cleocode/core/llm/provider-registry/index.js";
55896
55896
  async function runLlmLogin(provider, opts) {
55897
55897
  const meta = { operation: "llm.login", timestamp: (/* @__PURE__ */ new Date()).toISOString() };
55898
55898
  const profile = await getProviderProfile(provider);
@@ -55901,31 +55901,43 @@ async function runLlmLogin(provider, opts) {
55901
55901
  return _runKimiCodeLogin(opts, meta);
55902
55902
  }
55903
55903
  if (oauthMode === "pkce") {
55904
- return _runPkceLogin(provider, profile.oauth, opts, meta);
55904
+ return _runPkceLogin(profile.name, profile.oauth, opts, meta);
55905
55905
  }
55906
55906
  return {
55907
55907
  success: false,
55908
55908
  error: {
55909
55909
  code: "E_NOT_IMPLEMENTED",
55910
55910
  codeName: "E_NOT_IMPLEMENTED",
55911
- message: `OAuth login for '${provider}' is not yet wired. Supported providers: 'anthropic' (PKCE), 'kimi-code' (device-code). To add credentials for other providers use 'cleo llm add <provider> --api-key-stdin'.`
55911
+ message: `OAuth login for '${provider}' is not yet wired. ${await _supportedOauthProvidersHint()} For any other provider, add an API key with 'cleo llm add <provider> --api-key-stdin'.`
55912
55912
  },
55913
55913
  meta
55914
55914
  };
55915
55915
  }
55916
+ async function _supportedOauthProvidersHint() {
55917
+ try {
55918
+ const profiles = await listProviders();
55919
+ const oauthable = profiles.flatMap((p) => p.oauth ? [`'${p.name}' (${p.oauth.mode})`] : []);
55920
+ return oauthable.length > 0 ? `Providers with OAuth login: ${oauthable.join(", ")}.` : "No providers currently expose OAuth login.";
55921
+ } catch {
55922
+ return "Providers with OAuth login: 'anthropic' (pkce), 'openai' (pkce), 'kimi-code' (device-code).";
55923
+ }
55924
+ }
55916
55925
  async function _runPkceLogin(provider, oauthCfg, opts, meta) {
55917
55926
  const { codeVerifier, codeChallenge } = await generatePkcePair();
55918
55927
  const state = _generateState();
55919
55928
  const isHeadless = opts.headless || process.env["CLEO_HEADLESS"] === "1";
55920
- const port = isHeadless ? 0 : await _findFreePort();
55921
- const redirectUri = isHeadless ? oauthCfg.redirectUri ?? "http://localhost" : `http://localhost:${port}/callback`;
55929
+ const fixedPort = isHeadless ? null : _parseFixedLoopbackPort(oauthCfg.redirectUri);
55930
+ const port = isHeadless ? 0 : fixedPort ?? await _findFreePort();
55931
+ const randomRedirect = `http://localhost:${port}/callback`;
55932
+ const redirectUri = isHeadless ? oauthCfg.redirectUri ?? "http://localhost" : fixedPort != null ? oauthCfg.redirectUri ?? randomRedirect : randomRedirect;
55922
55933
  const authUrl = buildAuthorizationUrl({
55923
55934
  authorizationEndpoint: oauthCfg.authorizationEndpoint ?? "",
55924
55935
  clientId: oauthCfg.clientId,
55925
55936
  redirectUri,
55926
55937
  scope: oauthCfg.scope ?? "",
55927
55938
  codeChallenge,
55928
- state
55939
+ state,
55940
+ extraParams: oauthCfg.extraAuthParams
55929
55941
  });
55930
55942
  let code;
55931
55943
  if (isHeadless) {
@@ -55962,6 +55974,7 @@ async function _runPkceLogin(provider, oauthCfg, opts, meta) {
55962
55974
  process.stderr.write("\r Authorization approved. \n\n");
55963
55975
  const label = opts.label ?? "oauth-login";
55964
55976
  const expiresAt = typeof tokens.expiresIn === "number" ? Date.now() + tokens.expiresIn * 1e3 : void 0;
55977
+ const oauthExtraHeaders = provider === "anthropic" ? { "anthropic-beta": "oauth-2025-04-20" } : void 0;
55965
55978
  try {
55966
55979
  await addCredential({
55967
55980
  provider,
@@ -55972,7 +55985,7 @@ async function _runPkceLogin(provider, oauthCfg, opts, meta) {
55972
55985
  expiresAt,
55973
55986
  priority: 10,
55974
55987
  source: "oauth-pkce",
55975
- extraHeaders: { "anthropic-beta": "oauth-2025-04-20" }
55988
+ ...oauthExtraHeaders ? { extraHeaders: oauthExtraHeaders } : {}
55976
55989
  });
55977
55990
  } catch (err) {
55978
55991
  const msg = err instanceof Error ? err.message : String(err);
@@ -55992,6 +56005,18 @@ async function _runPkceLogin(provider, oauthCfg, opts, meta) {
55992
56005
  meta
55993
56006
  };
55994
56007
  }
56008
+ function _parseFixedLoopbackPort(redirectUri) {
56009
+ if (!redirectUri) return null;
56010
+ try {
56011
+ const u = new URL(redirectUri);
56012
+ const isLoopback = u.hostname === "localhost" || u.hostname === "127.0.0.1";
56013
+ if (!isLoopback || !u.port) return null;
56014
+ const port = Number(u.port);
56015
+ return Number.isInteger(port) && port > 0 ? port : null;
56016
+ } catch {
56017
+ return null;
56018
+ }
56019
+ }
55995
56020
  async function _headlessPkceFlow(provider, authUrl) {
55996
56021
  process.stderr.write("\n");
55997
56022
  process.stderr.write(` Provider: ${provider}
@@ -56278,11 +56303,11 @@ __export(llm_exports, {
56278
56303
  });
56279
56304
  import { pushWarning as pushWarning7 } from "@cleocode/core";
56280
56305
  async function getListProviders() {
56281
- const { listProviders } = await import(
56306
+ const { listProviders: listProviders2 } = await import(
56282
56307
  /* webpackIgnore: true */
56283
56308
  "@cleocode/core/llm/provider-registry"
56284
56309
  );
56285
- return listProviders;
56310
+ return listProviders2;
56286
56311
  }
56287
56312
  async function readApiKeyFromStdin() {
56288
56313
  if (process.stdin.isTTY) return "";
@@ -56593,8 +56618,8 @@ var init_llm3 = __esm({
56593
56618
  }
56594
56619
  },
56595
56620
  async run() {
56596
- const listProviders = await getListProviders();
56597
- const profiles = await listProviders();
56621
+ const listProviders2 = await getListProviders();
56622
+ const profiles = await listProviders2();
56598
56623
  const providers = profiles.map((p) => ({
56599
56624
  name: p.name,
56600
56625
  displayName: p.displayName,
@@ -56654,12 +56679,12 @@ var init_llm3 = __esm({
56654
56679
  loginCommand = defineCommand({
56655
56680
  meta: {
56656
56681
  name: "login",
56657
- description: "Authenticate with a provider via OAuth device-code flow. Supported providers: anthropic (MVP). Prints the verification URL and user code, then polls until the user approves."
56682
+ description: "Authenticate an LLM provider via OAuth. PKCE (browser): anthropic, openai/codex. Device-code: kimi-code. Example: `cleo llm login openai`. For any other provider use `cleo llm add <provider> --api-key-stdin`. Prompts/URLs go to stderr; the result is a human line on a terminal or a JSON envelope when piped/--json."
56658
56683
  },
56659
56684
  args: {
56660
56685
  provider: {
56661
56686
  type: "positional",
56662
- description: "Provider to authenticate with (e.g. anthropic)",
56687
+ description: "Provider to authenticate: anthropic | openai | codex | kimi-code",
56663
56688
  required: true
56664
56689
  },
56665
56690
  label: {
@@ -56675,16 +56700,18 @@ var init_llm3 = __esm({
56675
56700
  const a = args;
56676
56701
  const provider = String(a["provider"] ?? "");
56677
56702
  const label = typeof a["label"] === "string" && a["label"] ? a["label"] : void 0;
56678
- const jsonOutput = a["json"] === true;
56679
56703
  const result = await runLlmLogin(provider, { label });
56680
- if (jsonOutput) {
56681
- process.stdout.write(`${JSON.stringify(result, null, 2)}
56682
- `);
56683
- } else if (result.success && result.data) {
56684
- process.stdout.write(
56685
- `Logged in to ${result.data.provider} as '${result.data.label}'` + (result.data.expiresIn != null ? ` (expires in ${Math.round(result.data.expiresIn / 60)} min)` : "") + "\n"
56686
- );
56687
- } else if (result.error) {
56704
+ if (result.success && result.data) {
56705
+ if (isHumanOutput()) {
56706
+ humanLine(
56707
+ `Logged in to ${result.data.provider} as '${result.data.label}'` + (result.data.expiresIn != null ? ` (expires in ${Math.round(result.data.expiresIn / 60)} min)` : "")
56708
+ );
56709
+ } else {
56710
+ cliOutput(result.data, { command: "llm-login", operation: "llm.login" });
56711
+ }
56712
+ return;
56713
+ }
56714
+ if (result.error) {
56688
56715
  cliError(
56689
56716
  result.error.message,
56690
56717
  result.error.code || 1,
@@ -56706,19 +56733,20 @@ var init_llm3 = __esm({
56706
56733
  description: "Output result as JSON"
56707
56734
  }
56708
56735
  },
56709
- async run({ args }) {
56710
- const jsonOutput = args["json"] === true;
56736
+ async run() {
56711
56737
  const result = await runLlmRefreshCatalog();
56712
- if (jsonOutput) {
56713
- process.stdout.write(`${JSON.stringify(result, null, 2)}
56714
- `);
56715
- } else if (result.success && result.data) {
56738
+ if (result.success && result.data) {
56716
56739
  const d = result.data;
56717
- process.stdout.write(
56718
- `Catalog refreshed: ${d.providers} providers, ${d.models} models written to ${d.filePath}
56719
- `
56720
- );
56721
- } else if (!result.success) {
56740
+ if (isHumanOutput()) {
56741
+ humanLine(
56742
+ `Catalog refreshed: ${d.providers} providers, ${d.models} models written to ${d.filePath}`
56743
+ );
56744
+ } else {
56745
+ cliOutput(result.data, { command: "llm-refresh-catalog", operation: "llm.refreshCatalog" });
56746
+ }
56747
+ return;
56748
+ }
56749
+ if (!result.success) {
56722
56750
  cliError(
56723
56751
  result.error?.message ?? "refresh failed",
56724
56752
  result.error?.code ?? 1,
@@ -75791,15 +75819,34 @@ async function maybePromptFirstRun() {
75791
75819
  }
75792
75820
  }
75793
75821
 
75822
+ // packages/cleo/src/cli/lib/interactive-commands.ts
75823
+ var INTERACTIVE_COMMAND_PATHS = [
75824
+ ["llm", "login"],
75825
+ ["llm", "add"],
75826
+ ["llm", "refresh-catalog"],
75827
+ ["login"],
75828
+ ["auth", "login"],
75829
+ ["setup"],
75830
+ ["init"]
75831
+ ];
75832
+ function isInteractiveInvocation(argv) {
75833
+ const positionals = argv.filter((token) => !token.startsWith("-"));
75834
+ if (positionals.length === 0) return false;
75835
+ return INTERACTIVE_COMMAND_PATHS.some(
75836
+ (path6) => path6.length <= positionals.length && path6.every((seg, i) => seg === positionals[i])
75837
+ );
75838
+ }
75839
+
75794
75840
  // packages/cleo/src/cli/middleware/output-format.ts
75795
75841
  import { resolveOutputFormat } from "@cleocode/lafs";
75796
- function resolveFormat(opts, defaults) {
75842
+ function resolveFormat(opts, defaults, tty) {
75797
75843
  const input2 = {
75798
75844
  jsonFlag: opts["json"] === true,
75799
75845
  humanFlag: opts["human"] === true,
75800
75846
  quiet: opts["quiet"] === true,
75801
75847
  projectDefault: defaults?.projectDefault,
75802
- userDefault: defaults?.userDefault
75848
+ userDefault: defaults?.userDefault,
75849
+ tty
75803
75850
  };
75804
75851
  return resolveOutputFormat(input2);
75805
75852
  }
@@ -75890,7 +75937,8 @@ async function startCli() {
75890
75937
  else if (arg === "--output" && i + 1 < argv.length) outputModeRaw = argv[++i];
75891
75938
  else if (arg === "--summary") summaryFlag = true;
75892
75939
  }
75893
- const formatResolution = resolveFormat(rawOpts);
75940
+ const interactiveTty = isInteractiveInvocation(argv) && process.stdout.isTTY === true;
75941
+ const formatResolution = resolveFormat(rawOpts, void 0, interactiveTty);
75894
75942
  setFormatContext(formatResolution);
75895
75943
  const fieldResolution = resolveFieldContext(rawOpts);
75896
75944
  if (fieldResolution.mviSource === "default") {