openkitt 0.3.2 → 0.3.4

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.
Files changed (2) hide show
  1. package/dist/cli.js +367 -41
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2583,9 +2583,12 @@ var exports_config = {};
2583
2583
  __export(exports_config, {
2584
2584
  updateLlmModel: () => updateLlmModel,
2585
2585
  storeLlmCredentials: () => storeLlmCredentials,
2586
+ storeCopilotCredentials: () => storeCopilotCredentials,
2586
2587
  isLlmConfigured: () => isLlmConfigured,
2587
2588
  getLlmConfig: () => getLlmConfig,
2588
2589
  getLlmApiKey: () => getLlmApiKey,
2590
+ getCopilotToken: () => getCopilotToken,
2591
+ exchangeCopilotToken: () => exchangeCopilotToken,
2589
2592
  clearLlmCredentials: () => clearLlmCredentials
2590
2593
  });
2591
2594
  import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, statSync as statSync2, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
@@ -2656,7 +2659,10 @@ function writeConfig(config) {
2656
2659
  }
2657
2660
  }
2658
2661
  function isStoredProvider(value) {
2659
- return value === "anthropic" || value === "openai" || value === "gemini";
2662
+ return value === "anthropic" || value === "openai" || value === "gemini" || value === "github-copilot";
2663
+ }
2664
+ function isStoredAuthType(value) {
2665
+ return value === "api-key" || value === "github-copilot";
2660
2666
  }
2661
2667
  function getStoredLlm(config) {
2662
2668
  const llm = config.llm;
@@ -2675,6 +2681,17 @@ function getStoredLlm(config) {
2675
2681
  if (llm.storage !== "keychain" && llm.storage !== "encrypted") {
2676
2682
  return null;
2677
2683
  }
2684
+ if (llm.provider === "github-copilot" && llm.authType === "github-copilot") {
2685
+ const c = llm;
2686
+ if (typeof c.githubToken !== "string" || typeof c.githubIv !== "string" || typeof c.githubSalt !== "string" || typeof c.githubAuthTag !== "string" || typeof c.copilotToken !== "string" || typeof c.copilotIv !== "string" || typeof c.copilotSalt !== "string" || typeof c.copilotAuthTag !== "string" || typeof c.copilotTokenExpiresAt !== "number") {
2687
+ return null;
2688
+ }
2689
+ return c;
2690
+ }
2691
+ const authType = llm.authType;
2692
+ if (authType !== undefined && !isStoredAuthType(authType)) {
2693
+ return null;
2694
+ }
2678
2695
  return llm;
2679
2696
  }
2680
2697
  async function storeLlmCredentials(provider, model, apiKey) {
@@ -2688,6 +2705,7 @@ async function storeLlmCredentials(provider, model, apiKey) {
2688
2705
  provider,
2689
2706
  model,
2690
2707
  validatedAt,
2708
+ authType: "api-key",
2691
2709
  storage: "keychain"
2692
2710
  }
2693
2711
  });
