cascade-ai 0.2.0 → 0.2.1
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/README.md +10 -3
- package/bin/cascade.js +3 -3
- package/completions/cascade.bash +23 -23
- package/completions/cascade.fish +20 -20
- package/completions/cascade.zsh +32 -32
- package/dist/cli.cjs +295 -96
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +295 -96
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +25 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +25 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/web/dist/assets/{index-qNI_hqt1.js → index-BdaS_Mbj.js} +69 -69
- package/web/dist/index.html +13 -13
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { GoogleGenAI, HarmBlockThreshold, HarmCategory } from '@google/genai';
|
|
|
4
4
|
import axios2 from 'axios';
|
|
5
5
|
import { render, useApp, useStdout, useInput, Box, Text } from 'ink';
|
|
6
6
|
import { Command } from 'commander';
|
|
7
|
-
import React5, { useState, useReducer, useRef, useCallback, useEffect } from 'react';
|
|
7
|
+
import React5, { useState, useReducer, useRef, useCallback, useEffect, useMemo } from 'react';
|
|
8
8
|
import chalk8 from 'chalk';
|
|
9
9
|
import dotenv from 'dotenv';
|
|
10
10
|
import fs7 from 'fs/promises';
|
|
@@ -26,9 +26,9 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
|
26
26
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
27
27
|
import { createContext, runInContext } from 'vm';
|
|
28
28
|
import Spinner from 'ink-spinner';
|
|
29
|
-
import SelectInput from 'ink-select-input';
|
|
30
29
|
import wrapAnsi from 'wrap-ansi';
|
|
31
30
|
import ora from 'ora';
|
|
31
|
+
import SelectInput from 'ink-select-input';
|
|
32
32
|
import { createServer } from 'http';
|
|
33
33
|
import { fileURLToPath } from 'url';
|
|
34
34
|
import express from 'express';
|
|
@@ -82,7 +82,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
82
82
|
var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SECRET_FILE, GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_RUNTIME_DB_FILE, DEFAULT_DASHBOARD_PORT, DEFAULT_CONTEXT_LIMIT, DEFAULT_AUTO_SUMMARIZE_AT, MODELS, T1_MODEL_PRIORITY, T2_MODEL_PRIORITY, T3_MODEL_PRIORITY, VISION_MODEL_PRIORITY, COMPLEXITY_T2_COUNT, THEME_NAMES, DEFAULT_THEME, OLLAMA_BASE_URL, LM_STUDIO_BASE_URL, AZURE_BASE_URL_TEMPLATE, TOOL_NAMES, DEFAULT_APPROVAL_REQUIRED;
|
|
83
83
|
var init_constants = __esm({
|
|
84
84
|
"src/constants.ts"() {
|
|
85
|
-
CASCADE_VERSION = "0.
|
|
85
|
+
CASCADE_VERSION = "0.2.0";
|
|
86
86
|
CASCADE_CONFIG_FILE = ".cascade/config.json";
|
|
87
87
|
CASCADE_DB_FILE = ".cascade/memory.db";
|
|
88
88
|
CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
|
|
@@ -2780,6 +2780,15 @@ var ModelSelector = class {
|
|
|
2780
2780
|
markProviderUnavailable(provider) {
|
|
2781
2781
|
this.availableProviders.delete(provider);
|
|
2782
2782
|
}
|
|
2783
|
+
/**
|
|
2784
|
+
* Re-add a provider to the available set after it has recovered (e.g. after
|
|
2785
|
+
* a failover timeout expires or a successful call confirms recovery). Only
|
|
2786
|
+
* re-enables providers that were originally configured — callers should
|
|
2787
|
+
* guard against enabling providers that were never configured.
|
|
2788
|
+
*/
|
|
2789
|
+
markProviderAvailable(provider) {
|
|
2790
|
+
this.availableProviders.add(provider);
|
|
2791
|
+
}
|
|
2783
2792
|
resolveDynamicModel(overrideModelId) {
|
|
2784
2793
|
let providerStr = null;
|
|
2785
2794
|
let actualId = overrideModelId;
|
|
@@ -2849,10 +2858,23 @@ var FailoverManager = class {
|
|
|
2849
2858
|
if (!failure) return true;
|
|
2850
2859
|
if (Date.now() - failure.failedAt >= failure.retryAfterMs) {
|
|
2851
2860
|
this.failures.delete(provider);
|
|
2861
|
+
this.selector.markProviderAvailable(provider);
|
|
2852
2862
|
return true;
|
|
2853
2863
|
}
|
|
2854
2864
|
return false;
|
|
2855
2865
|
}
|
|
2866
|
+
/**
|
|
2867
|
+
* Call after a successful generation to immediately re-enable a provider
|
|
2868
|
+
* that had previously been marked unavailable. This allows fast recovery
|
|
2869
|
+
* when a transient rate-limit clears before the backoff window expires,
|
|
2870
|
+
* preventing unnecessary routing to more expensive fallback models.
|
|
2871
|
+
*/
|
|
2872
|
+
recordSuccess(provider) {
|
|
2873
|
+
if (this.failures.has(provider)) {
|
|
2874
|
+
this.failures.delete(provider);
|
|
2875
|
+
this.selector.markProviderAvailable(provider);
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2856
2878
|
getFallbackModel(currentModel, tier) {
|
|
2857
2879
|
return this.selector.getNextFallback(currentModel.id, tier);
|
|
2858
2880
|
}
|
|
@@ -2869,6 +2891,7 @@ var FailoverManager = class {
|
|
|
2869
2891
|
}
|
|
2870
2892
|
clearFailure(provider) {
|
|
2871
2893
|
this.failures.delete(provider);
|
|
2894
|
+
this.selector.markProviderAvailable(provider);
|
|
2872
2895
|
}
|
|
2873
2896
|
};
|
|
2874
2897
|
|
|
@@ -3084,6 +3107,7 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
3084
3107
|
throw new Error(`Provider ${model.provider}:${model.id} returned an invalid generation result.`);
|
|
3085
3108
|
}
|
|
3086
3109
|
this.recordStats(tier, model, result.usage);
|
|
3110
|
+
this.failover.recordSuccess(model.provider);
|
|
3087
3111
|
return result;
|
|
3088
3112
|
} catch (err) {
|
|
3089
3113
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -7303,12 +7327,17 @@ var SlashCommandRegistry = class {
|
|
|
7303
7327
|
});
|
|
7304
7328
|
this.register({
|
|
7305
7329
|
command: "/model",
|
|
7330
|
+
description: "Pick a provider and model for a tier (interactive)",
|
|
7331
|
+
handler: (_args, ctx) => ({ output: ctx.onModelPicker(), handled: true })
|
|
7332
|
+
});
|
|
7333
|
+
this.register({
|
|
7334
|
+
command: "/model-info",
|
|
7306
7335
|
description: "Show active models per tier",
|
|
7307
7336
|
handler: (_args, ctx) => ({ output: ctx.onModelInfo(), handled: true })
|
|
7308
7337
|
});
|
|
7309
7338
|
this.register({
|
|
7310
7339
|
command: "/models",
|
|
7311
|
-
description: "
|
|
7340
|
+
description: "Browse available models by provider",
|
|
7312
7341
|
handler: (_args, ctx) => ({ output: ctx.onModelsInfo(), handled: true })
|
|
7313
7342
|
});
|
|
7314
7343
|
this.register({
|
|
@@ -7611,55 +7640,140 @@ function ApprovalPrompt({ request, theme, onDecision }) {
|
|
|
7611
7640
|
}
|
|
7612
7641
|
);
|
|
7613
7642
|
}
|
|
7614
|
-
var
|
|
7615
|
-
|
|
7616
|
-
|
|
7643
|
+
var TIERS = [
|
|
7644
|
+
{ id: "T1", label: "T1 \u2014 Administrator", hint: "complex reasoning \xB7 runs once per task" },
|
|
7645
|
+
{ id: "T2", label: "T2 \u2014 Manager", hint: "per-section planning \xB7 a few calls per task" },
|
|
7646
|
+
{ id: "T3", label: "T3 \u2014 Worker", hint: "high volume \xB7 many parallel runs" }
|
|
7647
|
+
];
|
|
7648
|
+
var ModelsDisplay = ({
|
|
7649
|
+
providers,
|
|
7650
|
+
modelsByProvider,
|
|
7651
|
+
onSelect,
|
|
7652
|
+
onClose
|
|
7653
|
+
}) => {
|
|
7654
|
+
const [step, setStep] = useState("PROVIDER");
|
|
7655
|
+
const [cursor, setCursor] = useState(0);
|
|
7656
|
+
const [picked, setPicked] = useState({});
|
|
7657
|
+
const providerItems = useMemo(() => {
|
|
7658
|
+
const base = [
|
|
7659
|
+
{ label: "\u25C7 Auto", sublabel: "let Cascade pick per tier \u2014 recommended", value: "auto" }
|
|
7660
|
+
];
|
|
7661
|
+
for (const p of providers) {
|
|
7662
|
+
const count = modelsByProvider.get(p)?.length ?? 0;
|
|
7663
|
+
base.push({ label: p, sublabel: `${count} model${count === 1 ? "" : "s"} discovered`, value: p });
|
|
7664
|
+
}
|
|
7665
|
+
return base;
|
|
7666
|
+
}, [providers, modelsByProvider]);
|
|
7667
|
+
const tierItems = useMemo(
|
|
7668
|
+
() => TIERS.map((t) => ({ label: t.label, sublabel: t.hint, value: t.id })),
|
|
7669
|
+
[]
|
|
7670
|
+
);
|
|
7671
|
+
const modelItems = useMemo(() => {
|
|
7672
|
+
if (!picked.provider || picked.provider === "auto") return [];
|
|
7673
|
+
const list = (modelsByProvider.get(picked.provider) ?? []).slice().sort((a, b) => a.id.localeCompare(b.id));
|
|
7674
|
+
const items = [
|
|
7675
|
+
{ label: "\u25C7 Auto", sublabel: "best available from this provider", value: "__auto__" }
|
|
7676
|
+
];
|
|
7677
|
+
for (const m of list) {
|
|
7678
|
+
const ctx = m.contextWindow >= 1e6 ? `${(m.contextWindow / 1e6).toFixed(1)}M ctx` : m.contextWindow >= 1e3 ? `${Math.round(m.contextWindow / 1e3)}K ctx` : `${m.contextWindow} ctx`;
|
|
7679
|
+
items.push({ label: m.name, sublabel: `${m.id} \xB7 ${ctx}`, value: m.id });
|
|
7680
|
+
}
|
|
7681
|
+
return items;
|
|
7682
|
+
}, [picked.provider, modelsByProvider]);
|
|
7683
|
+
const currentItems = step === "PROVIDER" ? providerItems : step === "TIER" ? tierItems : modelItems;
|
|
7684
|
+
useInput((input, key) => {
|
|
7617
7685
|
if (key.escape) {
|
|
7618
|
-
if (
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7686
|
+
if (step === "MODEL") {
|
|
7687
|
+
setStep("TIER");
|
|
7688
|
+
setCursor(0);
|
|
7689
|
+
return;
|
|
7690
|
+
}
|
|
7691
|
+
if (step === "TIER") {
|
|
7692
|
+
setStep("PROVIDER");
|
|
7693
|
+
setCursor(0);
|
|
7694
|
+
return;
|
|
7622
7695
|
}
|
|
7696
|
+
onClose();
|
|
7697
|
+
return;
|
|
7623
7698
|
}
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7699
|
+
if (key.upArrow || input === "k") {
|
|
7700
|
+
setCursor((c) => currentItems.length === 0 ? 0 : (c - 1 + currentItems.length) % currentItems.length);
|
|
7701
|
+
return;
|
|
7702
|
+
}
|
|
7703
|
+
if (key.downArrow || key.tab || input === "j") {
|
|
7704
|
+
setCursor((c) => currentItems.length === 0 ? 0 : (c + 1) % currentItems.length);
|
|
7705
|
+
return;
|
|
7706
|
+
}
|
|
7707
|
+
if (/^[1-9]$/.test(input)) {
|
|
7708
|
+
const idx = parseInt(input, 10) - 1;
|
|
7709
|
+
if (idx < currentItems.length) setCursor(idx);
|
|
7710
|
+
return;
|
|
7711
|
+
}
|
|
7712
|
+
if (key.return) {
|
|
7713
|
+
const selected = currentItems[cursor];
|
|
7714
|
+
if (!selected) return;
|
|
7715
|
+
if (step === "PROVIDER") {
|
|
7716
|
+
if (selected.value === "auto") {
|
|
7717
|
+
setPicked({ provider: "auto" });
|
|
7718
|
+
setStep("TIER");
|
|
7719
|
+
setCursor(0);
|
|
7720
|
+
return;
|
|
7641
7721
|
}
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
] }),
|
|
7654
|
-
modelItems.length === 0 ? /* @__PURE__ */ jsx(Text, { italic: true, color: "yellow", children: "No supported models discovered for this provider." }) : /* @__PURE__ */ jsx(
|
|
7655
|
-
SelectInput,
|
|
7656
|
-
{
|
|
7657
|
-
items: modelItems,
|
|
7658
|
-
limit: 10,
|
|
7659
|
-
onSelect: () => {
|
|
7722
|
+
setPicked({ provider: selected.value });
|
|
7723
|
+
setStep("TIER");
|
|
7724
|
+
setCursor(0);
|
|
7725
|
+
return;
|
|
7726
|
+
}
|
|
7727
|
+
if (step === "TIER") {
|
|
7728
|
+
const tier = selected.value;
|
|
7729
|
+
if (picked.provider === "auto") {
|
|
7730
|
+
onSelect?.({ kind: "auto", tier });
|
|
7731
|
+
onClose();
|
|
7732
|
+
return;
|
|
7660
7733
|
}
|
|
7734
|
+
setPicked((p) => ({ ...p, tier }));
|
|
7735
|
+
setStep("MODEL");
|
|
7736
|
+
setCursor(0);
|
|
7737
|
+
return;
|
|
7661
7738
|
}
|
|
7662
|
-
|
|
7739
|
+
if (!picked.tier || !picked.provider || picked.provider === "auto") {
|
|
7740
|
+
onClose();
|
|
7741
|
+
return;
|
|
7742
|
+
}
|
|
7743
|
+
if (selected.value === "__auto__") {
|
|
7744
|
+
onSelect?.({ kind: "auto", tier: picked.tier });
|
|
7745
|
+
} else {
|
|
7746
|
+
onSelect?.({
|
|
7747
|
+
kind: "pick",
|
|
7748
|
+
tier: picked.tier,
|
|
7749
|
+
provider: picked.provider,
|
|
7750
|
+
modelId: selected.value
|
|
7751
|
+
});
|
|
7752
|
+
}
|
|
7753
|
+
onClose();
|
|
7754
|
+
}
|
|
7755
|
+
});
|
|
7756
|
+
const title = step === "PROVIDER" ? "\u25C8 SELECT PROVIDER" : step === "TIER" ? `\u25C8 APPLY ${picked.provider === "auto" ? "AUTO" : String(picked.provider).toUpperCase()} TO WHICH TIER?` : `\u25C8 ${String(picked.provider).toUpperCase()} \u2192 SELECT MODEL FOR ${picked.tier}`;
|
|
7757
|
+
const breadcrumb = step === "PROVIDER" ? "Step 1 / 3" : step === "TIER" ? `Step 2 / 3 \xB7 provider: ${picked.provider}` : `Step 3 / 3 \xB7 ${picked.provider} \u2192 ${picked.tier}`;
|
|
7758
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
|
|
7759
|
+
/* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", children: [
|
|
7760
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: title }),
|
|
7761
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: "[Esc back \xB7 Enter select]" })
|
|
7762
|
+
] }),
|
|
7763
|
+
/* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
7764
|
+
breadcrumb,
|
|
7765
|
+
" \xB7 \u2191/\u2193 navigate \xB7 1\u20139 jump"
|
|
7766
|
+
] }) }),
|
|
7767
|
+
currentItems.length === 0 ? /* @__PURE__ */ jsx(Text, { italic: true, color: "yellow", children: "No items to show." }) : /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: currentItems.map((item, i) => {
|
|
7768
|
+
const focused = i === cursor;
|
|
7769
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
|
|
7770
|
+
/* @__PURE__ */ jsx(Text, { color: focused ? "green" : "gray", children: focused ? "\u276F " : " " }),
|
|
7771
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
7772
|
+
/* @__PURE__ */ jsx(Text, { color: focused ? "white" : "gray", bold: focused, children: item.label }),
|
|
7773
|
+
item.sublabel && /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: ` ${item.sublabel}` })
|
|
7774
|
+
] })
|
|
7775
|
+
] }, `${step}-${item.value}-${i}`);
|
|
7776
|
+
}) })
|
|
7663
7777
|
] });
|
|
7664
7778
|
};
|
|
7665
7779
|
function CostTracker({
|
|
@@ -7924,6 +8038,40 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName })
|
|
|
7924
8038
|
const persistMessage = useCallback((role, content, timestamp) => {
|
|
7925
8039
|
storeRef.current?.addMessage({ id: randomUUID(), sessionId: sessionIdRef.current, role, content, timestamp });
|
|
7926
8040
|
}, []);
|
|
8041
|
+
const applyModelPick = useCallback(async (sel) => {
|
|
8042
|
+
const tierKey = sel.tier.toLowerCase();
|
|
8043
|
+
const tierLabel = sel.tier;
|
|
8044
|
+
if (sel.kind === "auto") {
|
|
8045
|
+
delete config.models[tierKey];
|
|
8046
|
+
} else {
|
|
8047
|
+
config.models[tierKey] = sel.modelId;
|
|
8048
|
+
}
|
|
8049
|
+
try {
|
|
8050
|
+
const router = cascadeRef.current?.getRouter();
|
|
8051
|
+
if (router && sel.kind === "pick") {
|
|
8052
|
+
const candidate = (cachedModels.get(sel.provider) ?? []).find((m) => m.id === sel.modelId);
|
|
8053
|
+
if (candidate) router.overrideTierModel(tierLabel, candidate);
|
|
8054
|
+
}
|
|
8055
|
+
} catch {
|
|
8056
|
+
}
|
|
8057
|
+
const configPath = path17.join(workspacePath, ".cascade", "config.json");
|
|
8058
|
+
try {
|
|
8059
|
+
await fs7.mkdir(path17.dirname(configPath), { recursive: true });
|
|
8060
|
+
await fs7.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
8061
|
+
} catch (err) {
|
|
8062
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
8063
|
+
dispatch({
|
|
8064
|
+
type: "ADD_MESSAGE",
|
|
8065
|
+
message: { id: randomUUID(), role: "error", content: `Failed to persist model selection: ${msg}`, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
8066
|
+
});
|
|
8067
|
+
return;
|
|
8068
|
+
}
|
|
8069
|
+
const summary = sel.kind === "auto" ? `${tierLabel} \u2192 Auto (Cascade will pick best available).` : `${tierLabel} \u2192 ${sel.modelId} (provider: ${sel.provider}).`;
|
|
8070
|
+
dispatch({
|
|
8071
|
+
type: "ADD_MESSAGE",
|
|
8072
|
+
message: { id: randomUUID(), role: "system", content: `\u2714 Model updated \u2014 ${summary}`, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
8073
|
+
});
|
|
8074
|
+
}, [config, workspacePath, cachedModels]);
|
|
7927
8075
|
const globalStoreRef = useRef(null);
|
|
7928
8076
|
const persistRuntimeSession = useCallback((status, latestPrompt) => {
|
|
7929
8077
|
const runtimeSession = { sessionId: sessionIdRef.current, title: sessionTitle, workspacePath, status, startedAt: startedAtRef.current, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), latestPrompt, isGlobal: false };
|
|
@@ -8070,6 +8218,10 @@ ${msg.content}`).join("\n\n");
|
|
|
8070
8218
|
return `${t}: ${m?.name ?? "none"} (${m?.provider ?? "\u2014"})${configured ? ` | configured: ${configured}` : ""}`;
|
|
8071
8219
|
}).join("\n");
|
|
8072
8220
|
},
|
|
8221
|
+
onModelPicker: async () => {
|
|
8222
|
+
setIsShowingModels(true);
|
|
8223
|
+
return "Opening model picker \u2014 choose provider \u2192 tier \u2192 model (ESC to exit).";
|
|
8224
|
+
},
|
|
8073
8225
|
onModelsInfo: async () => {
|
|
8074
8226
|
setIsShowingModels(true);
|
|
8075
8227
|
return "Opening interactive models explorer... (ESC to exit)";
|
|
@@ -8352,6 +8504,16 @@ Use /identity <name|id> to switch.`;
|
|
|
8352
8504
|
}
|
|
8353
8505
|
}, [handleSlashCommand, persistMessage, state.messages, workspacePath, rebuildTree, recordNodeEvent, slashCompletions, slashIndex, state.isExecuting]);
|
|
8354
8506
|
useInput((_input, key) => {
|
|
8507
|
+
if (isShowingModels) {
|
|
8508
|
+
if (key.ctrl && _input === "c") {
|
|
8509
|
+
if (quitAttempted) {
|
|
8510
|
+
exit();
|
|
8511
|
+
return;
|
|
8512
|
+
}
|
|
8513
|
+
setQuitAttempted(true);
|
|
8514
|
+
}
|
|
8515
|
+
return;
|
|
8516
|
+
}
|
|
8355
8517
|
if (key.ctrl && _input === "c") {
|
|
8356
8518
|
if (quitAttempted) {
|
|
8357
8519
|
exit();
|
|
@@ -8520,7 +8682,17 @@ Use /identity <name|id> to switch.`;
|
|
|
8520
8682
|
] }) }),
|
|
8521
8683
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
|
|
8522
8684
|
state.messages.length === 0 && !state.isStreaming && /* @__PURE__ */ jsx(WelcomeBanner, { theme, config, workspacePath, sessionId: sessionIdRef.current }),
|
|
8523
|
-
isShowingModels && /* @__PURE__ */ jsx(
|
|
8685
|
+
isShowingModels && /* @__PURE__ */ jsx(
|
|
8686
|
+
ModelsDisplay,
|
|
8687
|
+
{
|
|
8688
|
+
providers: config.providers.map((p) => p.type),
|
|
8689
|
+
modelsByProvider: cachedModels,
|
|
8690
|
+
onSelect: (sel) => {
|
|
8691
|
+
void applyModelPick(sel);
|
|
8692
|
+
},
|
|
8693
|
+
onClose: () => setIsShowingModels(false)
|
|
8694
|
+
}
|
|
8695
|
+
)
|
|
8524
8696
|
] }),
|
|
8525
8697
|
/* @__PURE__ */ jsx(AgentTree, { root: state.agentTree, theme }),
|
|
8526
8698
|
state.showDetails && /* @__PURE__ */ jsx(TimelinePanel, { nodes: [...treeNodesRef.current.values()], theme, currentIndex: timelineIndex, onChangeIndex: setTimelineIndex }),
|
|
@@ -8572,7 +8744,7 @@ Use /identity <name|id> to switch.`;
|
|
|
8572
8744
|
/* @__PURE__ */ jsx(
|
|
8573
8745
|
SafeTextInput,
|
|
8574
8746
|
{
|
|
8575
|
-
focus: !state.approvalRequest,
|
|
8747
|
+
focus: !state.approvalRequest && !isShowingModels,
|
|
8576
8748
|
value: input,
|
|
8577
8749
|
manageMouseReporting: false,
|
|
8578
8750
|
onChange: (val) => {
|
|
@@ -8765,6 +8937,7 @@ var PROVIDER_LABELS = {
|
|
|
8765
8937
|
ollama: "Ollama (local)",
|
|
8766
8938
|
"openai-compatible": "OpenAI-Compatible"
|
|
8767
8939
|
};
|
|
8940
|
+
var providerOrder = ["anthropic", "openai", "gemini", "azure", "ollama", "openai-compatible"];
|
|
8768
8941
|
function buildInitialEntries(types) {
|
|
8769
8942
|
return [...types].map((type) => ({
|
|
8770
8943
|
id: randomUUID(),
|
|
@@ -8780,6 +8953,17 @@ function wizardReducer(state, action) {
|
|
|
8780
8953
|
else next.add(action.provider);
|
|
8781
8954
|
return { ...state, selectedTypes: next };
|
|
8782
8955
|
}
|
|
8956
|
+
case "TOGGLE_ALL": {
|
|
8957
|
+
const allSelected = state.selectedTypes.size === providerOrder.length;
|
|
8958
|
+
return { ...state, selectedTypes: allSelected ? /* @__PURE__ */ new Set() : new Set(providerOrder) };
|
|
8959
|
+
}
|
|
8960
|
+
case "INVERT_SELECTION": {
|
|
8961
|
+
const next = /* @__PURE__ */ new Set();
|
|
8962
|
+
providerOrder.forEach((p) => {
|
|
8963
|
+
if (!state.selectedTypes.has(p)) next.add(p);
|
|
8964
|
+
});
|
|
8965
|
+
return { ...state, selectedTypes: next };
|
|
8966
|
+
}
|
|
8783
8967
|
case "CONFIRM_PROVIDERS": {
|
|
8784
8968
|
const entries = buildInitialEntries(state.selectedTypes);
|
|
8785
8969
|
return { ...state, entries, currentEntryIdx: 0, step: "API_KEYS" };
|
|
@@ -8867,7 +9051,6 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
8867
9051
|
tierSelectFocus: "T1",
|
|
8868
9052
|
error: null
|
|
8869
9053
|
});
|
|
8870
|
-
const providerOrder = ["anthropic", "openai", "gemini", "azure", "ollama", "openai-compatible"];
|
|
8871
9054
|
const [providerCursor, setProviderCursor] = useState(0);
|
|
8872
9055
|
const [fieldBuffer, setFieldBuffer] = useState("");
|
|
8873
9056
|
const [fieldStage, setFieldStage] = useState("apiKey");
|
|
@@ -8960,6 +9143,8 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
8960
9143
|
if (key.upArrow) setProviderCursor((p) => Math.max(0, p - 1));
|
|
8961
9144
|
if (key.downArrow) setProviderCursor((p) => Math.min(providerOrder.length - 1, p + 1));
|
|
8962
9145
|
if (_input === " ") dispatch({ type: "TOGGLE_PROVIDER", provider: providerOrder[providerCursor] });
|
|
9146
|
+
if (_input === "a") dispatch({ type: "TOGGLE_ALL" });
|
|
9147
|
+
if (_input === "i") dispatch({ type: "INVERT_SELECTION" });
|
|
8963
9148
|
if (key.return) {
|
|
8964
9149
|
if (state.selectedTypes.size === 0) return;
|
|
8965
9150
|
dispatch({ type: "CONFIRM_PROVIDERS" });
|
|
@@ -9021,21 +9206,23 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9021
9206
|
}, [currentEntry, fieldStage]);
|
|
9022
9207
|
if (state.step === "PROVIDER_SELECT") {
|
|
9023
9208
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9024
|
-
/* @__PURE__ */
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9209
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
9210
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "? " }),
|
|
9211
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Which providers do you want to configure?" })
|
|
9212
|
+
] }),
|
|
9213
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", children: providerOrder.map((p, i) => {
|
|
9028
9214
|
const selected = state.selectedTypes.has(p);
|
|
9029
9215
|
const focused = i === providerCursor;
|
|
9030
9216
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
9031
|
-
/* @__PURE__ */ jsx(Text, { color: focused ? "
|
|
9032
|
-
/* @__PURE__ */ jsx(Text, { color: selected ? "green" : "white", children: selected ? "\u25C9 " : "\
|
|
9033
|
-
/* @__PURE__ */ jsx(Text, { color: focused ? "white" : "gray", children: PROVIDER_LABELS[p] }),
|
|
9217
|
+
/* @__PURE__ */ jsx(Text, { color: focused ? "magenta" : "white", children: focused ? "\u276F " : " " }),
|
|
9218
|
+
/* @__PURE__ */ jsx(Text, { color: selected ? "green" : "white", children: selected ? "\u25C9 " : "\u25EF " }),
|
|
9219
|
+
/* @__PURE__ */ jsx(Text, { color: focused ? "magenta" : selected ? "white" : "gray", children: PROVIDER_LABELS[p] }),
|
|
9034
9220
|
p === "azure" && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2014 multiple deployments supported" }),
|
|
9035
9221
|
p === "openai-compatible" && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2014 Groq, Together, custom" }),
|
|
9036
9222
|
p === "ollama" && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2014 no API key needed" })
|
|
9037
9223
|
] }, p);
|
|
9038
9224
|
}) }),
|
|
9225
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)" }) }),
|
|
9039
9226
|
state.error && /* @__PURE__ */ jsx(Text, { color: "red", children: state.error })
|
|
9040
9227
|
] });
|
|
9041
9228
|
}
|
|
@@ -9045,15 +9232,19 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9045
9232
|
const isOllama = currentEntry.type === "ollama";
|
|
9046
9233
|
if (fieldStage === "askMore") {
|
|
9047
9234
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9048
|
-
/* @__PURE__ */
|
|
9049
|
-
|
|
9050
|
-
|
|
9235
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
9236
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "? " }),
|
|
9237
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: isAzure ? "Add another Azure deployment? (y/n)" : "Add another custom endpoint? (y/n)" })
|
|
9238
|
+
] }),
|
|
9239
|
+
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
|
|
9051
9240
|
SelectInput,
|
|
9052
9241
|
{
|
|
9053
9242
|
items: [
|
|
9054
9243
|
{ label: "Yes \u2014 add another", value: "yes" },
|
|
9055
9244
|
{ label: "No \u2014 continue", value: "no" }
|
|
9056
9245
|
],
|
|
9246
|
+
indicatorComponent: ({ isSelected }) => /* @__PURE__ */ jsx(Text, { color: "magenta", children: isSelected ? "\u276F " : " " }),
|
|
9247
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ jsx(Text, { color: isSelected ? "magenta" : "white", children: label }),
|
|
9057
9248
|
onSelect: (item) => {
|
|
9058
9249
|
if (item.value === "yes") {
|
|
9059
9250
|
if (isAzure) dispatch({ type: "ADD_AZURE" });
|
|
@@ -9070,21 +9261,15 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9070
9261
|
) })
|
|
9071
9262
|
] });
|
|
9072
9263
|
}
|
|
9073
|
-
const prompt = isAzure && fieldStage === "deploymentName" ? `Azure deployment name (${currentEntry.label})
|
|
9264
|
+
const prompt = isAzure && fieldStage === "deploymentName" ? `Azure deployment name (${currentEntry.label})` : isAzure && fieldStage === "baseUrl" ? `Azure endpoint URL` : isCompat && fieldStage === "label" ? `Name for this endpoint (e.g. Groq)` : isCompat && fieldStage === "baseUrl" ? `Base URL (e.g. https://api.groq.com/openai/v1)` : isOllama ? `Ollama URL (Enter for http://localhost:11434)` : `${currentEntry.label} API Key`;
|
|
9074
9265
|
const isMasked = fieldStage === "apiKey";
|
|
9075
9266
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9076
|
-
/* @__PURE__ */
|
|
9077
|
-
|
|
9078
|
-
|
|
9079
|
-
|
|
9080
|
-
|
|
9081
|
-
|
|
9082
|
-
": ",
|
|
9083
|
-
currentEntry.label
|
|
9084
|
-
] }) }),
|
|
9085
|
-
/* @__PURE__ */ jsx(Text, { children: prompt }),
|
|
9086
|
-
/* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
9087
|
-
/* @__PURE__ */ jsx(Text, { color: "green", children: "> " }),
|
|
9267
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
9268
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "? " }),
|
|
9269
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: prompt })
|
|
9270
|
+
] }),
|
|
9271
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
9272
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", children: "\u276F " }),
|
|
9088
9273
|
/* @__PURE__ */ jsx(
|
|
9089
9274
|
SafeTextInput,
|
|
9090
9275
|
{
|
|
@@ -9102,13 +9287,13 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9102
9287
|
}
|
|
9103
9288
|
if (state.step === "FETCH_MODELS") {
|
|
9104
9289
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9105
|
-
/* @__PURE__ */
|
|
9106
|
-
|
|
9290
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
9291
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "? " }),
|
|
9292
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Connecting to providers and fetching models..." })
|
|
9293
|
+
] }),
|
|
9294
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
9107
9295
|
state.fetchLog.map((line, i) => /* @__PURE__ */ jsx(Text, { children: line }, i)),
|
|
9108
|
-
state.fetchedModels.length === 0 && /* @__PURE__ */
|
|
9109
|
-
/* @__PURE__ */ jsx(Spinner, { type: "dots" }),
|
|
9110
|
-
/* @__PURE__ */ jsx(Text, { children: " Connecting to providers..." })
|
|
9111
|
-
] })
|
|
9296
|
+
state.fetchedModels.length === 0 && /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Spinner, { type: "dots" }) })
|
|
9112
9297
|
] })
|
|
9113
9298
|
] });
|
|
9114
9299
|
}
|
|
@@ -9123,41 +9308,55 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9123
9308
|
const tierLabel = (tier, hint) => {
|
|
9124
9309
|
const isFocused = state.tierSelectFocus === tier;
|
|
9125
9310
|
const current = tier === "T1" ? state.tierT1 : tier === "T2" ? state.tierT2 : state.tierT3;
|
|
9311
|
+
if (!isFocused) {
|
|
9312
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
9313
|
+
/* @__PURE__ */ jsx(Text, { color: "green", bold: true, children: "\u2714 " }),
|
|
9314
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
9315
|
+
tier,
|
|
9316
|
+
" ",
|
|
9317
|
+
hint,
|
|
9318
|
+
": "
|
|
9319
|
+
] }),
|
|
9320
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", children: current === "auto" ? "Auto \u2014 let Cascade choose best available" : state.fetchedModels.find((m) => m.id === current)?.name || current })
|
|
9321
|
+
] }, tier);
|
|
9322
|
+
}
|
|
9126
9323
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
9127
|
-
/* @__PURE__ */ jsxs(
|
|
9128
|
-
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
current === "auto" ? "Auto" : current
|
|
9324
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
9325
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "? " }),
|
|
9326
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
9327
|
+
tier,
|
|
9328
|
+
" ",
|
|
9329
|
+
hint,
|
|
9330
|
+
": "
|
|
9331
|
+
] })
|
|
9136
9332
|
] }),
|
|
9137
|
-
|
|
9333
|
+
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
|
|
9138
9334
|
SelectInput,
|
|
9139
9335
|
{
|
|
9140
9336
|
items: modelOptions,
|
|
9141
|
-
onSelect: (item) => dispatch({ type: "SET_TIER", tier, value: item.value })
|
|
9337
|
+
onSelect: (item) => dispatch({ type: "SET_TIER", tier, value: item.value }),
|
|
9338
|
+
indicatorComponent: ({ isSelected }) => /* @__PURE__ */ jsx(Text, { color: "magenta", children: isSelected ? "\u276F " : " " }),
|
|
9339
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ jsx(Text, { color: isSelected ? "magenta" : "white", children: label })
|
|
9142
9340
|
}
|
|
9143
|
-
)
|
|
9341
|
+
) })
|
|
9144
9342
|
] }, tier);
|
|
9145
9343
|
};
|
|
9146
9344
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9147
|
-
/* @__PURE__ */
|
|
9148
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Tab to switch tier \xB7 Enter to confirm selection" }),
|
|
9149
|
-
/* @__PURE__ */ jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
|
|
9345
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
9150
9346
|
tierLabel("T1", "(Administrator \u2014 complex reasoning, runs once per task)"),
|
|
9151
9347
|
tierLabel("T2", "(Manager \u2014 runs per section)"),
|
|
9152
9348
|
tierLabel("T3", "(Worker \u2014 high volume, many parallel runs)")
|
|
9153
9349
|
] }),
|
|
9154
|
-
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "
|
|
9350
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(Tab/Arrow Down to skip tier, Enter to select or save)" }) }),
|
|
9155
9351
|
state.error && /* @__PURE__ */ jsx(Text, { color: "red", children: state.error })
|
|
9156
9352
|
] });
|
|
9157
9353
|
}
|
|
9158
9354
|
if (state.step === "SAVE") {
|
|
9159
9355
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9160
|
-
/* @__PURE__ */
|
|
9356
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
9357
|
+
/* @__PURE__ */ jsx(Text, { color: "green", bold: true, children: "\u2714 " }),
|
|
9358
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Setup complete!" })
|
|
9359
|
+
] }),
|
|
9161
9360
|
/* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
9162
9361
|
/* @__PURE__ */ jsx(Spinner, { type: "dots" }),
|
|
9163
9362
|
/* @__PURE__ */ jsx(Text, { children: " Writing .cascade/config.json..." })
|