@trops/dash-core 0.1.611 → 0.1.613

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/index.esm.js CHANGED
@@ -46033,6 +46033,55 @@ var NewProviderPicker = function NewProviderPicker(_ref2) {
46033
46033
  });
46034
46034
  };
46035
46035
 
46036
+ /**
46037
+ * providerType.js
46038
+ *
46039
+ * Helpers for assigning a stable, UNIQUE `type` to a provider.
46040
+ *
46041
+ * A provider's identity in the widget system is its `type` string (widgets
46042
+ * declare `requiredProviders: [{ type }]`, runtime resolves by type). Catalog
46043
+ * providers each carry a unique type (`slack`, `github`, …). Hand-rolled
46044
+ * custom MCP servers historically all shared `type: "custom"`, which conflates
46045
+ * multiple customs at binding/runtime. These helpers give each NEW custom
46046
+ * provider a unique slug derived from its name so it behaves like a catalog
46047
+ * provider.
46048
+ *
46049
+ * Forward-only: existing providers are never renamed here (that would break
46050
+ * widgets already built against their type). Only the create path uses this.
46051
+ */
46052
+
46053
+ /**
46054
+ * Convert a provider name into a kebab-case type slug.
46055
+ * "My Granola Server" → "my-granola-server". Falls back to a sane default
46056
+ * when the name has no slug-able characters.
46057
+ */
46058
+ function slugifyProviderType(name) {
46059
+ var slug = String(name || "").toLowerCase().trim().replace(/[^\w\s-]/g, "") // drop punctuation/symbols
46060
+ .replace(/[\s_]+/g, "-") // whitespace/underscores → hyphen
46061
+ .replace(/-+/g, "-") // collapse repeats
46062
+ .replace(/^-+|-+$/g, ""); // trim hyphens
46063
+ return slug || "custom-provider";
46064
+ }
46065
+
46066
+ /**
46067
+ * Produce a slug type unique against the already-configured provider types.
46068
+ * Appends `-2`, `-3`, … on collision so two providers can never share a type.
46069
+ *
46070
+ * @param {string} name provider instance name
46071
+ * @param {Iterable<string>} existingTypes types already in use (e.g.
46072
+ * Object.values(providers).map(p => p.type))
46073
+ * @returns {string}
46074
+ */
46075
+ function uniqueProviderType(name) {
46076
+ var existingTypes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
46077
+ var base = slugifyProviderType(name);
46078
+ var taken = new Set(existingTypes || []);
46079
+ if (!taken.has(base)) return base;
46080
+ var i = 2;
46081
+ while (taken.has("".concat(base, "-").concat(i))) i++;
46082
+ return "".concat(base, "-").concat(i);
46083
+ }
46084
+
46036
46085
  var ProvidersSection = function ProvidersSection(_ref) {
46037
46086
  var _ref$dashApi = _ref.dashApi,
46038
46087
  dashApi = _ref$dashApi === void 0 ? null : _ref$dashApi,
@@ -46257,8 +46306,21 @@ var ProvidersSection = function ProvidersSection(_ref) {
46257
46306
  function handleMcpSave(providerName, providerType, mcpCredentials, mcpConfig) {
46258
46307
  var allowedTools = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
46259
46308
  if (!dashApi || !appId) return;
46309
+ // Forward-only unique types: a hand-rolled custom MCP server arrives with
46310
+ // the generic default type "custom", which conflates multiple customs at
46311
+ // widget-binding/runtime time. Give each NEW one a unique slug so it has a
46312
+ // distinct identity like catalog providers. Catalog installs pass a real
46313
+ // id (not "custom") and are untouched; the edit path preserves type, so no
46314
+ // existing provider is ever renamed.
46315
+ var resolvedType = providerType;
46316
+ if (resolvedType === "custom") {
46317
+ var existingTypes = Object.values(providers || {}).map(function (p) {
46318
+ return p.type;
46319
+ });
46320
+ resolvedType = uniqueProviderType(providerName, existingTypes);
46321
+ }
46260
46322
  dashApi.saveProvider(appId, providerName, {
46261
- providerType: providerType,
46323
+ providerType: resolvedType,
46262
46324
  credentials: mcpCredentials,
46263
46325
  providerClass: "mcp",
46264
46326
  mcpConfig: mcpConfig,
@@ -52178,18 +52240,35 @@ var McpServerSection = function McpServerSection() {
52178
52240
  });
52179
52241
  };
52180
52242
 
52243
+ /**
52244
+ * modelMigration.js (renderer mirror)
52245
+ *
52246
+ * Mirrors the retired-model map + default from the main-process provider
52247
+ * registry (electron/llm/modelProviders.js) so the Settings UI can detect a
52248
+ * stale saved selection, auto-migrate it, and show a notice — without an IPC
52249
+ * round-trip. KEEP IN SYNC with modelProviders.js (anthropic provider).
52250
+ */
52251
+ var DEFAULT_MODEL = "claude-opus-4-8";
52252
+ var RETIRED_MODEL_MAP = {
52253
+ "claude-sonnet-4-20250514": "claude-sonnet-4-6",
52254
+ "claude-opus-4-20250514": "claude-opus-4-8",
52255
+ "claude-opus-4-1-20250805": "claude-opus-4-8",
52256
+ "claude-3-7-sonnet-20250219": "claude-sonnet-4-6",
52257
+ "claude-3-5-sonnet-20241022": "claude-sonnet-4-6",
52258
+ "claude-3-5-sonnet-20240620": "claude-sonnet-4-6",
52259
+ "claude-3-5-haiku-20241022": "claude-haiku-4-5",
52260
+ "claude-3-opus-20240229": "claude-opus-4-8"
52261
+ };
52262
+ function isRetiredModel(id) {
52263
+ return !!RETIRED_MODEL_MAP[id];
52264
+ }
52265
+ function migrateModelId(id) {
52266
+ if (!id) return DEFAULT_MODEL;
52267
+ return RETIRED_MODEL_MAP[id] || id;
52268
+ }
52269
+
52181
52270
  function ownKeys$m(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
52182
52271
  function _objectSpread$m(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$m(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$m(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
52183
- var MODELS = [{
52184
- value: "claude-sonnet-4-20250514",
52185
- label: "Claude Sonnet 4"
52186
- }, {
52187
- value: "claude-opus-4-20250514",
52188
- label: "Claude Opus 4"
52189
- }, {
52190
- value: "claude-haiku-4-5-20251001",
52191
- label: "Claude Haiku 4.5"
52192
- }];
52193
52272
  var BACKENDS = [{
52194
52273
  value: "claude-code",
52195
52274
  label: "Claude Code CLI"
@@ -52198,6 +52277,7 @@ var BACKENDS = [{
52198
52277
  label: "Anthropic API"
52199
52278
  }];
52200
52279
  var AiAssistantSection = function AiAssistantSection() {
52280
+ var _anthropicProvider$;
52201
52281
  var appContext = useContext(AppContext);
52202
52282
  var settings = (appContext === null || appContext === void 0 ? void 0 : appContext.settings) || {};
52203
52283
  var providers = (appContext === null || appContext === void 0 ? void 0 : appContext.providers) || {};
@@ -52215,7 +52295,7 @@ var AiAssistantSection = function AiAssistantSection() {
52215
52295
  _useState4 = _slicedToArray(_useState3, 2),
52216
52296
  preferredBackend = _useState4[0],
52217
52297
  setPreferredBackend = _useState4[1];
52218
- var _useState5 = useState(aiSettings.model || "claude-sonnet-4-20250514"),
52298
+ var _useState5 = useState(aiSettings.model || DEFAULT_MODEL),
52219
52299
  _useState6 = _slicedToArray(_useState5, 2),
52220
52300
  selectedModel = _useState6[0],
52221
52301
  setSelectedModel = _useState6[1];
@@ -52232,6 +52312,21 @@ var AiAssistantSection = function AiAssistantSection() {
52232
52312
  saveKeyStatus = _useState10[0],
52233
52313
  setSaveKeyStatus = _useState10[1];
52234
52314
 
52315
+ // Provider-pluggable model discovery (live fetch + curated fallback)
52316
+ var _useState11 = useState([]),
52317
+ _useState12 = _slicedToArray(_useState11, 2),
52318
+ availableModels = _useState12[0],
52319
+ setAvailableModels = _useState12[1];
52320
+ var _useState13 = useState(false),
52321
+ _useState14 = _slicedToArray(_useState13, 2),
52322
+ modelsLoading = _useState14[0],
52323
+ setModelsLoading = _useState14[1];
52324
+ // One-time notice when a retired saved model was auto-migrated
52325
+ var _useState15 = useState(null),
52326
+ _useState16 = _slicedToArray(_useState15, 2),
52327
+ migrationNotice = _useState16[0],
52328
+ setMigrationNotice = _useState16[1];
52329
+
52235
52330
  // Find existing Anthropic provider
52236
52331
  var anthropicProvider = Object.entries(providers).find(function (_ref) {
52237
52332
  var _ref2 = _slicedToArray(_ref, 2),
@@ -52240,6 +52335,9 @@ var AiAssistantSection = function AiAssistantSection() {
52240
52335
  });
52241
52336
  var hasAnthropicProvider = !!anthropicProvider;
52242
52337
  var anthropicProviderName = anthropicProvider === null || anthropicProvider === void 0 ? void 0 : anthropicProvider[0];
52338
+ // Decrypted key (when AppContext exposes it) enables the live Models API
52339
+ // fetch; without it, listModels returns the curated fallback list.
52340
+ var anthropicApiKey = (anthropicProvider === null || anthropicProvider === void 0 || (_anthropicProvider$ = anthropicProvider[1]) === null || _anthropicProvider$ === void 0 || (_anthropicProvider$ = _anthropicProvider$.credentials) === null || _anthropicProvider$ === void 0 ? void 0 : _anthropicProvider$.apiKey) || null;
52243
52341
 
52244
52342
  // Check CLI availability on mount
52245
52343
  useEffect(function () {
@@ -52261,6 +52359,28 @@ var AiAssistantSection = function AiAssistantSection() {
52261
52359
  }
52262
52360
  }, []);
52263
52361
 
52362
+ // Fetch selectable models for the current provider. With an API key the
52363
+ // main process does a live Anthropic Models API fetch; otherwise it returns
52364
+ // the curated fallback. Provider is "anthropic" for both current backends.
52365
+ var fetchModels = useCallback(function () {
52366
+ var _window$mainApi2;
52367
+ if (!((_window$mainApi2 = window.mainApi) !== null && _window$mainApi2 !== void 0 && (_window$mainApi2 = _window$mainApi2.llm) !== null && _window$mainApi2 !== void 0 && _window$mainApi2.listModels)) return;
52368
+ setModelsLoading(true);
52369
+ window.mainApi.llm.listModels("anthropic", anthropicApiKey).then(function (res) {
52370
+ return setAvailableModels(res && res.models || []);
52371
+ })["catch"](function () {
52372
+ return setAvailableModels([]);
52373
+ })["finally"](function () {
52374
+ return setModelsLoading(false);
52375
+ });
52376
+ }, [anthropicApiKey]);
52377
+
52378
+ // Load models on mount and whenever the available API key changes (so the
52379
+ // list upgrades from curated → live once a key is configured).
52380
+ useEffect(function () {
52381
+ fetchModels();
52382
+ }, [fetchModels]);
52383
+
52264
52384
  // Save AI settings to app settings
52265
52385
  var saveAiSettings = useCallback(function (updates) {
52266
52386
  if (appContext !== null && appContext !== void 0 && appContext.changeSettings) {
@@ -52271,6 +52391,24 @@ var AiAssistantSection = function AiAssistantSection() {
52271
52391
  }));
52272
52392
  }
52273
52393
  }, [appContext]);
52394
+
52395
+ // Auto-migrate a retired saved model to its current replacement (one-time,
52396
+ // on mount) so an old selection can't 404 the assistant. Shows a notice.
52397
+ useEffect(function () {
52398
+ var saved = aiSettings.model;
52399
+ if (saved && isRetiredModel(saved)) {
52400
+ var replacement = migrateModelId(saved);
52401
+ setSelectedModel(replacement);
52402
+ saveAiSettings({
52403
+ model: replacement
52404
+ });
52405
+ setMigrationNotice({
52406
+ from: saved,
52407
+ to: replacement
52408
+ });
52409
+ }
52410
+ // eslint-disable-next-line react-hooks/exhaustive-deps
52411
+ }, []);
52274
52412
  function handleBackendChange(e) {
52275
52413
  var value = e.target.value;
52276
52414
  setPreferredBackend(value);
@@ -52314,6 +52452,15 @@ var AiAssistantSection = function AiAssistantSection() {
52314
52452
  });
52315
52453
  }
52316
52454
 
52455
+ // Model dropdown options — keep the current selection selectable even if it
52456
+ // isn't in the fetched list (e.g. a pinned/custom id).
52457
+ var modelOptions = selectedModel && !availableModels.some(function (m) {
52458
+ return m.value === selectedModel;
52459
+ }) ? [{
52460
+ value: selectedModel,
52461
+ label: selectedModel
52462
+ }].concat(_toConsumableArray(availableModels)) : availableModels;
52463
+
52317
52464
  // Determine effective backend (what will actually be used)
52318
52465
  var effectiveBackend = preferredBackend === "claude-code" && cliStatus !== null && cliStatus !== void 0 && cliStatus.available ? "claude-code" : preferredBackend === "anthropic" && hasAnthropicProvider ? "anthropic" : cliStatus !== null && cliStatus !== void 0 && cliStatus.available ? "claude-code" : hasAnthropicProvider ? "anthropic" : null;
52319
52466
  return /*#__PURE__*/jsxs("div", {
@@ -52410,6 +52557,18 @@ var AiAssistantSection = function AiAssistantSection() {
52410
52557
  }, b.value);
52411
52558
  })
52412
52559
  })]
52560
+ }), migrationNotice && /*#__PURE__*/jsxs("div", {
52561
+ className: "flex items-start gap-2 rounded-lg bg-yellow-900/30 border border-yellow-700 px-3 py-2 text-xs text-yellow-300",
52562
+ children: [/*#__PURE__*/jsx(FontAwesomeIcon, {
52563
+ icon: "exclamation-triangle",
52564
+ className: "h-3.5 w-3.5 mt-0.5"
52565
+ }), /*#__PURE__*/jsxs("span", {
52566
+ children: ["Your previously selected model (", /*#__PURE__*/jsx("code", {
52567
+ children: migrationNotice.from
52568
+ }), ") is no longer available. Switched to ", /*#__PURE__*/jsx("code", {
52569
+ children: migrationNotice.to
52570
+ }), "."]
52571
+ })]
52413
52572
  }), /*#__PURE__*/jsxs("div", {
52414
52573
  className: "flex flex-row items-center justify-between py-1",
52415
52574
  children: [/*#__PURE__*/jsxs("div", {
@@ -52419,18 +52578,31 @@ var AiAssistantSection = function AiAssistantSection() {
52419
52578
  children: "Model"
52420
52579
  }), /*#__PURE__*/jsx("span", {
52421
52580
  className: "text-xs opacity-50",
52422
- children: "Claude model for AI Assistant conversations"
52581
+ children: "Model for AI Assistant conversations"
52582
+ })]
52583
+ }), /*#__PURE__*/jsxs("div", {
52584
+ className: "flex items-center gap-2",
52585
+ children: [/*#__PURE__*/jsx("select", {
52586
+ value: selectedModel,
52587
+ onChange: handleModelChange,
52588
+ className: "bg-white/10 border border-white/10 rounded-md px-3 py-1.5 text-sm text-gray-200 focus:outline-none focus:ring-1 focus:ring-blue-500",
52589
+ children: modelOptions.map(function (m) {
52590
+ return /*#__PURE__*/jsx("option", {
52591
+ value: m.value,
52592
+ children: m.label
52593
+ }, m.value);
52594
+ })
52595
+ }), /*#__PURE__*/jsx("button", {
52596
+ type: "button",
52597
+ onClick: fetchModels,
52598
+ disabled: modelsLoading,
52599
+ title: "Refresh available models",
52600
+ className: "text-gray-400 hover:text-gray-200 disabled:opacity-40 transition-colors p-1.5",
52601
+ children: /*#__PURE__*/jsx(FontAwesomeIcon, {
52602
+ icon: "rotate",
52603
+ className: "h-3.5 w-3.5 ".concat(modelsLoading ? "animate-spin" : "")
52604
+ })
52423
52605
  })]
52424
- }), /*#__PURE__*/jsx("select", {
52425
- value: selectedModel,
52426
- onChange: handleModelChange,
52427
- className: "bg-white/10 border border-white/10 rounded-md px-3 py-1.5 text-sm text-gray-200 focus:outline-none focus:ring-1 focus:ring-blue-500",
52428
- children: MODELS.map(function (m) {
52429
- return /*#__PURE__*/jsx("option", {
52430
- value: m.value,
52431
- children: m.label
52432
- }, m.value);
52433
- })
52434
52606
  })]
52435
52607
  })]
52436
52608
  }), !hasAnthropicProvider && /*#__PURE__*/jsxs("div", {