@@ -2701,6 +2719,7 @@ async function storeLlmCredentials(provider, model, apiKey) {
2701
2719
  provider,
2702
2720
  model,
2703
2721
  validatedAt,
2722
+ authType: "api-key",
2704
2723
  storage: "encrypted",
2705
2724
  apiKey: encrypted.encrypted,
2706
2725
  iv: encrypted.iv,
@@ -2712,26 +2731,117 @@ async function storeLlmCredentials(provider, model, apiKey) {
2712
2731
  async function getLlmApiKey() {
2713
2732
  const config = readConfig();
2714
2733
  const llm = getStoredLlm(config);
2715
- if (!llm) {
2734
+ if (!llm || llm.authType === "github-copilot") {
2716
2735
  return null;
2717
2736
  }
2718
2737
  if (llm.storage === "keychain") {
2719
2738
  return getKeychainValue(KEYCHAIN_API_KEY);
2720
2739
  }
2721
- if (typeof llm.apiKey !== "string" || typeof llm.iv !== "string" || typeof llm.salt !== "string" || typeof llm.authTag !== "string") {
2740
+ const enc = llm;
2741
+ if (typeof enc.apiKey !== "string" || typeof enc.iv !== "string" || typeof enc.salt !== "string" || typeof enc.authTag !== "string") {
2722
2742
  return null;
2723
2743
  }
2724
2744
  try {
2725
2745
  return await decrypt({
2726
- encrypted: llm.apiKey,
2727
- iv: llm.iv,
2728
- salt: llm.salt,
2729
- authTag: llm.authTag
2746
+ encrypted: enc.apiKey,
2747
+ iv: enc.iv,
2748
+ salt: enc.salt,
2749
+ authTag: enc.authTag
2750
+ });
2751
+ } catch {
2752
+ return null;
2753
+ }
2754
+ }
2755
+ async function storeCopilotCredentials(model, githubToken, copilotToken, copilotTokenExpiresAt) {
2756
+ const validatedAt = new Date().toISOString();
2757
+ const [encGithub, encCopilot] = await Promise.all([
2758
+ encrypt(githubToken),
2759
+ encrypt(copilotToken)
2760
+ ]);
2761
+ writeConfig({
2762
+ llm: {
2763
+ provider: "github-copilot",
2764
+ model,
2765
+ validatedAt,
2766
+ authType: "github-copilot",
2767
+ storage: "encrypted",
2768
+ githubToken: encGithub.encrypted,
2769
+ githubIv: encGithub.iv,
2770
+ githubSalt: encGithub.salt,
2771
+ githubAuthTag: encGithub.authTag,
2772
+ copilotToken: encCopilot.encrypted,
2773
+ copilotIv: encCopilot.iv,
2774
+ copilotSalt: encCopilot.salt,
2775
+ copilotAuthTag: encCopilot.authTag,
2776
+ copilotTokenExpiresAt
2777
+ }
2778
+ });
2779
+ }
2780
+ async function decryptCopilotStored(stored) {
2781
+ try {
2782
+ const [githubToken, copilotToken] = await Promise.all([
2783
+ decrypt({ encrypted: stored.githubToken, iv: stored.githubIv, salt: stored.githubSalt, authTag: stored.githubAuthTag }),
2784
+ decrypt({ encrypted: stored.copilotToken, iv: stored.copilotIv, salt: stored.copilotSalt, authTag: stored.copilotAuthTag })
2785
+ ]);
2786
+ return { githubToken, copilotToken };
2787
+ } catch {
2788
+ return null;
2789
+ }
2790
+ }
2791
+ async function refreshCopilotToken(githubToken) {
2792
+ try {
2793
+ const res = await fetch(COPILOT_TOKEN_URL, {
2794
+ headers: {
2795
+ Authorization: `Token ${githubToken}`,
2796
+ "User-Agent": "GitHubCopilotChat/0.26.7"
2797
+ }
2730
2798
  });
2799
+ if (!res.ok)
2800
+ return null;
2801
+ const data = await res.json();
2802
+ if (typeof data.token !== "string" || typeof data.expires_at !== "number")
2803
+ return null;
2804
+ return { token: data.token, expiresAt: data.expires_at * 1000 };
2731
2805
  } catch {
2732
2806
  return null;
2733
2807
  }
2734
2808
  }
2809
+ async function getCopilotToken() {
2810
+ const config = readConfig();
2811
+ const llm = getStoredLlm(config);
2812
+ if (!llm || llm.authType !== "github-copilot") {
2813
+ return null;
2814
+ }
2815
+ const stored = llm;
2816
+ const decrypted = await decryptCopilotStored(stored);
2817
+ if (!decrypted)
2818
+ return null;
2819
+ if (Date.now() < stored.copilotTokenExpiresAt - 60000) {
2820
+ return decrypted.copilotToken;
2821
+ }
2822
+ const refreshed = await refreshCopilotToken(decrypted.githubToken);
2823
+ if (!refreshed)
2824
+ return null;
2825
+ const encCopilot = await encrypt(refreshed.token);
2826
+ writeConfig({
2827
+ llm: {
2828
+ ...stored,
2829
+ copilotToken: encCopilot.encrypted,
2830
+ copilotIv: encCopilot.iv,
2831
+ copilotSalt: encCopilot.salt,
2832
+ copilotAuthTag: encCopilot.authTag,
2833
+ copilotTokenExpiresAt: refreshed.expiresAt
2834
+ }
2835
+ });
2836
+ return refreshed.token;
2837
+ }
2838
+ async function exchangeCopilotToken(githubToken) {
2839
+ const result = await refreshCopilotToken(githubToken);
2840
+ if (!result) {
2841
+ throw new Error("Failed to exchange GitHub token for Copilot bearer token. Check your Copilot subscription is active.");
2842
+ }
2843
+ return result;
2844
+ }
2735
2845
  async function getLlmConfig() {
2736
2846
  const config = readConfig();
2737
2847
  const llm = getStoredLlm(config);
@@ -2741,7 +2851,8 @@ async function getLlmConfig() {
2741
2851
  return {
2742
2852
  provider: llm.provider,
2743
2853
  model: llm.model,
2744
- validatedAt: llm.validatedAt
2854
+ validatedAt: llm.validatedAt,
2855
+ authType: llm.authType ?? "api-key"
2745
2856
  };
2746
2857
  }
2747
2858
  async function updateLlmModel(model) {
@@ -2769,10 +2880,17 @@ async function clearLlmCredentials() {
2769
2880
  writeConfig(nextConfig);
2770
2881
  }
2771
2882
  async function isLlmConfigured() {
2772
- const [apiKey, llmConfig] = await Promise.all([getLlmApiKey(), getLlmConfig()]);
2773
- return apiKey !== null && llmConfig !== null;
2883
+ const llmConfig = await getLlmConfig();
2884
+ if (!llmConfig)
2885
+ return false;
2886
+ if (llmConfig.authType === "github-copilot") {
2887
+ const token = await getCopilotToken();
2888
+ return token !== null;
2889
+ }
2890
+ const apiKey = await getLlmApiKey();
2891
+ return apiKey !== null;
2774
2892
  }
2775
- var CONFIG_DIR, CONFIG_FILE, KEYCHAIN_API_KEY = "llm-api-key", DIR_MODE = 448, FILE_MODE = 384;
2893
+ var CONFIG_DIR, CONFIG_FILE, KEYCHAIN_API_KEY = "llm-api-key", DIR_MODE = 448, FILE_MODE = 384, COPILOT_TOKEN_URL = "https://api.github.com/copilot_internal/v2/token";
2776
2894
  var init_config = __esm(() => {
2777
2895
  init_encryption();
2778
2896
  init_keychain();
@@ -255595,9 +255713,9 @@ var {
255595
255713
  var import_picocolors12 = __toESM(require_picocolors(), 1);
255596
255714
  import { createInterface, emitKeypressEvents } from "node:readline";
255597
255715
  import { stdin as input, stdout as output } from "node:process";
255598
- import { existsSync as existsSync20, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync15, realpathSync } from "node:fs";
255599
- import { homedir as homedir5 } from "node:os";
255600
- import { join as join23, resolve as resolve6, dirname as dirname5 } from "node:path";
255716
+ import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync15, writeFileSync as writeFileSync15, realpathSync } from "node:fs";
255717
+ import { homedir as homedir6 } from "node:os";
255718
+ import { join as join24, resolve as resolve6, dirname as dirname5 } from "node:path";
255601
255719
  import { pathToFileURL } from "node:url";
255602
255720
 
255603
255721
  // src/utils/prerequisites.ts
@@ -256991,7 +257109,8 @@ var WORKSPACE_REQUIRED = new Set([
256991
257109
  "domain",
256992
257110
  "logs",
256993
257111
  "status",
256994
- "settings"
257112
+ "settings",
257113
+ "versions"
256995
257114
  ]);
256996
257115
  var APPS_REQUIRED = new Set([
256997
257116
  "delete",
@@ -257757,11 +257876,36 @@ function createGeminiClient(Provider, apiKey, model, rateLimiter) {
257757
257876
  }
257758
257877
  };
257759
257878
  }
257879
+ var COPILOT_BASE_URL = "https://api.githubcopilot.com";
257880
+ var COPILOT_HEADERS = {
257881
+ "Editor-Version": "vscode/1.85.0",
257882
+ "Editor-Plugin-Version": "copilot-chat/0.26.7",
257883
+ "Copilot-Integration-Id": "vscode-chat"
257884
+ };
257885
+ async function createCopilotClient(bearerToken, model, rateLimiter) {
257886
+ const OpenAI2 = (await Promise.resolve().then(() => (init_openai(), exports_openai))).default;
257887
+ const CopilotProvider = function(options) {
257888
+ return new OpenAI2({
257889
+ apiKey: options.apiKey,
257890
+ baseURL: COPILOT_BASE_URL,
257891
+ defaultHeaders: COPILOT_HEADERS
257892
+ });
257893
+ };
257894
+ return createOpenAiClient(CopilotProvider, bearerToken, model, rateLimiter);
257895
+ }
257760
257896
  async function createLlmClient(rateLimiter) {
257761
- const [llmConfig, apiKey] = await Promise.all([getLlmConfig(), getLlmApiKey()]);
257897
+ const llmConfig = await getLlmConfig();
257762
257898
  if (!llmConfig) {
257763
257899
  throw new Error("LLM provider is not configured. Run /login llm to configure it.");
257764
257900
  }
257901
+ if (llmConfig.provider === "github-copilot") {
257902
+ const copilotToken = await getCopilotToken();
257903
+ if (!copilotToken) {
257904
+ throw new Error("GitHub Copilot token unavailable. Run /login llm to re-authenticate.");
257905
+ }
257906
+ return await createCopilotClient(copilotToken, llmConfig.model, rateLimiter);
257907
+ }
257908
+ const apiKey = await getLlmApiKey();
257765
257909
  if (!apiKey) {
257766
257910
  throw new Error("LLM API key is not configured. Run /login llm to configure it.");
257767
257911
  }
@@ -264917,6 +265061,9 @@ async function statusCommand(_context, _args, _commandKey) {
264917
265061
  }
264918
265062
 
264919
265063
  // src/commands/login.ts
265064
+ import { existsSync as existsSync19, readFileSync as readFileSync13 } from "node:fs";
265065
+ import { homedir as homedir5 } from "node:os";
265066
+ import { join as join22 } from "node:path";
264920
265067
  init_config();
264921
265068
  var import_picocolors8 = __toESM(require_picocolors(), 1);
264922
265069
  var PROVIDER_LABELS = {
@@ -264947,10 +265094,23 @@ var MODEL_OPTIONS = {
264947
265094
  { value: "gemini-1.5-pro", label: "gemini-1.5-pro" }
264948
265095
  ]
264949
265096
  };
265097
+ var COPILOT_MODEL_OPTIONS = [
265098
+ { value: "claude-sonnet-4", label: "claude-sonnet-4 (recommended — Claude via Copilot)" },
265099
+ { value: "gpt-4o", label: "gpt-4o (GPT-4o via Copilot)" },
265100
+ { value: "gpt-4.1", label: "gpt-4.1 (GPT-4.1 via Copilot)" },
265101
+ { value: "o4-mini", label: "o4-mini (o4-mini via Copilot)" },
265102
+ { value: "gemini-2.5-pro", label: "gemini-2.5-pro (Gemini 2.5 Pro via Copilot)" }
265103
+ ];
265104
+ var GITHUB_DEVICE_CLIENT_ID = "01ab8ac9400c4e429b23";
264950
265105
  function cancelled() {
264951
265106
  console.log(import_picocolors8.default.yellow("Cancelled."));
264952
265107
  return true;
264953
265108
  }
265109
+ function providerLabel(config) {
265110
+ if (config.authType === "github-copilot")
265111
+ return "GitHub Copilot";
265112
+ return PROVIDER_LABELS[config.provider] ?? config.provider;
265113
+ }
264954
265114
  async function runRailwayLogin() {
264955
265115
  const status = await checkRailwayAuth();
264956
265116
  if (status.authenticated) {
@@ -264971,6 +265131,20 @@ async function runRailwayLogin() {
264971
265131
  }
264972
265132
  console.log(import_picocolors8.default.red(loginStatus.error ?? "Failed to authenticate with Railway."));
264973
265133
  }
265134
+ async function promptAuthMode() {
265135
+ const mode = await ve({
265136
+ message: "How do you want to connect to an LLM?",
265137
+ options: [
265138
+ { value: "api-key", label: "API Key (Anthropic, OpenAI, or Gemini — pay per token)" },
265139
+ { value: "github-copilot", label: "GitHub Copilot (use your Copilot subscription — Claude, GPT, Gemini included)" }
265140
+ ]
265141
+ });
265142
+ if (pD(mode)) {
265143
+ cancelled();
265144
+ return null;
265145
+ }
265146
+ return mode;
265147
+ }
264974
265148
  async function promptProvider() {
264975
265149
  const provider = await ve({
264976
265150
  message: "Select LLM provider:",
@@ -264993,14 +265167,105 @@ async function promptModel(provider) {
264993
265167
  }
264994
265168
  return model;
264995
265169
  }
264996
- async function runLlmLogin(context) {
265170
+ function readGhCliToken() {
265171
+ try {
265172
+ const hostsFile = join22(homedir5(), ".config", "gh", "hosts.yml");
265173
+ if (!existsSync19(hostsFile))
265174
+ return null;
265175
+ const raw = readFileSync13(hostsFile, "utf-8");
265176
+ for (const line of raw.split(`
265177
+ `)) {
265178
+ const match = line.match(/^\s*oauth_token:\s*(.+)$/);
265179
+ if (match?.[1]) {
265180
+ return match[1].trim();
265181
+ }
265182
+ }
265183
+ return null;
265184
+ } catch {
265185
+ return null;
265186
+ }
265187
+ }
265188
+ async function runGitHubDeviceFlow() {
265189
+ try {
265190
+ const codeRes = await fetch("https://github.com/login/device/code", {
265191
+ method: "POST",
265192
+ headers: {
265193
+ Accept: "application/json",
265194
+ "Content-Type": "application/json"
265195
+ },
265196
+ body: JSON.stringify({
265197
+ client_id: GITHUB_DEVICE_CLIENT_ID,
265198
+ scope: "read:user"
265199
+ })
265200
+ });
265201
+ if (!codeRes.ok) {
265202
+ console.log(import_picocolors8.default.red("Failed to start GitHub login. Check your internet connection."));
265203
+ return null;
265204
+ }
265205
+ const codeData = await codeRes.json();
265206
+ console.log();
265207
+ console.log(import_picocolors8.default.bold("GitHub login"));
265208
+ console.log(`Open ${import_picocolors8.default.cyan(codeData.verification_uri)} and enter code: ${import_picocolors8.default.bold(import_picocolors8.default.yellow(codeData.user_code))}`);
265209
+ console.log();
265210
+ try {
265211
+ const openModule = await import("open");
265212
+ await openModule.default(codeData.verification_uri);
265213
+ } catch {}
265214
+ const intervalMs = (codeData.interval ?? 5) * 1000;
265215
+ const expiresAt = Date.now() + (codeData.expires_in ?? 900) * 1000;
265216
+ while (Date.now() < expiresAt) {
265217
+ await new Promise((resolve6) => setTimeout(resolve6, intervalMs));
265218
+ const tokenRes = await fetch("https://github.com/login/oauth/access_token", {
265219
+ method: "POST",
265220
+ headers: {
265221
+ Accept: "application/json",
265222
+ "Content-Type": "application/json"
265223
+ },
265224
+ body: JSON.stringify({
265225
+ client_id: GITHUB_DEVICE_CLIENT_ID,
265226
+ device_code: codeData.device_code,
265227
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
265228
+ })
265229
+ });
265230
+ if (!tokenRes.ok)
265231
+ continue;
265232
+ const tokenData = await tokenRes.json();
265233
+ if (tokenData.access_token) {
265234
+ return tokenData.access_token;
265235
+ }
265236
+ if (tokenData.error === "authorization_pending") {
265237
+ continue;
265238
+ }
265239
+ if (tokenData.error === "slow_down") {
265240
+ await new Promise((resolve6) => setTimeout(resolve6, intervalMs));
265241
+ continue;
265242
+ }
265243
+ console.log(import_picocolors8.default.red(tokenData.error_description ?? `GitHub login failed: ${tokenData.error}`));
265244
+ return null;
265245
+ }
265246
+ console.log(import_picocolors8.default.red("GitHub login timed out. Run /login llm again to retry."));
265247
+ return null;
265248
+ } catch {
265249
+ console.log(import_picocolors8.default.red("GitHub login failed. Check your internet connection."));
265250
+ return null;
265251
+ }
265252
+ }
265253
+ async function getGitHubToken() {
265254
+ const ghToken = readGhCliToken();
265255
+ if (ghToken) {
265256
+ console.log(import_picocolors8.default.dim("Using existing GitHub CLI session."));
265257
+ return ghToken;
265258
+ }
265259
+ console.log(import_picocolors8.default.cyan("No GitHub CLI session found. Opening GitHub login in your browser..."));
265260
+ return runGitHubDeviceFlow();
265261
+ }
265262
+ async function runCopilotLogin(context) {
264997
265263
  const existingConfig = await getLlmConfig();
264998
- if (existingConfig) {
264999
- const providerName = PROVIDER_LABELS[existingConfig.provider];
265000
- console.log(import_picocolors8.default.green(`✓ LLM ${providerName} (${existingConfig.model}) already configured`));
265264
+ if (existingConfig && existingConfig.authType === "github-copilot") {
265265
+ console.log(import_picocolors8.default.green(`✓ GitHub Copilot (${existingConfig.model}) already configured`));
265001
265266
  if (!context.yes) {
265002
265267
  const shouldChange = await ye({
265003
- message: "Change LLM provider/model/key?",
265268
+ message: "Change Copilot model or re-authenticate?",
265004
265269
  initialValue: false
265005
265270
  });
265006
265271
  if (pD(shouldChange)) {
@@ -265012,14 +265277,60 @@ async function runLlmLogin(context) {
265012
265277
  }
265013
265278
  }
265014
265279
  }
265015
- const provider = await promptProvider();
265016
- if (!provider) {
265280
+ const model = await ve({
265281
+ message: "Select model (via GitHub Copilot):",
265282
+ options: COPILOT_MODEL_OPTIONS
265283
+ });
265284
+ if (pD(model)) {
265285
+ cancelled();
265017
265286
  return;
265018
265287
  }
265019
- const model = await promptModel(provider);
265020
- if (!model) {
265288
+ const githubToken = await getGitHubToken();
265289
+ if (!githubToken)
265290
+ return;
265291
+ console.log(import_picocolors8.default.dim("Authenticating with GitHub Copilot..."));
265292
+ let copilotResult;
265293
+ try {
265294
+ copilotResult = await exchangeCopilotToken(githubToken);
265295
+ } catch (err) {
265296
+ const msg = err instanceof Error ? err.message : "Unknown error";
265297
+ console.log(import_picocolors8.default.red(`✗ ${msg}`));
265298
+ console.log(import_picocolors8.default.dim("Make sure your GitHub account has an active Copilot Pro, Pro+, Business, or Enterprise subscription."));
265021
265299
  return;
265022
265300
  }
265301
+ await storeCopilotCredentials(model, githubToken, copilotResult.token, copilotResult.expiresAt);
265302
+ console.log(import_picocolors8.default.green(`✓ GitHub Copilot authenticated (${model})`));
265303
+ }
265304
+ async function runLlmLogin(context) {
265305
+ const existingConfig = await getLlmConfig();
265306
+ if (existingConfig && !context.yes) {
265307
+ const label = providerLabel(existingConfig);
265308
+ console.log(import_picocolors8.default.green(`✓ LLM ${label} (${existingConfig.model}) already configured`));
265309
+ const shouldChange = await ye({
265310
+ message: "Change LLM provider/model/key?",
265311
+ initialValue: false
265312
+ });
265313
+ if (pD(shouldChange)) {
265314
+ cancelled();
265315
+ return;
265316
+ }
265317
+ if (!shouldChange) {
265318
+ return;
265319
+ }
265320
+ }
265321
+ const authMode = await promptAuthMode();
265322
+ if (!authMode)
265323
+ return;
265324
+ if (authMode === "github-copilot") {
265325
+ await runCopilotLogin(context);
265326
+ return;
265327
+ }
265328
+ const provider = await promptProvider();
265329
+ if (!provider)
265330
+ return;
265331
+ const model = await promptModel(provider);
265332
+ if (!model)
265333
+ return;
265023
265334
  const apiKey = await ge({
265024
265335
  message: `Enter your ${PROVIDER_LABELS[provider]} API key:`,
265025
265336
  validate: (value) => {
@@ -265042,10 +265353,23 @@ async function runModelSwitch() {
265042
265353
  console.log(import_picocolors8.default.red("No LLM provider configured. Run /login llm first."));
265043
265354
  return;
265044
265355
  }
265045
- const model = await promptModel(config.provider);
265046
- if (!model) {
265356
+ if (config.authType === "github-copilot") {
265357
+ const model2 = await ve({
265358
+ message: "Select model (via GitHub Copilot):",
265359
+ options: COPILOT_MODEL_OPTIONS
265360
+ });
265361
+ if (pD(model2)) {
265362
+ cancelled();
265363
+ return;
265364
+ }
265365
+ await updateLlmModel(model2);
265366
+ console.log(import_picocolors8.default.green(`✓ Copilot model changed to ${model2}`));
265047
265367
  return;
265048
265368
  }
265369
+ const provider = config.provider;
265370
+ const model = await promptModel(provider);
265371
+ if (!model)
265372
+ return;
265049
265373
  await updateLlmModel(model);
265050
265374
  console.log(import_picocolors8.default.green(`✓ LLM model changed to ${model}`));
265051
265375
  }
@@ -265068,7 +265392,8 @@ async function runFullLogin(context) {
265068
265392
  }
265069
265393
  const [llmConfigured, llmConfig] = await Promise.all([isLlmConfigured(), getLlmConfig()]);
265070
265394
  if (llmConfigured && llmConfig) {
265071
- console.log(import_picocolors8.default.green(`✓ LLM ${PROVIDER_LABELS[llmConfig.provider]} (${llmConfig.model})`));
265395
+ const label = providerLabel(llmConfig);
265396
+ console.log(import_picocolors8.default.green(`✓ LLM ${label} (${llmConfig.model})`));
265072
265397
  } else {
265073
265398
  await runLlmLogin(context);
265074
265399
  }
@@ -265088,7 +265413,8 @@ async function runFullLogin(context) {
265088
265413
  console.log(import_picocolors8.default.yellow("• Railway: not authenticated"));
265089
265414
  }
265090
265415
  if (finalLlmConfigured && finalLlmConfig) {
265091
- console.log(import_picocolors8.default.green(`✓ LLM: ${PROVIDER_LABELS[finalLlmConfig.provider]} (${finalLlmConfig.model})`));
265416
+ const label = providerLabel(finalLlmConfig);
265417
+ console.log(import_picocolors8.default.green(`✓ LLM: ${label} (${finalLlmConfig.model})`));
265092
265418
  } else {
265093
265419
  console.log(import_picocolors8.default.yellow("• LLM: not configured"));
265094
265420
  }
@@ -265096,7 +265422,7 @@ async function runFullLogin(context) {
265096
265422
  async function runLogout(context) {
265097
265423
  if (!context.yes) {
265098
265424
  const shouldLogout = await ye({
265099
- message: "This will log out of Railway and remove your stored LLM API key. Continue?",
265425
+ message: "This will log out of Railway and remove your stored LLM credentials. Continue?",
265100
265426
  initialValue: false
265101
265427
  });
265102
265428
  if (pD(shouldLogout)) {
@@ -265114,7 +265440,7 @@ async function runLogout(context) {
265114
265440
  } else {
265115
265441
  console.log(import_picocolors8.default.yellow("• Railway logout skipped or not authenticated"));
265116
265442
  }
265117
- console.log(import_picocolors8.default.green("✓ LLM key removed"));
265443
+ console.log(import_picocolors8.default.green("✓ LLM credentials removed"));
265118
265444
  }
265119
265445
  async function loginCommand(context, args, commandKey = "login") {
265120
265446
  if (commandKey === "logout") {
@@ -265143,8 +265469,8 @@ async function loginCommand(context, args, commandKey = "login") {
265143
265469
  }
265144
265470
 
265145
265471
  // src/commands/versions.ts
265146
- import { existsSync as existsSync19, readFileSync as readFileSync13, writeFileSync as writeFileSync14 } from "node:fs";
265147
- import { join as join22 } from "node:path";
265472
+ import { existsSync as existsSync20, readFileSync as readFileSync14, writeFileSync as writeFileSync14 } from "node:fs";
265473
+ import { join as join23 } from "node:path";
265148
265474
  var import_picocolors9 = __toESM(require_picocolors(), 1);
265149
265475
  var TABLE_HEADER_INTEGRATION = "Integration";
265150
265476
  var TABLE_HEADER_VERSION = "Version";
@@ -265187,13 +265513,13 @@ function loadVersionsContext(logger) {
265187
265513
  logger.cmd("/versions", "FAILED", "not a workspace");
265188
265514
  return null;
265189
265515
  }
265190
- const versionsPath = join22(workspaceDir, "versions.md");
265191
- if (!existsSync19(versionsPath)) {
265516
+ const versionsPath = join23(workspaceDir, "versions.md");
265517
+ if (!existsSync20(versionsPath)) {
265192
265518
  error("versions.md not found in workspace root.");
265193
265519
  logger.cmd("/versions", "FAILED", "versions.md missing");
265194
265520
  return null;
265195
265521
  }
265196
- const versionsMarkdown = readFileSync13(versionsPath, "utf-8");
265522
+ const versionsMarkdown = readFileSync14(versionsPath, "utf-8");
265197
265523
  const result = parseVersionsTable(versionsMarkdown);
265198
265524
  for (const parseError of result.errors) {
265199
265525
  warn(parseError);
@@ -265621,7 +265947,7 @@ async function helpCommand(_context, _args) {
265621
265947
  // package.json
265622
265948
  var package_default = {
265623
265949
  name: "openkitt",
265624
- version: "0.3.2",
265950
+ version: "0.3.4",
265625
265951
  description: "AI-powered monorepo scaffolding CLI",
265626
265952
  keywords: [
265627
265953
  "cli",
@@ -265693,8 +266019,8 @@ var STATE_CHANGING_COMMANDS = new Set([
265693
266019
  "domain",
265694
266020
  "publish"
265695
266021
  ]);
265696
- var KITT_DIR3 = join23(homedir5(), ".kitt");
265697
- var UPDATE_CHECK_FILE = join23(KITT_DIR3, "update-check.json");
266022
+ var KITT_DIR3 = join24(homedir6(), ".kitt");
266023
+ var UPDATE_CHECK_FILE = join24(KITT_DIR3, "update-check.json");
265698
266024
  var commandRegistry = {
265699
266025
  init: { handler: initCommand },
265700
266026
  create: { handler: createCommand2 },
@@ -265774,11 +266100,11 @@ function isVersionNewer(latest, current) {
265774
266100
  return false;
265775
266101
  }
265776
266102
  function hasFreshUpdateCache(now) {
265777
- if (!existsSync20(UPDATE_CHECK_FILE)) {
266103
+ if (!existsSync21(UPDATE_CHECK_FILE)) {
265778
266104
  return false;
265779
266105
  }
265780
266106
  try {
265781
- const rawCache = readFileSync14(UPDATE_CHECK_FILE, "utf-8").trim();
266107
+ const rawCache = readFileSync15(UPDATE_CHECK_FILE, "utf-8").trim();
265782
266108
  if (rawCache.length === 0) {
265783
266109
  return false;
265784
266110
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openkitt",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "AI-powered monorepo scaffolding CLI",
5
5
  "keywords": [
6
6
  "cli",