fss-link 1.5.0 → 1.5.2

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/bundle/fss-link.js +181 -93
  2. package/package.json +1 -1
@@ -22379,7 +22379,7 @@ async function createContentGeneratorConfig(config, authType) {
22379
22379
  async function createContentGenerator(config, gcConfig, sessionId2) {
22380
22380
  if (DEBUG_CONTENT)
22381
22381
  console.log(`\u{1F41B} DEBUG createContentGenerator: authType=${config.authType}, apiKey=${config.apiKey}, baseUrl=${config.baseUrl}`);
22382
- const version = "1.5.0";
22382
+ const version = "1.5.2";
22383
22383
  const userAgent = `FSS-Link/${version} (${process.platform}; ${process.arch})`;
22384
22384
  const baseHeaders = {
22385
22385
  "User-Agent": userAgent
@@ -50437,7 +50437,7 @@ Co-authored-by: ${gitCoAuthorSettings.name} <${gitCoAuthorSettings.email}>`;
50437
50437
  description: "(OPTIONAL) Directory to run the command in, if not the project root directory. Must be relative to the project root directory and must already exist."
50438
50438
  }
50439
50439
  },
50440
- required: ["command", "is_background"]
50440
+ required: ["command"]
50441
50441
  },
50442
50442
  false,
50443
50443
  // output is not markdown
@@ -50456,6 +50456,9 @@ Co-authored-by: ${gitCoAuthorSettings.name} <${gitCoAuthorSettings.email}>`;
50456
50456
  // Override build method to preprocess parameters before validation
50457
50457
  build(params) {
50458
50458
  const processedParams = { ...params };
50459
+ if (processedParams.is_background === void 0 || processedParams.is_background === null) {
50460
+ processedParams.is_background = false;
50461
+ }
50459
50462
  if (typeof processedParams.is_background === "string") {
50460
50463
  const boolStr = processedParams.is_background.toLowerCase();
50461
50464
  if (boolStr === "false") {
@@ -96359,7 +96362,7 @@ async function getPackageJson() {
96359
96362
  // packages/cli/src/utils/version.ts
96360
96363
  async function getCliVersion() {
96361
96364
  const pkgJson = await getPackageJson();
96362
- return "1.5.0";
96365
+ return "1.5.2";
96363
96366
  }
96364
96367
 
96365
96368
  // packages/cli/src/ui/commands/aboutCommand.ts
@@ -96411,7 +96414,7 @@ import open4 from "open";
96411
96414
  import process11 from "node:process";
96412
96415
 
96413
96416
  // packages/cli/src/generated/git-commit.ts
96414
- var GIT_COMMIT_INFO = "6eb449b6";
96417
+ var GIT_COMMIT_INFO = "9c5ce749";
96415
96418
 
96416
96419
  // packages/cli/src/ui/commands/bugCommand.ts
96417
96420
  init_dist2();
@@ -120708,6 +120711,9 @@ import { useState as useState33, useEffect as useEffect32 } from "react";
120708
120711
  import { Box as Box19, Text as Text26, useInput as useInput4 } from "ink";
120709
120712
 
120710
120713
  // packages/cli/src/utils/modelFetcher.ts
120714
+ init_dist2();
120715
+ var modelCache = /* @__PURE__ */ new Map();
120716
+ var MODEL_CACHE_TTL = 5 * 60 * 1e3;
120711
120717
  async function fetchModelsFromCustomEndpoint(baseUrl, providerType, apiKey) {
120712
120718
  const normalizedBase = baseUrl.replace(/\/+$/, "");
120713
120719
  const url2 = `${normalizedBase}/models`;
@@ -120779,6 +120785,39 @@ async function fetchModelsFromCustomEndpoint(baseUrl, providerType, apiKey) {
120779
120785
  throw new Error(`Connection failed: ${String(error)}`);
120780
120786
  }
120781
120787
  }
120788
+ async function fetchModelsFromProvider(provider, endpoint, apiKey, forceRefresh = false) {
120789
+ const cacheKey = `${provider}:${endpoint || "default"}`;
120790
+ if (!forceRefresh) {
120791
+ const cached = modelCache.get(cacheKey);
120792
+ if (cached && Date.now() - cached.timestamp < MODEL_CACHE_TTL) {
120793
+ console.error(`[DEBUG] Using cached models for ${cacheKey}`);
120794
+ return cached.models;
120795
+ }
120796
+ }
120797
+ let fetchUrl;
120798
+ if (endpoint) {
120799
+ fetchUrl = endpoint;
120800
+ } else {
120801
+ switch (provider) {
120802
+ case AuthType.OLLAMA:
120803
+ fetchUrl = "http://localhost:11434";
120804
+ break;
120805
+ case AuthType.LM_STUDIO:
120806
+ fetchUrl = "http://localhost:1234/v1";
120807
+ break;
120808
+ default:
120809
+ throw new Error(`No default endpoint for provider: ${provider}`);
120810
+ }
120811
+ }
120812
+ console.error(`[DEBUG] Fetching models from ${provider} at ${fetchUrl}`);
120813
+ const models = await fetchModelsFromCustomEndpoint(fetchUrl, provider, apiKey);
120814
+ modelCache.set(cacheKey, {
120815
+ models,
120816
+ timestamp: Date.now()
120817
+ });
120818
+ console.error(`[DEBUG] Cached ${models.length} models for ${cacheKey}`);
120819
+ return models;
120820
+ }
120782
120821
 
120783
120822
  // packages/cli/src/ui/components/shared/PasteAwareTextInput.tsx
120784
120823
  import TextInput from "ink-text-input";
@@ -121791,85 +121830,104 @@ function EnhancedModelSelectionDialog({
121791
121830
  onBack,
121792
121831
  onCancel
121793
121832
  }) {
121794
- const [models, setModels] = useState37([]);
121833
+ const [allModels, setAllModels] = useState37([]);
121795
121834
  const [selectedIndex, setSelectedIndex] = useState37(0);
121835
+ const [scrollOffset, setScrollOffset] = useState37(0);
121796
121836
  const [loading, setLoading] = useState37(true);
121797
121837
  const [error, setError] = useState37(null);
121798
121838
  const [switching, setSwitching] = useState37(false);
121799
- const loadModels = async () => {
121800
- try {
121801
- const modelManager = getModelManager();
121802
- const allModels = await modelManager.getAllModels();
121803
- const filtered = allModels.filter((model) => {
121804
- if (model.authType !== providerType) return false;
121805
- const modelEndpoint = model.endpointUrl || void 0;
121806
- if (endpointUrl && modelEndpoint !== endpointUrl) return false;
121807
- if (!endpointUrl && modelEndpoint) return false;
121808
- return true;
121809
- });
121810
- if (filtered.length === 0) {
121811
- setError(`No models found for ${endpointLabel}`);
121839
+ const VISIBLE_MODELS = 15;
121840
+ useEffect35(() => {
121841
+ const loadAndFetchModels = async () => {
121842
+ setLoading(true);
121843
+ setError(null);
121844
+ try {
121845
+ const modelManager = getModelManager();
121846
+ const allDbModels = await modelManager.getAllModels();
121847
+ const existing = allDbModels.filter(
121848
+ (m) => m.authType === providerType && (!endpointUrl || m.endpointUrl === endpointUrl)
121849
+ );
121850
+ const activeModel = allDbModels.find((m) => m.isActive);
121851
+ const activeModelId = activeModel?.modelName;
121852
+ console.error(`[DEBUG] Fetching models for ${providerType} from ${endpointUrl || "default"}`);
121853
+ const fetched = await fetchModelsFromProvider(providerType, endpointUrl);
121854
+ const existingModelNames = new Set(existing.map((m) => m.modelName));
121855
+ const merged = fetched.map((fm) => ({
121856
+ ...fm,
121857
+ isNew: !existingModelNames.has(fm.id),
121858
+ isExisting: existingModelNames.has(fm.id),
121859
+ isActive: fm.id === activeModelId
121860
+ }));
121861
+ merged.sort((a, b) => {
121862
+ if (a.isActive && !b.isActive) return -1;
121863
+ if (!a.isActive && b.isActive) return 1;
121864
+ if (a.isExisting && !b.isExisting) return -1;
121865
+ if (!a.isExisting && b.isExisting) return 1;
121866
+ return 0;
121867
+ });
121868
+ setAllModels(merged);
121869
+ setLoading(false);
121870
+ } catch (err) {
121871
+ setError(err instanceof Error ? err.message : "Failed to fetch models");
121812
121872
  setLoading(false);
121813
- return;
121814
- }
121815
- filtered.sort((a, b) => {
121816
- if (a.isFavorite && !b.isFavorite) return -1;
121817
- if (!a.isFavorite && b.isFavorite) return 1;
121818
- if (a.isActive && !b.isActive) return -1;
121819
- if (!a.isActive && b.isActive) return 1;
121820
- return a.modelName.localeCompare(b.modelName);
121821
- });
121822
- const activeIndex = filtered.findIndex((m) => m.isActive);
121823
- if (activeIndex !== -1) {
121824
- setSelectedIndex(activeIndex);
121825
121873
  }
121826
- setModels(filtered);
121827
- setLoading(false);
121828
- } catch (err) {
121829
- setError(err instanceof Error ? err.message : "Failed to load models");
121830
- setLoading(false);
121831
- }
121832
- };
121833
- useEffect35(() => {
121834
- loadModels();
121874
+ };
121875
+ loadAndFetchModels();
121835
121876
  }, [providerType, endpointUrl]);
121836
121877
  useInput6((input, key) => {
121837
121878
  if (loading || switching) return;
121838
- if (key.escape) {
121879
+ if (key.escape || input === "b" || input === "B") {
121839
121880
  onBack();
121840
121881
  return;
121841
121882
  }
121842
- if (input === "f" || input === "F") {
121843
- const selectedModel = models[selectedIndex];
121844
- if (selectedModel.id) {
121845
- const modelManager = getModelManager();
121846
- modelManager.toggleFavorite(selectedModel.id).then(() => {
121847
- loadModels();
121848
- }).catch((err) => {
121849
- console.error("Failed to toggle favorite:", err);
121850
- });
121883
+ if (key.upArrow && selectedIndex > 0) {
121884
+ const newIndex = selectedIndex - 1;
121885
+ setSelectedIndex(newIndex);
121886
+ if (newIndex < scrollOffset) {
121887
+ setScrollOffset(newIndex);
121851
121888
  }
121852
121889
  return;
121853
121890
  }
121854
- if (input.includes("\n") || input.includes("\r")) {
121855
- if (models.length > 0) {
121856
- const selectedModel = models[selectedIndex];
121857
- setSwitching(true);
121858
- const modelManager = getModelManager();
121859
- modelManager.switchToModel(selectedModel.id).then(() => {
121860
- onComplete();
121861
- }).catch((err) => {
121862
- setError(err instanceof Error ? err.message : "Failed to switch model");
121863
- setSwitching(false);
121864
- });
121891
+ if (key.downArrow && selectedIndex < allModels.length - 1) {
121892
+ const newIndex = selectedIndex + 1;
121893
+ setSelectedIndex(newIndex);
121894
+ if (newIndex >= scrollOffset + VISIBLE_MODELS) {
121895
+ setScrollOffset(newIndex - VISIBLE_MODELS + 1);
121865
121896
  }
121866
121897
  return;
121867
121898
  }
121868
- if (key.upArrow && selectedIndex > 0) {
121869
- setSelectedIndex(selectedIndex - 1);
121870
- }
121871
- if (key.downArrow && selectedIndex < models.length - 1) {
121872
- setSelectedIndex(selectedIndex + 1);
121899
+ if (key.return && allModels.length > 0) {
121900
+ const selectedModel = allModels[selectedIndex];
121901
+ setSwitching(true);
121902
+ const modelManager = getModelManager();
121903
+ (async () => {
121904
+ try {
121905
+ if (selectedModel.isNew) {
121906
+ console.error(`[DEBUG] Auto-adding new model: ${selectedModel.id}`);
121907
+ await modelManager.configureModel(
121908
+ providerType,
121909
+ selectedModel.id,
121910
+ endpointUrl,
121911
+ void 0,
121912
+ // No API key needed for already configured endpoints
121913
+ selectedModel.id
121914
+ // Use model ID as display name
121915
+ );
121916
+ }
121917
+ const allDbModels = await modelManager.getAllModels();
121918
+ const dbModel = allDbModels.find((m) => m.modelName === selectedModel.id);
121919
+ if (dbModel?.id) {
121920
+ await modelManager.switchToModel(dbModel.id);
121921
+ onComplete();
121922
+ } else {
121923
+ throw new Error("Failed to find model after adding");
121924
+ }
121925
+ } catch (err) {
121926
+ setError(err instanceof Error ? err.message : "Failed to switch model");
121927
+ setSwitching(false);
121928
+ }
121929
+ })();
121930
+ return;
121873
121931
  }
121874
121932
  });
121875
121933
  if (loading) {
@@ -121882,11 +121940,8 @@ function EnhancedModelSelectionDialog({
121882
121940
  padding: 1,
121883
121941
  width: "100%",
121884
121942
  children: [
121885
- /* @__PURE__ */ jsxs26(Text30, { bold: true, color: Colors.AccentBlue, children: [
121886
- "Model Selection - ",
121887
- endpointLabel
121888
- ] }),
121889
- /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx29(Text30, { children: "Loading models..." }) })
121943
+ /* @__PURE__ */ jsx29(Text30, { bold: true, color: Colors.AccentBlue, children: "Model Selection" }),
121944
+ /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx29(Text30, { children: "Loading configured models..." }) })
121890
121945
  ]
121891
121946
  }
121892
121947
  );
@@ -121904,7 +121959,7 @@ function EnhancedModelSelectionDialog({
121904
121959
  /* @__PURE__ */ jsx29(Text30, { bold: true, color: Colors.AccentBlue, children: "Switching Model" }),
121905
121960
  /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsxs26(Text30, { children: [
121906
121961
  "Activating ",
121907
- models[selectedIndex].modelName,
121962
+ allModels[selectedIndex]?.id,
121908
121963
  "..."
121909
121964
  ] }) })
121910
121965
  ]
@@ -121923,11 +121978,14 @@ function EnhancedModelSelectionDialog({
121923
121978
  children: [
121924
121979
  /* @__PURE__ */ jsx29(Text30, { bold: true, color: Colors.AccentRed, children: "Model Selection Error" }),
121925
121980
  /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx29(Text30, { children: error }) }),
121926
- /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx29(Text30, { color: Colors.Gray, children: "Press Esc to go back" }) })
121981
+ /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx29(Text30, { color: Colors.Gray, children: "Press Esc to cancel" }) })
121927
121982
  ]
121928
121983
  }
121929
121984
  );
121930
121985
  }
121986
+ const visibleModels = allModels.slice(scrollOffset, scrollOffset + VISIBLE_MODELS);
121987
+ const modelsAbove = scrollOffset;
121988
+ const modelsBelow = Math.max(0, allModels.length - (scrollOffset + VISIBLE_MODELS));
121931
121989
  return /* @__PURE__ */ jsxs26(
121932
121990
  Box23,
121933
121991
  {
@@ -121939,15 +121997,35 @@ function EnhancedModelSelectionDialog({
121939
121997
  children: [
121940
121998
  /* @__PURE__ */ jsxs26(Text30, { bold: true, color: Colors.AccentBlue, children: [
121941
121999
  "Model Selection - ",
121942
- endpointLabel
122000
+ providerType
121943
122001
  ] }),
121944
- /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx29(Text30, { children: "Choose a model:" }) }),
121945
- /* @__PURE__ */ jsx29(Box23, { marginTop: 1, flexDirection: "column", children: models.map((model, index) => {
121946
- const isSelected = index === selectedIndex;
122002
+ endpointLabel && /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsxs26(Text30, { color: Colors.Gray, children: [
122003
+ "Endpoint: ",
122004
+ endpointLabel
122005
+ ] }) }),
122006
+ /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsxs26(Text30, { children: [
122007
+ "Select a model (",
122008
+ allModels.length,
122009
+ " available):"
122010
+ ] }) }),
122011
+ modelsAbove > 0 && /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsxs26(Text30, { color: Colors.Gray, children: [
122012
+ "\u2191 ",
122013
+ modelsAbove,
122014
+ " more above"
122015
+ ] }) }),
122016
+ /* @__PURE__ */ jsx29(Box23, { marginTop: 1, flexDirection: "column", children: visibleModels.map((model, visibleIndex) => {
122017
+ const actualIndex = scrollOffset + visibleIndex;
122018
+ const isSelected = actualIndex === selectedIndex;
121947
122019
  const prefix = isSelected ? ">" : " ";
121948
- const active = model.isActive ? "\u25CF " : "";
121949
- const favorite = model.isFavorite ? "\u2605 " : "";
121950
- const label = `${prefix} ${active}${favorite}${model.modelName}`;
122020
+ let statusIcon = "";
122021
+ if (model.isActive) {
122022
+ statusIcon = "\u25CF ";
122023
+ } else if (model.isNew) {
122024
+ statusIcon = "\u{1F195} ";
122025
+ } else if (model.isExisting) {
122026
+ statusIcon = "\u2713 ";
122027
+ }
122028
+ const label = `${prefix} ${statusIcon}${model.id}`;
121951
122029
  return /* @__PURE__ */ jsx29(
121952
122030
  Text30,
121953
122031
  {
@@ -121955,15 +122033,16 @@ function EnhancedModelSelectionDialog({
121955
122033
  bold: isSelected,
121956
122034
  children: label
121957
122035
  },
121958
- model.id || index
122036
+ model.id
121959
122037
  );
121960
122038
  }) }),
121961
- /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx29(Text30, { color: Colors.Gray, children: "\u25CF = Active \u2605 = Favorite" }) }),
121962
- /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx29(Text30, { color: Colors.Gray, children: "Enter to switch, F to toggle favorite, Esc to go back" }) }),
121963
- /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsxs26(Text30, { color: Colors.Gray, children: [
121964
- "Endpoint: ",
121965
- endpointUrl || "default"
121966
- ] }) })
122039
+ modelsBelow > 0 && /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsxs26(Text30, { color: Colors.Gray, children: [
122040
+ "\u2193 ",
122041
+ modelsBelow,
122042
+ " more below"
122043
+ ] }) }),
122044
+ /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx29(Text30, { color: Colors.Gray, children: "\u2191/\u2193 to navigate, Enter to select, Esc/b to go back" }) }),
122045
+ /* @__PURE__ */ jsx29(Box23, { marginTop: 1, children: /* @__PURE__ */ jsx29(Text30, { color: Colors.Gray, children: "\u{1F195} = new model, \u2713 = existing, \u25CF = currently active" }) })
121967
122046
  ]
