cascade-ai 0.2.0 → 0.2.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.
- 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 +317 -106
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +317 -106
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +47 -11
- 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 +47 -11
- 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.cjs
CHANGED
|
@@ -27,9 +27,9 @@ var index_js = require('@modelcontextprotocol/sdk/client/index.js');
|
|
|
27
27
|
var stdio_js = require('@modelcontextprotocol/sdk/client/stdio.js');
|
|
28
28
|
var vm = require('vm');
|
|
29
29
|
var Spinner = require('ink-spinner');
|
|
30
|
-
var SelectInput = require('ink-select-input');
|
|
31
30
|
var wrapAnsi = require('wrap-ansi');
|
|
32
31
|
var ora = require('ora');
|
|
32
|
+
var SelectInput = require('ink-select-input');
|
|
33
33
|
var http = require('http');
|
|
34
34
|
var url = require('url');
|
|
35
35
|
var express = require('express');
|
|
@@ -76,9 +76,9 @@ var Database__default = /*#__PURE__*/_interopDefault(Database);
|
|
|
76
76
|
var EventEmitter__default = /*#__PURE__*/_interopDefault(EventEmitter);
|
|
77
77
|
var PDFDocument__default = /*#__PURE__*/_interopDefault(PDFDocument);
|
|
78
78
|
var Spinner__default = /*#__PURE__*/_interopDefault(Spinner);
|
|
79
|
-
var SelectInput__default = /*#__PURE__*/_interopDefault(SelectInput);
|
|
80
79
|
var wrapAnsi__default = /*#__PURE__*/_interopDefault(wrapAnsi);
|
|
81
80
|
var ora__default = /*#__PURE__*/_interopDefault(ora);
|
|
81
|
+
var SelectInput__default = /*#__PURE__*/_interopDefault(SelectInput);
|
|
82
82
|
var express__default = /*#__PURE__*/_interopDefault(express);
|
|
83
83
|
var rateLimit__default = /*#__PURE__*/_interopDefault(rateLimit);
|
|
84
84
|
var bcrypt__default = /*#__PURE__*/_interopDefault(bcrypt);
|
|
@@ -129,7 +129,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
129
129
|
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;
|
|
130
130
|
var init_constants = __esm({
|
|
131
131
|
"src/constants.ts"() {
|
|
132
|
-
CASCADE_VERSION = "0.
|
|
132
|
+
CASCADE_VERSION = "0.2.2";
|
|
133
133
|
CASCADE_CONFIG_FILE = ".cascade/config.json";
|
|
134
134
|
CASCADE_DB_FILE = ".cascade/memory.db";
|
|
135
135
|
CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
|
|
@@ -2827,6 +2827,15 @@ var ModelSelector = class {
|
|
|
2827
2827
|
markProviderUnavailable(provider) {
|
|
2828
2828
|
this.availableProviders.delete(provider);
|
|
2829
2829
|
}
|
|
2830
|
+
/**
|
|
2831
|
+
* Re-add a provider to the available set after it has recovered (e.g. after
|
|
2832
|
+
* a failover timeout expires or a successful call confirms recovery). Only
|
|
2833
|
+
* re-enables providers that were originally configured — callers should
|
|
2834
|
+
* guard against enabling providers that were never configured.
|
|
2835
|
+
*/
|
|
2836
|
+
markProviderAvailable(provider) {
|
|
2837
|
+
this.availableProviders.add(provider);
|
|
2838
|
+
}
|
|
2830
2839
|
resolveDynamicModel(overrideModelId) {
|
|
2831
2840
|
let providerStr = null;
|
|
2832
2841
|
let actualId = overrideModelId;
|
|
@@ -2896,10 +2905,23 @@ var FailoverManager = class {
|
|
|
2896
2905
|
if (!failure) return true;
|
|
2897
2906
|
if (Date.now() - failure.failedAt >= failure.retryAfterMs) {
|
|
2898
2907
|
this.failures.delete(provider);
|
|
2908
|
+
this.selector.markProviderAvailable(provider);
|
|
2899
2909
|
return true;
|
|
2900
2910
|
}
|
|
2901
2911
|
return false;
|
|
2902
2912
|
}
|
|
2913
|
+
/**
|
|
2914
|
+
* Call after a successful generation to immediately re-enable a provider
|
|
2915
|
+
* that had previously been marked unavailable. This allows fast recovery
|
|
2916
|
+
* when a transient rate-limit clears before the backoff window expires,
|
|
2917
|
+
* preventing unnecessary routing to more expensive fallback models.
|
|
2918
|
+
*/
|
|
2919
|
+
recordSuccess(provider) {
|
|
2920
|
+
if (this.failures.has(provider)) {
|
|
2921
|
+
this.failures.delete(provider);
|
|
2922
|
+
this.selector.markProviderAvailable(provider);
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2903
2925
|
getFallbackModel(currentModel, tier) {
|
|
2904
2926
|
return this.selector.getNextFallback(currentModel.id, tier);
|
|
2905
2927
|
}
|
|
@@ -2916,6 +2938,7 @@ var FailoverManager = class {
|
|
|
2916
2938
|
}
|
|
2917
2939
|
clearFailure(provider) {
|
|
2918
2940
|
this.failures.delete(provider);
|
|
2941
|
+
this.selector.markProviderAvailable(provider);
|
|
2919
2942
|
}
|
|
2920
2943
|
};
|
|
2921
2944
|
|
|
@@ -3131,6 +3154,7 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
3131
3154
|
throw new Error(`Provider ${model.provider}:${model.id} returned an invalid generation result.`);
|
|
3132
3155
|
}
|
|
3133
3156
|
this.recordStats(tier, model, result.usage);
|
|
3157
|
+
this.failover.recordSuccess(model.provider);
|
|
3134
3158
|
return result;
|
|
3135
3159
|
} catch (err) {
|
|
3136
3160
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -3830,14 +3854,13 @@ Now execute your subtask using this context where relevant.`
|
|
|
3830
3854
|
await this.peerBus.barrier(this.id, barrierName, total);
|
|
3831
3855
|
}
|
|
3832
3856
|
receivePeerSync(fromId, content) {
|
|
3833
|
-
|
|
3834
|
-
if (existing) {
|
|
3835
|
-
existing.content = content;
|
|
3836
|
-
existing.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3837
|
-
} else {
|
|
3838
|
-
this.peerSyncBuffer.push({ fromId, content, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
3839
|
-
}
|
|
3857
|
+
this.peerSyncBuffer.push({ fromId, content, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
3840
3858
|
this.emit("peer-sync-received", { fromId, content });
|
|
3859
|
+
this.context.addMessage({
|
|
3860
|
+
role: "user",
|
|
3861
|
+
content: `[SYSTEM_NOTIFICATION]: You received a new peer message from ${fromId}. Use the "peer_message" tool with action="receive" to read it.`
|
|
3862
|
+
}).catch(() => {
|
|
3863
|
+
});
|
|
3841
3864
|
}
|
|
3842
3865
|
// ── Private ──────────────────────────────────
|
|
3843
3866
|
async runAgentLoop(systemPrompt, tools) {
|
|
@@ -3968,7 +3991,11 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
3968
3991
|
sendPeerSync: (to, syncType, content) => {
|
|
3969
3992
|
this.peerBus?.send(this.id, to, syncType, this.assignment?.subtaskId ?? "", content);
|
|
3970
3993
|
},
|
|
3971
|
-
getPeerMessages: () =>
|
|
3994
|
+
getPeerMessages: () => {
|
|
3995
|
+
const msgs = [...this.peerSyncBuffer];
|
|
3996
|
+
this.peerSyncBuffer = [];
|
|
3997
|
+
return msgs;
|
|
3998
|
+
}
|
|
3972
3999
|
});
|
|
3973
4000
|
if (this.audit) {
|
|
3974
4001
|
this.audit.toolCall(this.id, tc.name, tc.input);
|
|
@@ -4510,13 +4537,17 @@ var T2Manager = class extends BaseTier {
|
|
|
4510
4537
|
}
|
|
4511
4538
|
// ── Private ──────────────────────────────────
|
|
4512
4539
|
async decomposeSection(assignment) {
|
|
4540
|
+
const peerPlans = this.peerSyncBuffer.filter((p) => p.content?.type === "T2_PLAN_ANNOUNCEMENT").map((p) => `[Peer ${p.fromId} Plan]: ${p.content.sectionTitle} - ${p.content.subtaskTitles?.join(", ")}`).join("\n");
|
|
4513
4541
|
const prompt = `Decompose this section into 2-5 concrete subtasks for T3 workers.
|
|
4514
4542
|
|
|
4515
4543
|
Section: ${assignment.sectionTitle}
|
|
4516
4544
|
Description: ${assignment.description}
|
|
4517
4545
|
Expected output: ${assignment.expectedOutput}
|
|
4518
4546
|
Constraints: ${assignment.constraints.join("; ")}
|
|
4519
|
-
|
|
4547
|
+
${peerPlans ? `
|
|
4548
|
+
Context from sibling T2 plans (use this to align execution and avoid overlaps):
|
|
4549
|
+
${peerPlans}
|
|
4550
|
+
` : ""}
|
|
4520
4551
|
Return a JSON array of subtask objects, each with:
|
|
4521
4552
|
- subtaskId: string (unique)
|
|
4522
4553
|
- subtaskTitle: string
|
|
@@ -4730,9 +4761,14 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
4730
4761
|
const completed = results.filter((r) => r.status === "COMPLETED");
|
|
4731
4762
|
if (!completed.length) return `Section ${assignment.sectionTitle} failed \u2014 no T3 workers completed.`;
|
|
4732
4763
|
const outputs = completed.map((r, i) => `[T3-${i + 1}]: ${r.output}`).join("\n\n");
|
|
4764
|
+
const peerOutputs = this.peerSyncBuffer.filter((p) => p.content?.type === "T2_SECTION_OUTPUT").map((p) => `[Peer ${p.fromId} Output]: ${p.content.output}`).join("\n\n");
|
|
4733
4765
|
const prompt = `Summarize these T3 worker outputs for section "${assignment.sectionTitle}" in 2-3 sentences:
|
|
4734
4766
|
|
|
4735
|
-
${outputs}
|
|
4767
|
+
${outputs}
|
|
4768
|
+
${peerOutputs ? `
|
|
4769
|
+
|
|
4770
|
+
Context from sibling T2 completed sections (use this to ensure your summary aligns with the overall state):
|
|
4771
|
+
${peerOutputs}` : ""}`;
|
|
4736
4772
|
const messages = [{ role: "user", content: prompt }];
|
|
4737
4773
|
try {
|
|
4738
4774
|
const result = await this.router.generate("T2", {
|
|
@@ -7350,12 +7386,17 @@ var SlashCommandRegistry = class {
|
|
|
7350
7386
|
});
|
|
7351
7387
|
this.register({
|
|
7352
7388
|
command: "/model",
|
|
7389
|
+
description: "Pick a provider and model for a tier (interactive)",
|
|
7390
|
+
handler: (_args, ctx) => ({ output: ctx.onModelPicker(), handled: true })
|
|
7391
|
+
});
|
|
7392
|
+
this.register({
|
|
7393
|
+
command: "/model-info",
|
|
7353
7394
|
description: "Show active models per tier",
|
|
7354
7395
|
handler: (_args, ctx) => ({ output: ctx.onModelInfo(), handled: true })
|
|
7355
7396
|
});
|
|
7356
7397
|
this.register({
|
|
7357
7398
|
command: "/models",
|
|
7358
|
-
description: "
|
|
7399
|
+
description: "Browse available models by provider",
|
|
7359
7400
|
handler: (_args, ctx) => ({ output: ctx.onModelsInfo(), handled: true })
|
|
7360
7401
|
});
|
|
7361
7402
|
this.register({
|
|
@@ -7658,55 +7699,140 @@ function ApprovalPrompt({ request, theme, onDecision }) {
|
|
|
7658
7699
|
}
|
|
7659
7700
|
);
|
|
7660
7701
|
}
|
|
7661
|
-
var
|
|
7662
|
-
|
|
7663
|
-
|
|
7702
|
+
var TIERS = [
|
|
7703
|
+
{ id: "T1", label: "T1 \u2014 Administrator", hint: "complex reasoning \xB7 runs once per task" },
|
|
7704
|
+
{ id: "T2", label: "T2 \u2014 Manager", hint: "per-section planning \xB7 a few calls per task" },
|
|
7705
|
+
{ id: "T3", label: "T3 \u2014 Worker", hint: "high volume \xB7 many parallel runs" }
|
|
7706
|
+
];
|
|
7707
|
+
var ModelsDisplay = ({
|
|
7708
|
+
providers,
|
|
7709
|
+
modelsByProvider,
|
|
7710
|
+
onSelect,
|
|
7711
|
+
onClose
|
|
7712
|
+
}) => {
|
|
7713
|
+
const [step, setStep] = React5.useState("PROVIDER");
|
|
7714
|
+
const [cursor, setCursor] = React5.useState(0);
|
|
7715
|
+
const [picked, setPicked] = React5.useState({});
|
|
7716
|
+
const providerItems = React5.useMemo(() => {
|
|
7717
|
+
const base = [
|
|
7718
|
+
{ label: "\u25C7 Auto", sublabel: "let Cascade pick per tier \u2014 recommended", value: "auto" }
|
|
7719
|
+
];
|
|
7720
|
+
for (const p of providers) {
|
|
7721
|
+
const count = modelsByProvider.get(p)?.length ?? 0;
|
|
7722
|
+
base.push({ label: p, sublabel: `${count} model${count === 1 ? "" : "s"} discovered`, value: p });
|
|
7723
|
+
}
|
|
7724
|
+
return base;
|
|
7725
|
+
}, [providers, modelsByProvider]);
|
|
7726
|
+
const tierItems = React5.useMemo(
|
|
7727
|
+
() => TIERS.map((t) => ({ label: t.label, sublabel: t.hint, value: t.id })),
|
|
7728
|
+
[]
|
|
7729
|
+
);
|
|
7730
|
+
const modelItems = React5.useMemo(() => {
|
|
7731
|
+
if (!picked.provider || picked.provider === "auto") return [];
|
|
7732
|
+
const list = (modelsByProvider.get(picked.provider) ?? []).slice().sort((a, b) => a.id.localeCompare(b.id));
|
|
7733
|
+
const items = [
|
|
7734
|
+
{ label: "\u25C7 Auto", sublabel: "best available from this provider", value: "__auto__" }
|
|
7735
|
+
];
|
|
7736
|
+
for (const m of list) {
|
|
7737
|
+
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`;
|
|
7738
|
+
items.push({ label: m.name, sublabel: `${m.id} \xB7 ${ctx}`, value: m.id });
|
|
7739
|
+
}
|
|
7740
|
+
return items;
|
|
7741
|
+
}, [picked.provider, modelsByProvider]);
|
|
7742
|
+
const currentItems = step === "PROVIDER" ? providerItems : step === "TIER" ? tierItems : modelItems;
|
|
7743
|
+
ink.useInput((input, key) => {
|
|
7664
7744
|
if (key.escape) {
|
|
7665
|
-
if (
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7745
|
+
if (step === "MODEL") {
|
|
7746
|
+
setStep("TIER");
|
|
7747
|
+
setCursor(0);
|
|
7748
|
+
return;
|
|
7669
7749
|
}
|
|
7750
|
+
if (step === "TIER") {
|
|
7751
|
+
setStep("PROVIDER");
|
|
7752
|
+
setCursor(0);
|
|
7753
|
+
return;
|
|
7754
|
+
}
|
|
7755
|
+
onClose();
|
|
7756
|
+
return;
|
|
7670
7757
|
}
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7758
|
+
if (key.upArrow || input === "k") {
|
|
7759
|
+
setCursor((c) => currentItems.length === 0 ? 0 : (c - 1 + currentItems.length) % currentItems.length);
|
|
7760
|
+
return;
|
|
7761
|
+
}
|
|
7762
|
+
if (key.downArrow || key.tab || input === "j") {
|
|
7763
|
+
setCursor((c) => currentItems.length === 0 ? 0 : (c + 1) % currentItems.length);
|
|
7764
|
+
return;
|
|
7765
|
+
}
|
|
7766
|
+
if (/^[1-9]$/.test(input)) {
|
|
7767
|
+
const idx = parseInt(input, 10) - 1;
|
|
7768
|
+
if (idx < currentItems.length) setCursor(idx);
|
|
7769
|
+
return;
|
|
7770
|
+
}
|
|
7771
|
+
if (key.return) {
|
|
7772
|
+
const selected = currentItems[cursor];
|
|
7773
|
+
if (!selected) return;
|
|
7774
|
+
if (step === "PROVIDER") {
|
|
7775
|
+
if (selected.value === "auto") {
|
|
7776
|
+
setPicked({ provider: "auto" });
|
|
7777
|
+
setStep("TIER");
|
|
7778
|
+
setCursor(0);
|
|
7779
|
+
return;
|
|
7688
7780
|
}
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
] }),
|
|
7701
|
-
modelItems.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { italic: true, color: "yellow", children: "No supported models discovered for this provider." }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
7702
|
-
SelectInput__default.default,
|
|
7703
|
-
{
|
|
7704
|
-
items: modelItems,
|
|
7705
|
-
limit: 10,
|
|
7706
|
-
onSelect: () => {
|
|
7781
|
+
setPicked({ provider: selected.value });
|
|
7782
|
+
setStep("TIER");
|
|
7783
|
+
setCursor(0);
|
|
7784
|
+
return;
|
|
7785
|
+
}
|
|
7786
|
+
if (step === "TIER") {
|
|
7787
|
+
const tier = selected.value;
|
|
7788
|
+
if (picked.provider === "auto") {
|
|
7789
|
+
onSelect?.({ kind: "auto", tier });
|
|
7790
|
+
onClose();
|
|
7791
|
+
return;
|
|
7707
7792
|
}
|
|
7793
|
+
setPicked((p) => ({ ...p, tier }));
|
|
7794
|
+
setStep("MODEL");
|
|
7795
|
+
setCursor(0);
|
|
7796
|
+
return;
|
|
7708
7797
|
}
|
|
7709
|
-
|
|
7798
|
+
if (!picked.tier || !picked.provider || picked.provider === "auto") {
|
|
7799
|
+
onClose();
|
|
7800
|
+
return;
|
|
7801
|
+
}
|
|
7802
|
+
if (selected.value === "__auto__") {
|
|
7803
|
+
onSelect?.({ kind: "auto", tier: picked.tier });
|
|
7804
|
+
} else {
|
|
7805
|
+
onSelect?.({
|
|
7806
|
+
kind: "pick",
|
|
7807
|
+
tier: picked.tier,
|
|
7808
|
+
provider: picked.provider,
|
|
7809
|
+
modelId: selected.value
|
|
7810
|
+
});
|
|
7811
|
+
}
|
|
7812
|
+
onClose();
|
|
7813
|
+
}
|
|
7814
|
+
});
|
|
7815
|
+
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}`;
|
|
7816
|
+
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}`;
|
|
7817
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
|
|
7818
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { justifyContent: "space-between", children: [
|
|
7819
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, color: "cyan", children: title }),
|
|
7820
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "gray", children: "[Esc back \xB7 Enter select]" })
|
|
7821
|
+
] }),
|
|
7822
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { marginBottom: 1, children: /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { color: "gray", children: [
|
|
7823
|
+
breadcrumb,
|
|
7824
|
+
" \xB7 \u2191/\u2193 navigate \xB7 1\u20139 jump"
|
|
7825
|
+
] }) }),
|
|
7826
|
+
currentItems.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { italic: true, color: "yellow", children: "No items to show." }) : /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { flexDirection: "column", children: currentItems.map((item, i) => {
|
|
7827
|
+
const focused = i === cursor;
|
|
7828
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", children: [
|
|
7829
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: focused ? "green" : "gray", children: focused ? "\u276F " : " " }),
|
|
7830
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
7831
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: focused ? "white" : "gray", bold: focused, children: item.label }),
|
|
7832
|
+
item.sublabel && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "gray", dimColor: true, children: ` ${item.sublabel}` })
|
|
7833
|
+
] })
|
|
7834
|
+
] }, `${step}-${item.value}-${i}`);
|
|
7835
|
+
}) })
|
|
7710
7836
|
] });
|
|
7711
7837
|
};
|
|
7712
7838
|
function CostTracker({
|
|
@@ -7971,6 +8097,40 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName })
|
|
|
7971
8097
|
const persistMessage = React5.useCallback((role, content, timestamp) => {
|
|
7972
8098
|
storeRef.current?.addMessage({ id: crypto.randomUUID(), sessionId: sessionIdRef.current, role, content, timestamp });
|
|
7973
8099
|
}, []);
|
|
8100
|
+
const applyModelPick = React5.useCallback(async (sel) => {
|
|
8101
|
+
const tierKey = sel.tier.toLowerCase();
|
|
8102
|
+
const tierLabel = sel.tier;
|
|
8103
|
+
if (sel.kind === "auto") {
|
|
8104
|
+
delete config.models[tierKey];
|
|
8105
|
+
} else {
|
|
8106
|
+
config.models[tierKey] = sel.modelId;
|
|
8107
|
+
}
|
|
8108
|
+
try {
|
|
8109
|
+
const router = cascadeRef.current?.getRouter();
|
|
8110
|
+
if (router && sel.kind === "pick") {
|
|
8111
|
+
const candidate = (cachedModels.get(sel.provider) ?? []).find((m) => m.id === sel.modelId);
|
|
8112
|
+
if (candidate) router.overrideTierModel(tierLabel, candidate);
|
|
8113
|
+
}
|
|
8114
|
+
} catch {
|
|
8115
|
+
}
|
|
8116
|
+
const configPath = path17__default.default.join(workspacePath, ".cascade", "config.json");
|
|
8117
|
+
try {
|
|
8118
|
+
await fs7__default.default.mkdir(path17__default.default.dirname(configPath), { recursive: true });
|
|
8119
|
+
await fs7__default.default.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
8120
|
+
} catch (err) {
|
|
8121
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
8122
|
+
dispatch({
|
|
8123
|
+
type: "ADD_MESSAGE",
|
|
8124
|
+
message: { id: crypto.randomUUID(), role: "error", content: `Failed to persist model selection: ${msg}`, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
8125
|
+
});
|
|
8126
|
+
return;
|
|
8127
|
+
}
|
|
8128
|
+
const summary = sel.kind === "auto" ? `${tierLabel} \u2192 Auto (Cascade will pick best available).` : `${tierLabel} \u2192 ${sel.modelId} (provider: ${sel.provider}).`;
|
|
8129
|
+
dispatch({
|
|
8130
|
+
type: "ADD_MESSAGE",
|
|
8131
|
+
message: { id: crypto.randomUUID(), role: "system", content: `\u2714 Model updated \u2014 ${summary}`, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
8132
|
+
});
|
|
8133
|
+
}, [config, workspacePath, cachedModels]);
|
|
7974
8134
|
const globalStoreRef = React5.useRef(null);
|
|
7975
8135
|
const persistRuntimeSession = React5.useCallback((status, latestPrompt) => {
|
|
7976
8136
|
const runtimeSession = { sessionId: sessionIdRef.current, title: sessionTitle, workspacePath, status, startedAt: startedAtRef.current, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), latestPrompt, isGlobal: false };
|
|
@@ -8117,6 +8277,10 @@ ${msg.content}`).join("\n\n");
|
|
|
8117
8277
|
return `${t}: ${m?.name ?? "none"} (${m?.provider ?? "\u2014"})${configured ? ` | configured: ${configured}` : ""}`;
|
|
8118
8278
|
}).join("\n");
|
|
8119
8279
|
},
|
|
8280
|
+
onModelPicker: async () => {
|
|
8281
|
+
setIsShowingModels(true);
|
|
8282
|
+
return "Opening model picker \u2014 choose provider \u2192 tier \u2192 model (ESC to exit).";
|
|
8283
|
+
},
|
|
8120
8284
|
onModelsInfo: async () => {
|
|
8121
8285
|
setIsShowingModels(true);
|
|
8122
8286
|
return "Opening interactive models explorer... (ESC to exit)";
|
|
@@ -8399,6 +8563,16 @@ Use /identity <name|id> to switch.`;
|
|
|
8399
8563
|
}
|
|
8400
8564
|
}, [handleSlashCommand, persistMessage, state.messages, workspacePath, rebuildTree, recordNodeEvent, slashCompletions, slashIndex, state.isExecuting]);
|
|
8401
8565
|
ink.useInput((_input, key) => {
|
|
8566
|
+
if (isShowingModels) {
|
|
8567
|
+
if (key.ctrl && _input === "c") {
|
|
8568
|
+
if (quitAttempted) {
|
|
8569
|
+
exit();
|
|
8570
|
+
return;
|
|
8571
|
+
}
|
|
8572
|
+
setQuitAttempted(true);
|
|
8573
|
+
}
|
|
8574
|
+
return;
|
|
8575
|
+
}
|
|
8402
8576
|
if (key.ctrl && _input === "c") {
|
|
8403
8577
|
if (quitAttempted) {
|
|
8404
8578
|
exit();
|
|
@@ -8567,7 +8741,17 @@ Use /identity <name|id> to switch.`;
|
|
|
8567
8741
|
] }) }),
|
|
8568
8742
|
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingX: 1, children: [
|
|
8569
8743
|
state.messages.length === 0 && !state.isStreaming && /* @__PURE__ */ jsxRuntime.jsx(WelcomeBanner, { theme, config, workspacePath, sessionId: sessionIdRef.current }),
|
|
8570
|
-
isShowingModels && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8744
|
+
isShowingModels && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8745
|
+
ModelsDisplay,
|
|
8746
|
+
{
|
|
8747
|
+
providers: config.providers.map((p) => p.type),
|
|
8748
|
+
modelsByProvider: cachedModels,
|
|
8749
|
+
onSelect: (sel) => {
|
|
8750
|
+
void applyModelPick(sel);
|
|
8751
|
+
},
|
|
8752
|
+
onClose: () => setIsShowingModels(false)
|
|
8753
|
+
}
|
|
8754
|
+
)
|
|
8571
8755
|
] }),
|
|
8572
8756
|
/* @__PURE__ */ jsxRuntime.jsx(AgentTree, { root: state.agentTree, theme }),
|
|
8573
8757
|
state.showDetails && /* @__PURE__ */ jsxRuntime.jsx(TimelinePanel, { nodes: [...treeNodesRef.current.values()], theme, currentIndex: timelineIndex, onChangeIndex: setTimelineIndex }),
|
|
@@ -8619,7 +8803,7 @@ Use /identity <name|id> to switch.`;
|
|
|
8619
8803
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8620
8804
|
SafeTextInput,
|
|
8621
8805
|
{
|
|
8622
|
-
focus: !state.approvalRequest,
|
|
8806
|
+
focus: !state.approvalRequest && !isShowingModels,
|
|
8623
8807
|
value: input,
|
|
8624
8808
|
manageMouseReporting: false,
|
|
8625
8809
|
onChange: (val) => {
|
|
@@ -8812,6 +8996,7 @@ var PROVIDER_LABELS = {
|
|
|
8812
8996
|
ollama: "Ollama (local)",
|
|
8813
8997
|
"openai-compatible": "OpenAI-Compatible"
|
|
8814
8998
|
};
|
|
8999
|
+
var providerOrder = ["anthropic", "openai", "gemini", "azure", "ollama", "openai-compatible"];
|
|
8815
9000
|
function buildInitialEntries(types) {
|
|
8816
9001
|
return [...types].map((type) => ({
|
|
8817
9002
|
id: crypto.randomUUID(),
|
|
@@ -8827,6 +9012,17 @@ function wizardReducer(state, action) {
|
|
|
8827
9012
|
else next.add(action.provider);
|
|
8828
9013
|
return { ...state, selectedTypes: next };
|
|
8829
9014
|
}
|
|
9015
|
+
case "TOGGLE_ALL": {
|
|
9016
|
+
const allSelected = state.selectedTypes.size === providerOrder.length;
|
|
9017
|
+
return { ...state, selectedTypes: allSelected ? /* @__PURE__ */ new Set() : new Set(providerOrder) };
|
|
9018
|
+
}
|
|
9019
|
+
case "INVERT_SELECTION": {
|
|
9020
|
+
const next = /* @__PURE__ */ new Set();
|
|
9021
|
+
providerOrder.forEach((p) => {
|
|
9022
|
+
if (!state.selectedTypes.has(p)) next.add(p);
|
|
9023
|
+
});
|
|
9024
|
+
return { ...state, selectedTypes: next };
|
|
9025
|
+
}
|
|
8830
9026
|
case "CONFIRM_PROVIDERS": {
|
|
8831
9027
|
const entries = buildInitialEntries(state.selectedTypes);
|
|
8832
9028
|
return { ...state, entries, currentEntryIdx: 0, step: "API_KEYS" };
|
|
@@ -8914,7 +9110,6 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
8914
9110
|
tierSelectFocus: "T1",
|
|
8915
9111
|
error: null
|
|
8916
9112
|
});
|
|
8917
|
-
const providerOrder = ["anthropic", "openai", "gemini", "azure", "ollama", "openai-compatible"];
|
|
8918
9113
|
const [providerCursor, setProviderCursor] = React5.useState(0);
|
|
8919
9114
|
const [fieldBuffer, setFieldBuffer] = React5.useState("");
|
|
8920
9115
|
const [fieldStage, setFieldStage] = React5.useState("apiKey");
|
|
@@ -9007,6 +9202,8 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9007
9202
|
if (key.upArrow) setProviderCursor((p) => Math.max(0, p - 1));
|
|
9008
9203
|
if (key.downArrow) setProviderCursor((p) => Math.min(providerOrder.length - 1, p + 1));
|
|
9009
9204
|
if (_input === " ") dispatch({ type: "TOGGLE_PROVIDER", provider: providerOrder[providerCursor] });
|
|
9205
|
+
if (_input === "a") dispatch({ type: "TOGGLE_ALL" });
|
|
9206
|
+
if (_input === "i") dispatch({ type: "INVERT_SELECTION" });
|
|
9010
9207
|
if (key.return) {
|
|
9011
9208
|
if (state.selectedTypes.size === 0) return;
|
|
9012
9209
|
dispatch({ type: "CONFIRM_PROVIDERS" });
|
|
@@ -9068,21 +9265,23 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9068
9265
|
}, [currentEntry, fieldStage]);
|
|
9069
9266
|
if (state.step === "PROVIDER_SELECT") {
|
|
9070
9267
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9071
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9072
|
-
|
|
9073
|
-
|
|
9074
|
-
|
|
9268
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginBottom: 1, children: [
|
|
9269
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", bold: true, children: "? " }),
|
|
9270
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Which providers do you want to configure?" })
|
|
9271
|
+
] }),
|
|
9272
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { flexDirection: "column", children: providerOrder.map((p, i) => {
|
|
9075
9273
|
const selected = state.selectedTypes.has(p);
|
|
9076
9274
|
const focused = i === providerCursor;
|
|
9077
9275
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { children: [
|
|
9078
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: focused ? "
|
|
9079
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: selected ? "green" : "white", children: selected ? "\u25C9 " : "\
|
|
9080
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: focused ? "white" : "gray", children: PROVIDER_LABELS[p] }),
|
|
9276
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: focused ? "magenta" : "white", children: focused ? "\u276F " : " " }),
|
|
9277
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: selected ? "green" : "white", children: selected ? "\u25C9 " : "\u25EF " }),
|
|
9278
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: focused ? "magenta" : selected ? "white" : "gray", children: PROVIDER_LABELS[p] }),
|
|
9081
9279
|
p === "azure" && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: " \u2014 multiple deployments supported" }),
|
|
9082
9280
|
p === "openai-compatible" && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: " \u2014 Groq, Together, custom" }),
|
|
9083
9281
|
p === "ollama" && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: " \u2014 no API key needed" })
|
|
9084
9282
|
] }, p);
|
|
9085
9283
|
}) }),
|
|
9284
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "(Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)" }) }),
|
|
9086
9285
|
state.error && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "red", children: state.error })
|
|
9087
9286
|
] });
|
|
9088
9287
|
}
|
|
@@ -9092,15 +9291,19 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9092
9291
|
const isOllama = currentEntry.type === "ollama";
|
|
9093
9292
|
if (fieldStage === "askMore") {
|
|
9094
9293
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9095
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9096
|
-
|
|
9097
|
-
|
|
9294
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginBottom: 1, children: [
|
|
9295
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", bold: true, children: "? " }),
|
|
9296
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: isAzure ? "Add another Azure deployment? (y/n)" : "Add another custom endpoint? (y/n)" })
|
|
9297
|
+
] }),
|
|
9298
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9098
9299
|
SelectInput__default.default,
|
|
9099
9300
|
{
|
|
9100
9301
|
items: [
|
|
9101
9302
|
{ label: "Yes \u2014 add another", value: "yes" },
|
|
9102
9303
|
{ label: "No \u2014 continue", value: "no" }
|
|
9103
9304
|
],
|
|
9305
|
+
indicatorComponent: ({ isSelected }) => /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", children: isSelected ? "\u276F " : " " }),
|
|
9306
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: isSelected ? "magenta" : "white", children: label }),
|
|
9104
9307
|
onSelect: (item) => {
|
|
9105
9308
|
if (item.value === "yes") {
|
|
9106
9309
|
if (isAzure) dispatch({ type: "ADD_AZURE" });
|
|
@@ -9117,21 +9320,15 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9117
9320
|
) })
|
|
9118
9321
|
] });
|
|
9119
9322
|
}
|
|
9120
|
-
const prompt = isAzure && fieldStage === "deploymentName" ? `Azure deployment name (${currentEntry.label})
|
|
9323
|
+
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`;
|
|
9121
9324
|
const isMasked = fieldStage === "apiKey";
|
|
9122
9325
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9123
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
|
|
9127
|
-
|
|
9128
|
-
|
|
9129
|
-
": ",
|
|
9130
|
-
currentEntry.label
|
|
9131
|
-
] }) }),
|
|
9132
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: prompt }),
|
|
9133
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginTop: 1, children: [
|
|
9134
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "green", children: "> " }),
|
|
9326
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginBottom: 1, children: [
|
|
9327
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", bold: true, children: "? " }),
|
|
9328
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: prompt })
|
|
9329
|
+
] }),
|
|
9330
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { children: [
|
|
9331
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", children: "\u276F " }),
|
|
9135
9332
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9136
9333
|
SafeTextInput,
|
|
9137
9334
|
{
|
|
@@ -9149,13 +9346,13 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9149
9346
|
}
|
|
9150
9347
|
if (state.step === "FETCH_MODELS") {
|
|
9151
9348
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9152
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9153
|
-
|
|
9349
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginBottom: 1, children: [
|
|
9350
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", bold: true, children: "? " }),
|
|
9351
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Connecting to providers and fetching models..." })
|
|
9352
|
+
] }),
|
|
9353
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
9154
9354
|
state.fetchLog.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: line }, i)),
|
|
9155
|
-
state.fetchedModels.length === 0 && /* @__PURE__ */ jsxRuntime.
|
|
9156
|
-
/* @__PURE__ */ jsxRuntime.jsx(Spinner__default.default, { type: "dots" }),
|
|
9157
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: " Connecting to providers..." })
|
|
9158
|
-
] })
|
|
9355
|
+
state.fetchedModels.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(Spinner__default.default, { type: "dots" }) })
|
|
9159
9356
|
] })
|
|
9160
9357
|
] });
|
|
9161
9358
|
}
|
|
@@ -9170,41 +9367,55 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9170
9367
|
const tierLabel = (tier, hint) => {
|
|
9171
9368
|
const isFocused = state.tierSelectFocus === tier;
|
|
9172
9369
|
const current = tier === "T1" ? state.tierT1 : tier === "T2" ? state.tierT2 : state.tierT3;
|
|
9370
|
+
if (!isFocused) {
|
|
9371
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { children: [
|
|
9372
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "green", bold: true, children: "\u2714 " }),
|
|
9373
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { bold: true, children: [
|
|
9374
|
+
tier,
|
|
9375
|
+
" ",
|
|
9376
|
+
hint,
|
|
9377
|
+
": "
|
|
9378
|
+
] }),
|
|
9379
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", children: current === "auto" ? "Auto \u2014 let Cascade choose best available" : state.fetchedModels.find((m) => m.id === current)?.name || current })
|
|
9380
|
+
] }, tier);
|
|
9381
|
+
}
|
|
9173
9382
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
9174
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
current === "auto" ? "Auto" : current
|
|
9383
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { children: [
|
|
9384
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", bold: true, children: "? " }),
|
|
9385
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { bold: true, children: [
|
|
9386
|
+
tier,
|
|
9387
|
+
" ",
|
|
9388
|
+
hint,
|
|
9389
|
+
": "
|
|
9390
|
+
] })
|
|
9183
9391
|
] }),
|
|
9184
|
-
|
|
9392
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9185
9393
|
SelectInput__default.default,
|
|
9186
9394
|
{
|
|
9187
9395
|
items: modelOptions,
|
|
9188
|
-
onSelect: (item) => dispatch({ type: "SET_TIER", tier, value: item.value })
|
|
9396
|
+
onSelect: (item) => dispatch({ type: "SET_TIER", tier, value: item.value }),
|
|
9397
|
+
indicatorComponent: ({ isSelected }) => /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "magenta", children: isSelected ? "\u276F " : " " }),
|
|
9398
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: isSelected ? "magenta" : "white", children: label })
|
|
9189
9399
|
}
|
|
9190
|
-
)
|
|
9400
|
+
) })
|
|
9191
9401
|
] }, tier);
|
|
9192
9402
|
};
|
|
9193
9403
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9194
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9195
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Tab to switch tier \xB7 Enter to confirm selection" }),
|
|
9196
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginTop: 1, flexDirection: "column", children: [
|
|
9404
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
9197
9405
|
tierLabel("T1", "(Administrator \u2014 complex reasoning, runs once per task)"),
|
|
9198
9406
|
tierLabel("T2", "(Manager \u2014 runs per section)"),
|
|
9199
9407
|
tierLabel("T3", "(Worker \u2014 high volume, many parallel runs)")
|
|
9200
9408
|
] }),
|
|
9201
|
-
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "
|
|
9409
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "(Tab/Arrow Down to skip tier, Enter to select or save)" }) }),
|
|
9202
9410
|
state.error && /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "red", children: state.error })
|
|
9203
9411
|
] });
|
|
9204
9412
|
}
|
|
9205
9413
|
if (state.step === "SAVE") {
|
|
9206
9414
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9207
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9415
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { children: [
|
|
9416
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { color: "green", bold: true, children: "\u2714 " }),
|
|
9417
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Setup complete!" })
|
|
9418
|
+
] }),
|
|
9208
9419
|
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { marginTop: 1, children: [
|
|
9209
9420
|
/* @__PURE__ */ jsxRuntime.jsx(Spinner__default.default, { type: "dots" }),
|
|
9210
9421
|
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: " Writing .cascade/config.json..." })
|