@cleocode/cleo 2026.5.68 → 2026.5.70

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
@@ -1565,6 +1565,13 @@ var init_branch_lock = __esm({
1565
1565
  }
1566
1566
  });
1567
1567
 
1568
+ // packages/contracts/src/credentials.ts
1569
+ var init_credentials = __esm({
1570
+ "packages/contracts/src/credentials.ts"() {
1571
+ "use strict";
1572
+ }
1573
+ });
1574
+
1568
1575
  // packages/contracts/src/engine-result.ts
1569
1576
  var init_engine_result = __esm({
1570
1577
  "packages/contracts/src/engine-result.ts"() {
@@ -1781,6 +1788,13 @@ var init_lafs = __esm({
1781
1788
  }
1782
1789
  });
1783
1790
 
1791
+ // packages/contracts/src/llm/plugin-llm.ts
1792
+ var init_plugin_llm = __esm({
1793
+ "packages/contracts/src/llm/plugin-llm.ts"() {
1794
+ "use strict";
1795
+ }
1796
+ });
1797
+
1784
1798
  // packages/contracts/src/operations/admin.ts
1785
1799
  var init_admin = __esm({
1786
1800
  "packages/contracts/src/operations/admin.ts"() {
@@ -2743,6 +2757,7 @@ var init_src2 = __esm({
2743
2757
  init_acceptance_gate_schema();
2744
2758
  init_attachment_schema();
2745
2759
  init_branch_lock();
2760
+ init_credentials();
2746
2761
  init_engine_result();
2747
2762
  init_errors();
2748
2763
  init_evidence_record_schema();
@@ -2750,6 +2765,7 @@ var init_src2 = __esm({
2750
2765
  init_facade();
2751
2766
  init_graph();
2752
2767
  init_lafs();
2768
+ init_plugin_llm();
2753
2769
  init_operations();
2754
2770
  init_nexus_scope_map();
2755
2771
  init_params();
@@ -16779,8 +16795,9 @@ import {
16779
16795
  generateMemoryBridgeContent,
16780
16796
  getBrainDb,
16781
16797
  getBrainNativeDb,
16782
- resolveAnthropicApiKey,
16783
- resolveAnthropicApiKeySource,
16798
+ OAUTH_STATUS_PROVIDERS,
16799
+ resolveCredentials,
16800
+ resolveProviderStatus,
16784
16801
  typedAll
16785
16802
  } from "@cleocode/core/internal";
16786
16803
  import {
@@ -17271,8 +17288,22 @@ var init_memory2 = __esm({
17271
17288
  }
17272
17289
  // T791 — LLM extraction backend status
17273
17290
  case "llm-status": {
17274
- const resolvedSource = resolveAnthropicApiKeySource();
17275
- const extractionEnabled = resolveAnthropicApiKey() !== null;
17291
+ const _anthropicCred = resolveCredentials("anthropic");
17292
+ const extractionEnabled = _anthropicCred.apiKey !== null;
17293
+ const resolvedSource = (() => {
17294
+ switch (_anthropicCred.source) {
17295
+ case "env":
17296
+ return "env";
17297
+ case "claude-creds":
17298
+ return "oauth";
17299
+ case "global-config":
17300
+ case "project-config":
17301
+ return "config";
17302
+ default:
17303
+ return "none";
17304
+ }
17305
+ })();
17306
+ const providers = OAUTH_STATUS_PROVIDERS.map(resolveProviderStatus);
17276
17307
  let lastExtractionRun = null;
17277
17308
  try {
17278
17309
  await getBrainDb(projectRoot);
@@ -17297,7 +17328,8 @@ var init_memory2 = __esm({
17297
17328
  resolvedSource,
17298
17329
  extractionEnabled,
17299
17330
  lastExtractionRun,
17300
- testCommand: "cleo memory reflect --json"
17331
+ testCommand: "cleo memory reflect --json",
17332
+ providers
17301
17333
  }
17302
17334
  },
17303
17335
  "query",
@@ -34309,7 +34341,7 @@ var backup_inspect_exports = {};
34309
34341
  __export(backup_inspect_exports, {
34310
34342
  backupInspectSubCommand: () => backupInspectSubCommand
34311
34343
  });
34312
- import crypto from "node:crypto";
34344
+ import crypto2 from "node:crypto";
34313
34345
  import fs from "node:fs";
34314
34346
  import os from "node:os";
34315
34347
  import path from "node:path";
@@ -34357,7 +34389,7 @@ function verifyManifestHash(raw, manifest) {
34357
34389
  const intObj = obj["integrity"];
34358
34390
  intObj["manifestHash"] = "";
34359
34391
  const forHashing = JSON.stringify(obj);
34360
- const computed = crypto.createHash("sha256").update(forHashing).digest("hex");
34392
+ const computed = crypto2.createHash("sha256").update(forHashing).digest("hex");
34361
34393
  return computed === expectedHash;
34362
34394
  }
34363
34395
  function fmtBytes(bytes) {
@@ -34574,7 +34606,7 @@ var backup_exports = {};
34574
34606
  __export(backup_exports, {
34575
34607
  backupCommand: () => backupCommand
34576
34608
  });
34577
- import crypto2 from "node:crypto";
34609
+ import crypto3 from "node:crypto";
34578
34610
  import fs2 from "node:fs";
34579
34611
  import path2 from "node:path";
34580
34612
  import readline from "node:readline";
@@ -34628,7 +34660,7 @@ function sha256OfMachineKey(cleoHome) {
34628
34660
  if (!fs2.existsSync(keyPath)) {
34629
34661
  return "0".repeat(64);
34630
34662
  }
34631
- return crypto2.createHash("sha256").update(fs2.readFileSync(keyPath)).digest("hex");
34663
+ return crypto3.createHash("sha256").update(fs2.readFileSync(keyPath)).digest("hex");
34632
34664
  }
34633
34665
  var addCommand2, listCommand4, exportCommand, importCommand, backupCommand;
34634
34666
  var init_backup = __esm({
@@ -44164,28 +44196,210 @@ var init_llm_cost = __esm({
44164
44196
  });
44165
44197
 
44166
44198
  // packages/cleo/src/cli/commands/llm-login.ts
44199
+ import { spawn } from "node:child_process";
44200
+ import { createServer } from "node:http";
44167
44201
  import { addCredential } from "@cleocode/core/llm/credentials-store.js";
44168
44202
  import {
44169
44203
  DeviceCodeAuthError,
44170
44204
  DeviceCodeTimeoutError,
44171
- getAnthropicDeviceCodeConfig,
44205
+ getKimiCodeDeviceCodeConfig,
44172
44206
  pollForToken,
44173
44207
  startDeviceCodeFlow
44174
44208
  } from "@cleocode/core/llm/oauth/device-code.js";
44209
+ import {
44210
+ buildAuthorizationUrl,
44211
+ exchangePkceCode,
44212
+ generatePkcePair,
44213
+ refreshPkceToken
44214
+ } from "@cleocode/core/llm/oauth/pkce.js";
44215
+ import { getKimiCodeMshHeaders } from "@cleocode/core/llm/provider-registry/builtin/kimi-code.js";
44216
+ import { getProviderProfile } from "@cleocode/core/llm/provider-registry/index.js";
44175
44217
  async function runLlmLogin(provider, opts) {
44176
44218
  const meta = { operation: "llm.login", timestamp: (/* @__PURE__ */ new Date()).toISOString() };
44177
- if (provider !== "anthropic") {
44219
+ const profile = await getProviderProfile(provider);
44220
+ const oauthMode = profile?.oauth?.mode;
44221
+ if (provider === "kimi-code" || oauthMode === "device-code") {
44222
+ return _runKimiCodeLogin(opts, meta);
44223
+ }
44224
+ if (oauthMode === "pkce") {
44225
+ return _runPkceLogin(provider, profile.oauth, opts, meta);
44226
+ }
44227
+ return {
44228
+ success: false,
44229
+ error: {
44230
+ code: "E_NOT_IMPLEMENTED",
44231
+ codeName: "E_NOT_IMPLEMENTED",
44232
+ 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'.`
44233
+ },
44234
+ meta
44235
+ };
44236
+ }
44237
+ async function _runPkceLogin(provider, oauthCfg, opts, meta) {
44238
+ const { codeVerifier, codeChallenge } = await generatePkcePair();
44239
+ const state = _generateState();
44240
+ const isHeadless = opts.headless || process.env["CLEO_HEADLESS"] === "1";
44241
+ const port = isHeadless ? 0 : await _findFreePort();
44242
+ const redirectUri = isHeadless ? oauthCfg.redirectUri ?? "http://localhost" : `http://localhost:${port}/callback`;
44243
+ const authUrl = buildAuthorizationUrl({
44244
+ authorizationEndpoint: oauthCfg.authorizationEndpoint ?? "",
44245
+ clientId: oauthCfg.clientId,
44246
+ redirectUri,
44247
+ scope: oauthCfg.scope ?? "",
44248
+ codeChallenge,
44249
+ state
44250
+ });
44251
+ let code;
44252
+ if (isHeadless) {
44253
+ code = await _headlessPkceFlow(provider, authUrl);
44254
+ } else {
44255
+ const result = await _localCallbackPkceFlow(provider, authUrl, state, port);
44256
+ if ("error" in result) {
44257
+ return { success: false, error: result.error, meta };
44258
+ }
44259
+ code = result.code;
44260
+ }
44261
+ let tokens;
44262
+ try {
44263
+ tokens = await exchangePkceCode({
44264
+ provider,
44265
+ clientId: oauthCfg.clientId,
44266
+ code,
44267
+ codeVerifier,
44268
+ redirectUri,
44269
+ tokenEndpoint: oauthCfg.tokenEndpoint
44270
+ });
44271
+ } catch (err) {
44272
+ const msg = err instanceof Error ? err.message : String(err);
44178
44273
  return {
44179
44274
  success: false,
44180
44275
  error: {
44181
- code: "E_NOT_IMPLEMENTED",
44182
- codeName: "E_NOT_IMPLEMENTED",
44183
- message: `OAuth device-code login for '${provider}' is not yet wired. 'anthropic' is the only supported provider in the current MVP. To add credentials for other providers use 'cleo llm add <provider> --api-key-stdin'.`
44276
+ code: "E_PKCE_EXCHANGE_FAILED",
44277
+ codeName: "E_PKCE_EXCHANGE_FAILED",
44278
+ message: `PKCE code exchange failed: ${msg}`
44184
44279
  },
44185
44280
  meta
44186
44281
  };
44187
44282
  }
44188
- const cfg = getAnthropicDeviceCodeConfig();
44283
+ process.stderr.write("\r Authorization approved. \n\n");
44284
+ const label = opts.label ?? "oauth-login";
44285
+ const expiresAt = typeof tokens.expiresIn === "number" ? Date.now() + tokens.expiresIn * 1e3 : void 0;
44286
+ try {
44287
+ await addCredential({
44288
+ provider,
44289
+ label,
44290
+ authType: "oauth",
44291
+ accessToken: tokens.accessToken,
44292
+ refreshToken: tokens.refreshToken,
44293
+ expiresAt,
44294
+ priority: 10,
44295
+ source: "oauth-pkce",
44296
+ extraHeaders: { "anthropic-beta": "oauth-2025-04-20" }
44297
+ });
44298
+ } catch (err) {
44299
+ const msg = err instanceof Error ? err.message : String(err);
44300
+ return {
44301
+ success: false,
44302
+ error: {
44303
+ code: "E_CREDENTIAL_STORE_FAILED",
44304
+ codeName: "E_CREDENTIAL_STORE_FAILED",
44305
+ message: `Failed to store credential in pool: ${msg}`
44306
+ },
44307
+ meta
44308
+ };
44309
+ }
44310
+ return {
44311
+ success: true,
44312
+ data: { provider, label, expiresIn: tokens.expiresIn },
44313
+ meta
44314
+ };
44315
+ }
44316
+ async function _headlessPkceFlow(provider, authUrl) {
44317
+ process.stderr.write("\n");
44318
+ process.stderr.write(` Provider: ${provider}
44319
+ `);
44320
+ process.stderr.write(` Open this URL in your browser to authorize:
44321
+
44322
+ `);
44323
+ process.stderr.write(` ${authUrl}
44324
+
44325
+ `);
44326
+ process.stderr.write(
44327
+ ` After approving, paste the full redirect URL (http://localhost?code=\u2026&state=\u2026):
44328
+ `
44329
+ );
44330
+ return new Promise((resolve7, reject) => {
44331
+ let buf = "";
44332
+ process.stdin.setEncoding("utf8");
44333
+ process.stdin.once("data", (chunk) => {
44334
+ buf += String(chunk).trim();
44335
+ try {
44336
+ const url = new URL(buf);
44337
+ const code = url.searchParams.get("code");
44338
+ if (!code) {
44339
+ reject(new Error('Redirect URL is missing the "code" parameter'));
44340
+ return;
44341
+ }
44342
+ resolve7(code);
44343
+ } catch {
44344
+ reject(new Error(`Invalid redirect URL: ${buf}`));
44345
+ }
44346
+ });
44347
+ });
44348
+ }
44349
+ async function _localCallbackPkceFlow(provider, authUrl, expectedState, port) {
44350
+ return new Promise((resolve7) => {
44351
+ const server = createServer((req, res) => {
44352
+ const url = new URL(req.url ?? "/", `http://localhost:${port}`);
44353
+ const code = url.searchParams.get("code");
44354
+ const state = url.searchParams.get("state");
44355
+ const error = url.searchParams.get("error");
44356
+ res.writeHead(200, { "Content-Type": "text/html" });
44357
+ if (error) {
44358
+ res.end(`<h1>Authorization failed</h1><p>${error}</p><p>You may close this tab.</p>`);
44359
+ server.close();
44360
+ resolve7({
44361
+ error: {
44362
+ code: "E_PKCE_AUTH_DENIED",
44363
+ codeName: "E_PKCE_AUTH_DENIED",
44364
+ message: `OAuth authorization denied for '${provider}': ${error}`
44365
+ }
44366
+ });
44367
+ return;
44368
+ }
44369
+ if (!code || state !== expectedState) {
44370
+ res.end("<h1>Invalid callback</h1><p>You may close this tab.</p>");
44371
+ server.close();
44372
+ resolve7({
44373
+ error: {
44374
+ code: "E_PKCE_INVALID_CALLBACK",
44375
+ codeName: "E_PKCE_INVALID_CALLBACK",
44376
+ message: "OAuth callback received invalid code or state mismatch"
44377
+ }
44378
+ });
44379
+ return;
44380
+ }
44381
+ res.end("<h1>Authorized</h1><p>You may close this tab and return to your terminal.</p>");
44382
+ server.close();
44383
+ resolve7({ code });
44384
+ });
44385
+ server.listen(port, "localhost", () => {
44386
+ process.stderr.write("\n");
44387
+ process.stderr.write(` Provider: ${provider}
44388
+ `);
44389
+ process.stderr.write(` Open this URL to authorize (or it will open automatically):
44390
+
44391
+ `);
44392
+ process.stderr.write(` ${authUrl}
44393
+
44394
+ `);
44395
+ process.stderr.write(` Waiting for authorization callback...
44396
+ `);
44397
+ _tryOpenBrowser(authUrl);
44398
+ });
44399
+ });
44400
+ }
44401
+ async function _runKimiCodeLogin(opts, meta) {
44402
+ const cfg = getKimiCodeDeviceCodeConfig();
44189
44403
  let startResp;
44190
44404
  try {
44191
44405
  startResp = await startDeviceCodeFlow(cfg);
@@ -44196,7 +44410,7 @@ async function runLlmLogin(provider, opts) {
44196
44410
  error: {
44197
44411
  code: "E_DEVICE_CODE_START_FAILED",
44198
44412
  codeName: "E_DEVICE_CODE_START_FAILED",
44199
- message: `Failed to initiate device-code OAuth flow: ${msg}`
44413
+ message: `Failed to initiate Kimi Code device-code OAuth flow: ${msg}`
44200
44414
  },
44201
44415
  meta
44202
44416
  };
@@ -44210,7 +44424,7 @@ async function runLlmLogin(provider, opts) {
44210
44424
  `);
44211
44425
  process.stderr.write("\n");
44212
44426
  process.stderr.write(
44213
- ` Waiting for authorization (up to ${Math.round(startResp.expiresIn / 60)} min)...
44427
+ ` Waiting for Kimi Code authorization (up to ${Math.round(startResp.expiresIn / 60)} min)...
44214
44428
  `
44215
44429
  );
44216
44430
  let tokenResp;
@@ -44250,17 +44464,17 @@ async function runLlmLogin(provider, opts) {
44250
44464
  error: {
44251
44465
  code: "E_DEVICE_CODE_POLL_FAILED",
44252
44466
  codeName: "E_DEVICE_CODE_POLL_FAILED",
44253
- message: `Polling for device code token failed: ${msg}`
44467
+ message: `Polling for Kimi Code device-code token failed: ${msg}`
44254
44468
  },
44255
44469
  meta
44256
44470
  };
44257
44471
  }
44258
- process.stderr.write("\r Authorization approved. \n\n");
44472
+ process.stderr.write("\r Kimi Code authorization approved. \n\n");
44259
44473
  const label = opts.label ?? "oauth-login";
44260
44474
  const expiresAt = typeof tokenResp.expiresIn === "number" ? Date.now() + tokenResp.expiresIn * 1e3 : void 0;
44261
44475
  try {
44262
44476
  await addCredential({
44263
- provider: "anthropic",
44477
+ provider: "kimi-code",
44264
44478
  label,
44265
44479
  authType: "oauth",
44266
44480
  accessToken: tokenResp.accessToken,
@@ -44268,7 +44482,9 @@ async function runLlmLogin(provider, opts) {
44268
44482
  expiresAt,
44269
44483
  priority: 10,
44270
44484
  source: "oauth-device-code",
44271
- extraHeaders: { "anthropic-beta": "oauth-2025-04-20" }
44485
+ // The six mandatory X-Msh-* headers must be carried alongside the token
44486
+ // so every transport that picks this credential sends them automatically.
44487
+ extraHeaders: getKimiCodeMshHeaders()
44272
44488
  });
44273
44489
  } catch (err) {
44274
44490
  const msg = err instanceof Error ? err.message : String(err);
@@ -44277,7 +44493,7 @@ async function runLlmLogin(provider, opts) {
44277
44493
  error: {
44278
44494
  code: "E_CREDENTIAL_STORE_FAILED",
44279
44495
  codeName: "E_CREDENTIAL_STORE_FAILED",
44280
- message: `Failed to store credential in pool: ${msg}`
44496
+ message: `Failed to store Kimi Code credential in pool: ${msg}`
44281
44497
  },
44282
44498
  meta
44283
44499
  };
@@ -44285,13 +44501,40 @@ async function runLlmLogin(provider, opts) {
44285
44501
  return {
44286
44502
  success: true,
44287
44503
  data: {
44288
- provider: "anthropic",
44504
+ provider: "kimi-code",
44289
44505
  label,
44290
44506
  expiresIn: tokenResp.expiresIn
44291
44507
  },
44292
44508
  meta
44293
44509
  };
44294
44510
  }
44511
+ function _generateState() {
44512
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
44513
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
44514
+ }
44515
+ function _findFreePort() {
44516
+ return new Promise((resolve7, reject) => {
44517
+ const srv = createServer();
44518
+ srv.listen(0, "localhost", () => {
44519
+ const addr = srv.address();
44520
+ if (!addr || typeof addr === "string") {
44521
+ srv.close();
44522
+ reject(new Error("Could not determine callback port"));
44523
+ return;
44524
+ }
44525
+ const port = addr.port;
44526
+ srv.close(() => resolve7(port));
44527
+ });
44528
+ srv.on("error", reject);
44529
+ });
44530
+ }
44531
+ function _tryOpenBrowser(url) {
44532
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
44533
+ try {
44534
+ spawn(cmd, [url], { detached: true, stdio: "ignore" }).unref();
44535
+ } catch {
44536
+ }
44537
+ }
44295
44538
  var init_llm_login = __esm({
44296
44539
  "packages/cleo/src/cli/commands/llm-login.ts"() {
44297
44540
  "use strict";
@@ -58641,7 +58884,7 @@ var web_exports = {};
58641
58884
  __export(web_exports, {
58642
58885
  webCommand: () => webCommand
58643
58886
  });
58644
- import { execFileSync as execFileSync4, spawn } from "node:child_process";
58887
+ import { execFileSync as execFileSync4, spawn as spawn2 } from "node:child_process";
58645
58888
  import { mkdir as mkdir3, open, readFile as readFile5, rm, stat, writeFile as writeFile2 } from "node:fs/promises";
58646
58889
  import { join as join24 } from "node:path";
58647
58890
  import { CleoError as CleoError12, formatError as formatError9, getCleoHome as getCleoHome2 } from "@cleocode/core";
@@ -58718,7 +58961,7 @@ Logs: ${logFile}`
58718
58961
  }
58719
58962
  }
58720
58963
  const logFileHandle = await open(logFile, "a");
58721
- const serverProcess = spawn("node", [webIndexPath], {
58964
+ const serverProcess = spawn2("node", [webIndexPath], {
58722
58965
  cwd: studioDir,
58723
58966
  env: {
58724
58967
  ...process.env,
@@ -58817,7 +59060,7 @@ var init_web = __esm({
58817
59060
  }
58818
59061
  try {
58819
59062
  if (process.platform === "win32") {
58820
- spawn("taskkill", ["/PID", String(status.pid), "/T"], { stdio: "ignore" });
59063
+ spawn2("taskkill", ["/PID", String(status.pid), "/T"], { stdio: "ignore" });
58821
59064
  } else {
58822
59065
  process.kill(status.pid, "SIGTERM");
58823
59066
  }
@@ -58830,7 +59073,7 @@ var init_web = __esm({
58830
59073
  if (isProcessRunning(status.pid)) {
58831
59074
  try {
58832
59075
  if (process.platform === "win32") {
58833
- spawn("taskkill", ["/PID", String(status.pid), "/F", "/T"], { stdio: "ignore" });
59076
+ spawn2("taskkill", ["/PID", String(status.pid), "/F", "/T"], { stdio: "ignore" });
58834
59077
  } else {
58835
59078
  process.kill(status.pid, "SIGKILL");
58836
59079
  }
@@ -58869,7 +59112,7 @@ var init_web = __esm({
58869
59112
  if (status.running && status.pid) {
58870
59113
  try {
58871
59114
  if (process.platform === "win32") {
58872
- spawn("taskkill", ["/PID", String(status.pid), "/T"], { stdio: "ignore" });
59115
+ spawn2("taskkill", ["/PID", String(status.pid), "/T"], { stdio: "ignore" });
58873
59116
  } else {
58874
59117
  process.kill(status.pid, "SIGTERM");
58875
59118
  }
@@ -58882,7 +59125,7 @@ var init_web = __esm({
58882
59125
  if (isProcessRunning(status.pid)) {
58883
59126
  try {
58884
59127
  if (process.platform === "win32") {
58885
- spawn("taskkill", ["/PID", String(status.pid), "/F", "/T"], { stdio: "ignore" });
59128
+ spawn2("taskkill", ["/PID", String(status.pid), "/F", "/T"], { stdio: "ignore" });
58886
59129
  } else {
58887
59130
  process.kill(status.pid, "SIGKILL");
58888
59131
  }
@@ -58931,11 +59174,11 @@ var init_web = __esm({
58931
59174
  const platform = process.platform;
58932
59175
  try {
58933
59176
  if (platform === "linux") {
58934
- spawn("xdg-open", [url], { detached: true, stdio: "ignore" }).unref();
59177
+ spawn2("xdg-open", [url], { detached: true, stdio: "ignore" }).unref();
58935
59178
  } else if (platform === "darwin") {
58936
- spawn("open", [url], { detached: true, stdio: "ignore" }).unref();
59179
+ spawn2("open", [url], { detached: true, stdio: "ignore" }).unref();
58937
59180
  } else if (platform === "win32") {
58938
- spawn("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore" }).unref();
59181
+ spawn2("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore" }).unref();
58939
59182
  }
58940
59183
  } catch {
58941
59184
  }