121968
122047
  }
121969
122048
  );
@@ -123668,14 +123747,18 @@ async function loadCliConfig(settings, extensions, sessionId2, argv, cwd3 = proc
123668
123747
  const { SearchEngineConfigProvider: SearchEngineConfigProvider2 } = await Promise.resolve().then(() => (init_SearchEngineConfigProvider(), SearchEngineConfigProvider_exports));
123669
123748
  const configProvider = SearchEngineConfigProvider2.getInstance();
123670
123749
  const config2 = await configProvider.loadConfiguration();
123671
- console.log("[API KEY DEBUG] Search engine config loaded at startup:", {
123672
- hasBrave: !!config2.braveApiKey,
123673
- hasTavily: !!config2.tavilyApiKey,
123674
- braveEnvSet: !!process16.env["BRAVE_SEARCH_API_KEY"],
123675
- tavilyEnvSet: !!process16.env["TAVILY_API_KEY"]
123676
- });
123750
+ if (debugMode) {
123751
+ console.log("[API KEY DEBUG] Search engine config loaded at startup:", {
123752
+ hasBrave: !!config2.braveApiKey,
123753
+ hasTavily: !!config2.tavilyApiKey,
123754
+ braveEnvSet: !!process16.env["BRAVE_SEARCH_API_KEY"],
123755
+ tavilyEnvSet: !!process16.env["TAVILY_API_KEY"]
123756
+ });
123757
+ }
123677
123758
  } catch (error) {
123678
- console.error("[API KEY DEBUG] Failed to load search engine config at startup:", error);
123759
+ if (debugMode) {
123760
+ console.error("[API KEY DEBUG] Failed to load search engine config at startup:", error);
123761
+ }
123679
123762
  }
123680
123763
  const webScraperConfigLoader = () => {
123681
123764
  try {
@@ -131762,6 +131845,11 @@ main().catch((error) => {
131762
131845
  * Copyright 2025 FSS Coding
131763
131846
  * SPDX-License-Identifier: Apache-2.0
131764
131847
  */
131848
+ /**
131849
+ * @license
131850
+ * Copyright 2025 Google LLC
131851
+ * SPDX-License-Identifier: Apache-2.0
131852
+ */
131765
131853
  /*! Bundled license information:
131766
131854
 
131767
131855
  js-yaml/dist/js-yaml.mjs:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fss-link",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "engines": {
5
5
  "node": ">=20.0.0"
6
6
  },