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.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.2";
|
|
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);
|
|
@@ -3783,14 +3807,13 @@ Now execute your subtask using this context where relevant.`
|
|
|
3783
3807
|
await this.peerBus.barrier(this.id, barrierName, total);
|
|
3784
3808
|
}
|
|
3785
3809
|
receivePeerSync(fromId, content) {
|
|
3786
|
-
|
|
3787
|
-
if (existing) {
|
|
3788
|
-
existing.content = content;
|
|
3789
|
-
existing.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3790
|
-
} else {
|
|
3791
|
-
this.peerSyncBuffer.push({ fromId, content, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
3792
|
-
}
|
|
3810
|
+
this.peerSyncBuffer.push({ fromId, content, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
3793
3811
|
this.emit("peer-sync-received", { fromId, content });
|
|
3812
|
+
this.context.addMessage({
|
|
3813
|
+
role: "user",
|
|
3814
|
+
content: `[SYSTEM_NOTIFICATION]: You received a new peer message from ${fromId}. Use the "peer_message" tool with action="receive" to read it.`
|
|
3815
|
+
}).catch(() => {
|
|
3816
|
+
});
|
|
3794
3817
|
}
|
|
3795
3818
|
// ── Private ──────────────────────────────────
|
|
3796
3819
|
async runAgentLoop(systemPrompt, tools) {
|
|
@@ -3921,7 +3944,11 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
3921
3944
|
sendPeerSync: (to, syncType, content) => {
|
|
3922
3945
|
this.peerBus?.send(this.id, to, syncType, this.assignment?.subtaskId ?? "", content);
|
|
3923
3946
|
},
|
|
3924
|
-
getPeerMessages: () =>
|
|
3947
|
+
getPeerMessages: () => {
|
|
3948
|
+
const msgs = [...this.peerSyncBuffer];
|
|
3949
|
+
this.peerSyncBuffer = [];
|
|
3950
|
+
return msgs;
|
|
3951
|
+
}
|
|
3925
3952
|
});
|
|
3926
3953
|
if (this.audit) {
|
|
3927
3954
|
this.audit.toolCall(this.id, tc.name, tc.input);
|
|
@@ -4463,13 +4490,17 @@ var T2Manager = class extends BaseTier {
|
|
|
4463
4490
|
}
|
|
4464
4491
|
// ── Private ──────────────────────────────────
|
|
4465
4492
|
async decomposeSection(assignment) {
|
|
4493
|
+
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");
|
|
4466
4494
|
const prompt = `Decompose this section into 2-5 concrete subtasks for T3 workers.
|
|
4467
4495
|
|
|
4468
4496
|
Section: ${assignment.sectionTitle}
|
|
4469
4497
|
Description: ${assignment.description}
|
|
4470
4498
|
Expected output: ${assignment.expectedOutput}
|
|
4471
4499
|
Constraints: ${assignment.constraints.join("; ")}
|
|
4472
|
-
|
|
4500
|
+
${peerPlans ? `
|
|
4501
|
+
Context from sibling T2 plans (use this to align execution and avoid overlaps):
|
|
4502
|
+
${peerPlans}
|
|
4503
|
+
` : ""}
|
|
4473
4504
|
Return a JSON array of subtask objects, each with:
|
|
4474
4505
|
- subtaskId: string (unique)
|
|
4475
4506
|
- subtaskTitle: string
|
|
@@ -4683,9 +4714,14 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
4683
4714
|
const completed = results.filter((r) => r.status === "COMPLETED");
|
|
4684
4715
|
if (!completed.length) return `Section ${assignment.sectionTitle} failed \u2014 no T3 workers completed.`;
|
|
4685
4716
|
const outputs = completed.map((r, i) => `[T3-${i + 1}]: ${r.output}`).join("\n\n");
|
|
4717
|
+
const peerOutputs = this.peerSyncBuffer.filter((p) => p.content?.type === "T2_SECTION_OUTPUT").map((p) => `[Peer ${p.fromId} Output]: ${p.content.output}`).join("\n\n");
|
|
4686
4718
|
const prompt = `Summarize these T3 worker outputs for section "${assignment.sectionTitle}" in 2-3 sentences:
|
|
4687
4719
|
|
|
4688
|
-
${outputs}
|
|
4720
|
+
${outputs}
|
|
4721
|
+
${peerOutputs ? `
|
|
4722
|
+
|
|
4723
|
+
Context from sibling T2 completed sections (use this to ensure your summary aligns with the overall state):
|
|
4724
|
+
${peerOutputs}` : ""}`;
|
|
4689
4725
|
const messages = [{ role: "user", content: prompt }];
|
|
4690
4726
|
try {
|
|
4691
4727
|
const result = await this.router.generate("T2", {
|
|
@@ -7303,12 +7339,17 @@ var SlashCommandRegistry = class {
|
|
|
7303
7339
|
});
|
|
7304
7340
|
this.register({
|
|
7305
7341
|
command: "/model",
|
|
7342
|
+
description: "Pick a provider and model for a tier (interactive)",
|
|
7343
|
+
handler: (_args, ctx) => ({ output: ctx.onModelPicker(), handled: true })
|
|
7344
|
+
});
|
|
7345
|
+
this.register({
|
|
7346
|
+
command: "/model-info",
|
|
7306
7347
|
description: "Show active models per tier",
|
|
7307
7348
|
handler: (_args, ctx) => ({ output: ctx.onModelInfo(), handled: true })
|
|
7308
7349
|
});
|
|
7309
7350
|
this.register({
|
|
7310
7351
|
command: "/models",
|
|
7311
|
-
description: "
|
|
7352
|
+
description: "Browse available models by provider",
|
|
7312
7353
|
handler: (_args, ctx) => ({ output: ctx.onModelsInfo(), handled: true })
|
|
7313
7354
|
});
|
|
7314
7355
|
this.register({
|
|
@@ -7611,55 +7652,140 @@ function ApprovalPrompt({ request, theme, onDecision }) {
|
|
|
7611
7652
|
}
|
|
7612
7653
|
);
|
|
7613
7654
|
}
|
|
7614
|
-
var
|
|
7615
|
-
|
|
7616
|
-
|
|
7655
|
+
var TIERS = [
|
|
7656
|
+
{ id: "T1", label: "T1 \u2014 Administrator", hint: "complex reasoning \xB7 runs once per task" },
|
|
7657
|
+
{ id: "T2", label: "T2 \u2014 Manager", hint: "per-section planning \xB7 a few calls per task" },
|
|
7658
|
+
{ id: "T3", label: "T3 \u2014 Worker", hint: "high volume \xB7 many parallel runs" }
|
|
7659
|
+
];
|
|
7660
|
+
var ModelsDisplay = ({
|
|
7661
|
+
providers,
|
|
7662
|
+
modelsByProvider,
|
|
7663
|
+
onSelect,
|
|
7664
|
+
onClose
|
|
7665
|
+
}) => {
|
|
7666
|
+
const [step, setStep] = useState("PROVIDER");
|
|
7667
|
+
const [cursor, setCursor] = useState(0);
|
|
7668
|
+
const [picked, setPicked] = useState({});
|
|
7669
|
+
const providerItems = useMemo(() => {
|
|
7670
|
+
const base = [
|
|
7671
|
+
{ label: "\u25C7 Auto", sublabel: "let Cascade pick per tier \u2014 recommended", value: "auto" }
|
|
7672
|
+
];
|
|
7673
|
+
for (const p of providers) {
|
|
7674
|
+
const count = modelsByProvider.get(p)?.length ?? 0;
|
|
7675
|
+
base.push({ label: p, sublabel: `${count} model${count === 1 ? "" : "s"} discovered`, value: p });
|
|
7676
|
+
}
|
|
7677
|
+
return base;
|
|
7678
|
+
}, [providers, modelsByProvider]);
|
|
7679
|
+
const tierItems = useMemo(
|
|
7680
|
+
() => TIERS.map((t) => ({ label: t.label, sublabel: t.hint, value: t.id })),
|
|
7681
|
+
[]
|
|
7682
|
+
);
|
|
7683
|
+
const modelItems = useMemo(() => {
|
|
7684
|
+
if (!picked.provider || picked.provider === "auto") return [];
|
|
7685
|
+
const list = (modelsByProvider.get(picked.provider) ?? []).slice().sort((a, b) => a.id.localeCompare(b.id));
|
|
7686
|
+
const items = [
|
|
7687
|
+
{ label: "\u25C7 Auto", sublabel: "best available from this provider", value: "__auto__" }
|
|
7688
|
+
];
|
|
7689
|
+
for (const m of list) {
|
|
7690
|
+
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`;
|
|
7691
|
+
items.push({ label: m.name, sublabel: `${m.id} \xB7 ${ctx}`, value: m.id });
|
|
7692
|
+
}
|
|
7693
|
+
return items;
|
|
7694
|
+
}, [picked.provider, modelsByProvider]);
|
|
7695
|
+
const currentItems = step === "PROVIDER" ? providerItems : step === "TIER" ? tierItems : modelItems;
|
|
7696
|
+
useInput((input, key) => {
|
|
7617
7697
|
if (key.escape) {
|
|
7618
|
-
if (
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7698
|
+
if (step === "MODEL") {
|
|
7699
|
+
setStep("TIER");
|
|
7700
|
+
setCursor(0);
|
|
7701
|
+
return;
|
|
7702
|
+
}
|
|
7703
|
+
if (step === "TIER") {
|
|
7704
|
+
setStep("PROVIDER");
|
|
7705
|
+
setCursor(0);
|
|
7706
|
+
return;
|
|
7622
7707
|
}
|
|
7708
|
+
onClose();
|
|
7709
|
+
return;
|
|
7623
7710
|
}
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7711
|
+
if (key.upArrow || input === "k") {
|
|
7712
|
+
setCursor((c) => currentItems.length === 0 ? 0 : (c - 1 + currentItems.length) % currentItems.length);
|
|
7713
|
+
return;
|
|
7714
|
+
}
|
|
7715
|
+
if (key.downArrow || key.tab || input === "j") {
|
|
7716
|
+
setCursor((c) => currentItems.length === 0 ? 0 : (c + 1) % currentItems.length);
|
|
7717
|
+
return;
|
|
7718
|
+
}
|
|
7719
|
+
if (/^[1-9]$/.test(input)) {
|
|
7720
|
+
const idx = parseInt(input, 10) - 1;
|
|
7721
|
+
if (idx < currentItems.length) setCursor(idx);
|
|
7722
|
+
return;
|
|
7723
|
+
}
|
|
7724
|
+
if (key.return) {
|
|
7725
|
+
const selected = currentItems[cursor];
|
|
7726
|
+
if (!selected) return;
|
|
7727
|
+
if (step === "PROVIDER") {
|
|
7728
|
+
if (selected.value === "auto") {
|
|
7729
|
+
setPicked({ provider: "auto" });
|
|
7730
|
+
setStep("TIER");
|
|
7731
|
+
setCursor(0);
|
|
7732
|
+
return;
|
|
7641
7733
|
}
|
|
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: () => {
|
|
7734
|
+
setPicked({ provider: selected.value });
|
|
7735
|
+
setStep("TIER");
|
|
7736
|
+
setCursor(0);
|
|
7737
|
+
return;
|
|
7738
|
+
}
|
|
7739
|
+
if (step === "TIER") {
|
|
7740
|
+
const tier = selected.value;
|
|
7741
|
+
if (picked.provider === "auto") {
|
|
7742
|
+
onSelect?.({ kind: "auto", tier });
|
|
7743
|
+
onClose();
|
|
7744
|
+
return;
|
|
7660
7745
|
}
|
|
7746
|
+
setPicked((p) => ({ ...p, tier }));
|
|
7747
|
+
setStep("MODEL");
|
|
7748
|
+
setCursor(0);
|
|
7749
|
+
return;
|
|
7661
7750
|
}
|
|
7662
|
-
|
|
7751
|
+
if (!picked.tier || !picked.provider || picked.provider === "auto") {
|
|
7752
|
+
onClose();
|
|
7753
|
+
return;
|
|
7754
|
+
}
|
|
7755
|
+
if (selected.value === "__auto__") {
|
|
7756
|
+
onSelect?.({ kind: "auto", tier: picked.tier });
|
|
7757
|
+
} else {
|
|
7758
|
+
onSelect?.({
|
|
7759
|
+
kind: "pick",
|
|
7760
|
+
tier: picked.tier,
|
|
7761
|
+
provider: picked.provider,
|
|
7762
|
+
modelId: selected.value
|
|
7763
|
+
});
|
|
7764
|
+
}
|
|
7765
|
+
onClose();
|
|
7766
|
+
}
|
|
7767
|
+
});
|
|
7768
|
+
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}`;
|
|
7769
|
+
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}`;
|
|
7770
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
|
|
7771
|
+
/* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", children: [
|
|
7772
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: title }),
|
|
7773
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: "[Esc back \xB7 Enter select]" })
|
|
7774
|
+
] }),
|
|
7775
|
+
/* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
7776
|
+
breadcrumb,
|
|
7777
|
+
" \xB7 \u2191/\u2193 navigate \xB7 1\u20139 jump"
|
|
7778
|
+
] }) }),
|
|
7779
|
+
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) => {
|
|
7780
|
+
const focused = i === cursor;
|
|
7781
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
|
|
7782
|
+
/* @__PURE__ */ jsx(Text, { color: focused ? "green" : "gray", children: focused ? "\u276F " : " " }),
|
|
7783
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
7784
|
+
/* @__PURE__ */ jsx(Text, { color: focused ? "white" : "gray", bold: focused, children: item.label }),
|
|
7785
|
+
item.sublabel && /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: ` ${item.sublabel}` })
|
|
7786
|
+
] })
|
|
7787
|
+
] }, `${step}-${item.value}-${i}`);
|
|
7788
|
+
}) })
|
|
7663
7789
|
] });
|
|
7664
7790
|
};
|
|
7665
7791
|
function CostTracker({
|
|
@@ -7924,6 +8050,40 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName })
|
|
|
7924
8050
|
const persistMessage = useCallback((role, content, timestamp) => {
|
|
7925
8051
|
storeRef.current?.addMessage({ id: randomUUID(), sessionId: sessionIdRef.current, role, content, timestamp });
|
|
7926
8052
|
}, []);
|
|
8053
|
+
const applyModelPick = useCallback(async (sel) => {
|
|
8054
|
+
const tierKey = sel.tier.toLowerCase();
|
|
8055
|
+
const tierLabel = sel.tier;
|
|
8056
|
+
if (sel.kind === "auto") {
|
|
8057
|
+
delete config.models[tierKey];
|
|
8058
|
+
} else {
|
|
8059
|
+
config.models[tierKey] = sel.modelId;
|
|
8060
|
+
}
|
|
8061
|
+
try {
|
|
8062
|
+
const router = cascadeRef.current?.getRouter();
|
|
8063
|
+
if (router && sel.kind === "pick") {
|
|
8064
|
+
const candidate = (cachedModels.get(sel.provider) ?? []).find((m) => m.id === sel.modelId);
|
|
8065
|
+
if (candidate) router.overrideTierModel(tierLabel, candidate);
|
|
8066
|
+
}
|
|
8067
|
+
} catch {
|
|
8068
|
+
}
|
|
8069
|
+
const configPath = path17.join(workspacePath, ".cascade", "config.json");
|
|
8070
|
+
try {
|
|
8071
|
+
await fs7.mkdir(path17.dirname(configPath), { recursive: true });
|
|
8072
|
+
await fs7.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
8073
|
+
} catch (err) {
|
|
8074
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
8075
|
+
dispatch({
|
|
8076
|
+
type: "ADD_MESSAGE",
|
|
8077
|
+
message: { id: randomUUID(), role: "error", content: `Failed to persist model selection: ${msg}`, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
8078
|
+
});
|
|
8079
|
+
return;
|
|
8080
|
+
}
|
|
8081
|
+
const summary = sel.kind === "auto" ? `${tierLabel} \u2192 Auto (Cascade will pick best available).` : `${tierLabel} \u2192 ${sel.modelId} (provider: ${sel.provider}).`;
|
|
8082
|
+
dispatch({
|
|
8083
|
+
type: "ADD_MESSAGE",
|
|
8084
|
+
message: { id: randomUUID(), role: "system", content: `\u2714 Model updated \u2014 ${summary}`, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
8085
|
+
});
|
|
8086
|
+
}, [config, workspacePath, cachedModels]);
|
|
7927
8087
|
const globalStoreRef = useRef(null);
|
|
7928
8088
|
const persistRuntimeSession = useCallback((status, latestPrompt) => {
|
|
7929
8089
|
const runtimeSession = { sessionId: sessionIdRef.current, title: sessionTitle, workspacePath, status, startedAt: startedAtRef.current, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), latestPrompt, isGlobal: false };
|
|
@@ -8070,6 +8230,10 @@ ${msg.content}`).join("\n\n");
|
|
|
8070
8230
|
return `${t}: ${m?.name ?? "none"} (${m?.provider ?? "\u2014"})${configured ? ` | configured: ${configured}` : ""}`;
|
|
8071
8231
|
}).join("\n");
|
|
8072
8232
|
},
|
|
8233
|
+
onModelPicker: async () => {
|
|
8234
|
+
setIsShowingModels(true);
|
|
8235
|
+
return "Opening model picker \u2014 choose provider \u2192 tier \u2192 model (ESC to exit).";
|
|
8236
|
+
},
|
|
8073
8237
|
onModelsInfo: async () => {
|
|
8074
8238
|
setIsShowingModels(true);
|
|
8075
8239
|
return "Opening interactive models explorer... (ESC to exit)";
|
|
@@ -8352,6 +8516,16 @@ Use /identity <name|id> to switch.`;
|
|
|
8352
8516
|
}
|
|
8353
8517
|
}, [handleSlashCommand, persistMessage, state.messages, workspacePath, rebuildTree, recordNodeEvent, slashCompletions, slashIndex, state.isExecuting]);
|
|
8354
8518
|
useInput((_input, key) => {
|
|
8519
|
+
if (isShowingModels) {
|
|
8520
|
+
if (key.ctrl && _input === "c") {
|
|
8521
|
+
if (quitAttempted) {
|
|
8522
|
+
exit();
|
|
8523
|
+
return;
|
|
8524
|
+
}
|
|
8525
|
+
setQuitAttempted(true);
|
|
8526
|
+
}
|
|
8527
|
+
return;
|
|
8528
|
+
}
|
|
8355
8529
|
if (key.ctrl && _input === "c") {
|
|
8356
8530
|
if (quitAttempted) {
|
|
8357
8531
|
exit();
|
|
@@ -8520,7 +8694,17 @@ Use /identity <name|id> to switch.`;
|
|
|
8520
8694
|
] }) }),
|
|
8521
8695
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
|
|
8522
8696
|
state.messages.length === 0 && !state.isStreaming && /* @__PURE__ */ jsx(WelcomeBanner, { theme, config, workspacePath, sessionId: sessionIdRef.current }),
|
|
8523
|
-
isShowingModels && /* @__PURE__ */ jsx(
|
|
8697
|
+
isShowingModels && /* @__PURE__ */ jsx(
|
|
8698
|
+
ModelsDisplay,
|
|
8699
|
+
{
|
|
8700
|
+
providers: config.providers.map((p) => p.type),
|
|
8701
|
+
modelsByProvider: cachedModels,
|
|
8702
|
+
onSelect: (sel) => {
|
|
8703
|
+
void applyModelPick(sel);
|
|
8704
|
+
},
|
|
8705
|
+
onClose: () => setIsShowingModels(false)
|
|
8706
|
+
}
|
|
8707
|
+
)
|
|
8524
8708
|
] }),
|
|
8525
8709
|
/* @__PURE__ */ jsx(AgentTree, { root: state.agentTree, theme }),
|
|
8526
8710
|
state.showDetails && /* @__PURE__ */ jsx(TimelinePanel, { nodes: [...treeNodesRef.current.values()], theme, currentIndex: timelineIndex, onChangeIndex: setTimelineIndex }),
|
|
@@ -8572,7 +8756,7 @@ Use /identity <name|id> to switch.`;
|
|
|
8572
8756
|
/* @__PURE__ */ jsx(
|
|
8573
8757
|
SafeTextInput,
|
|
8574
8758
|
{
|
|
8575
|
-
focus: !state.approvalRequest,
|
|
8759
|
+
focus: !state.approvalRequest && !isShowingModels,
|
|
8576
8760
|
value: input,
|
|
8577
8761
|
manageMouseReporting: false,
|
|
8578
8762
|
onChange: (val) => {
|
|
@@ -8765,6 +8949,7 @@ var PROVIDER_LABELS = {
|
|
|
8765
8949
|
ollama: "Ollama (local)",
|
|
8766
8950
|
"openai-compatible": "OpenAI-Compatible"
|
|
8767
8951
|
};
|
|
8952
|
+
var providerOrder = ["anthropic", "openai", "gemini", "azure", "ollama", "openai-compatible"];
|
|
8768
8953
|
function buildInitialEntries(types) {
|
|
8769
8954
|
return [...types].map((type) => ({
|
|
8770
8955
|
id: randomUUID(),
|
|
@@ -8780,6 +8965,17 @@ function wizardReducer(state, action) {
|
|
|
8780
8965
|
else next.add(action.provider);
|
|
8781
8966
|
return { ...state, selectedTypes: next };
|
|
8782
8967
|
}
|
|
8968
|
+
case "TOGGLE_ALL": {
|
|
8969
|
+
const allSelected = state.selectedTypes.size === providerOrder.length;
|
|
8970
|
+
return { ...state, selectedTypes: allSelected ? /* @__PURE__ */ new Set() : new Set(providerOrder) };
|
|
8971
|
+
}
|
|
8972
|
+
case "INVERT_SELECTION": {
|
|
8973
|
+
const next = /* @__PURE__ */ new Set();
|
|
8974
|
+
providerOrder.forEach((p) => {
|
|
8975
|
+
if (!state.selectedTypes.has(p)) next.add(p);
|
|
8976
|
+
});
|
|
8977
|
+
return { ...state, selectedTypes: next };
|
|
8978
|
+
}
|
|
8783
8979
|
case "CONFIRM_PROVIDERS": {
|
|
8784
8980
|
const entries = buildInitialEntries(state.selectedTypes);
|
|
8785
8981
|
return { ...state, entries, currentEntryIdx: 0, step: "API_KEYS" };
|
|
@@ -8867,7 +9063,6 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
8867
9063
|
tierSelectFocus: "T1",
|
|
8868
9064
|
error: null
|
|
8869
9065
|
});
|
|
8870
|
-
const providerOrder = ["anthropic", "openai", "gemini", "azure", "ollama", "openai-compatible"];
|
|
8871
9066
|
const [providerCursor, setProviderCursor] = useState(0);
|
|
8872
9067
|
const [fieldBuffer, setFieldBuffer] = useState("");
|
|
8873
9068
|
const [fieldStage, setFieldStage] = useState("apiKey");
|
|
@@ -8960,6 +9155,8 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
8960
9155
|
if (key.upArrow) setProviderCursor((p) => Math.max(0, p - 1));
|
|
8961
9156
|
if (key.downArrow) setProviderCursor((p) => Math.min(providerOrder.length - 1, p + 1));
|
|
8962
9157
|
if (_input === " ") dispatch({ type: "TOGGLE_PROVIDER", provider: providerOrder[providerCursor] });
|
|
9158
|
+
if (_input === "a") dispatch({ type: "TOGGLE_ALL" });
|
|
9159
|
+
if (_input === "i") dispatch({ type: "INVERT_SELECTION" });
|
|
8963
9160
|
if (key.return) {
|
|
8964
9161
|
if (state.selectedTypes.size === 0) return;
|
|
8965
9162
|
dispatch({ type: "CONFIRM_PROVIDERS" });
|
|
@@ -9021,21 +9218,23 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9021
9218
|
}, [currentEntry, fieldStage]);
|
|
9022
9219
|
if (state.step === "PROVIDER_SELECT") {
|
|
9023
9220
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9024
|
-
/* @__PURE__ */
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9221
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
9222
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "? " }),
|
|
9223
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Which providers do you want to configure?" })
|
|
9224
|
+
] }),
|
|
9225
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", children: providerOrder.map((p, i) => {
|
|
9028
9226
|
const selected = state.selectedTypes.has(p);
|
|
9029
9227
|
const focused = i === providerCursor;
|
|
9030
9228
|
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] }),
|
|
9229
|
+
/* @__PURE__ */ jsx(Text, { color: focused ? "magenta" : "white", children: focused ? "\u276F " : " " }),
|
|
9230
|
+
/* @__PURE__ */ jsx(Text, { color: selected ? "green" : "white", children: selected ? "\u25C9 " : "\u25EF " }),
|
|
9231
|
+
/* @__PURE__ */ jsx(Text, { color: focused ? "magenta" : selected ? "white" : "gray", children: PROVIDER_LABELS[p] }),
|
|
9034
9232
|
p === "azure" && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2014 multiple deployments supported" }),
|
|
9035
9233
|
p === "openai-compatible" && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2014 Groq, Together, custom" }),
|
|
9036
9234
|
p === "ollama" && /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2014 no API key needed" })
|
|
9037
9235
|
] }, p);
|
|
9038
9236
|
}) }),
|
|
9237
|
+
/* @__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
9238
|
state.error && /* @__PURE__ */ jsx(Text, { color: "red", children: state.error })
|
|
9040
9239
|
] });
|
|
9041
9240
|
}
|
|
@@ -9045,15 +9244,19 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9045
9244
|
const isOllama = currentEntry.type === "ollama";
|
|
9046
9245
|
if (fieldStage === "askMore") {
|
|
9047
9246
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9048
|
-
/* @__PURE__ */
|
|
9049
|
-
|
|
9050
|
-
|
|
9247
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
9248
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "? " }),
|
|
9249
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: isAzure ? "Add another Azure deployment? (y/n)" : "Add another custom endpoint? (y/n)" })
|
|
9250
|
+
] }),
|
|
9251
|
+
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
|
|
9051
9252
|
SelectInput,
|
|
9052
9253
|
{
|
|
9053
9254
|
items: [
|
|
9054
9255
|
{ label: "Yes \u2014 add another", value: "yes" },
|
|
9055
9256
|
{ label: "No \u2014 continue", value: "no" }
|
|
9056
9257
|
],
|
|
9258
|
+
indicatorComponent: ({ isSelected }) => /* @__PURE__ */ jsx(Text, { color: "magenta", children: isSelected ? "\u276F " : " " }),
|
|
9259
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ jsx(Text, { color: isSelected ? "magenta" : "white", children: label }),
|
|
9057
9260
|
onSelect: (item) => {
|
|
9058
9261
|
if (item.value === "yes") {
|
|
9059
9262
|
if (isAzure) dispatch({ type: "ADD_AZURE" });
|
|
@@ -9070,21 +9273,15 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9070
9273
|
) })
|
|
9071
9274
|
] });
|
|
9072
9275
|
}
|
|
9073
|
-
const prompt = isAzure && fieldStage === "deploymentName" ? `Azure deployment name (${currentEntry.label})
|
|
9276
|
+
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
9277
|
const isMasked = fieldStage === "apiKey";
|
|
9075
9278
|
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: "> " }),
|
|
9279
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
9280
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "? " }),
|
|
9281
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: prompt })
|
|
9282
|
+
] }),
|
|
9283
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
9284
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", children: "\u276F " }),
|
|
9088
9285
|
/* @__PURE__ */ jsx(
|
|
9089
9286
|
SafeTextInput,
|
|
9090
9287
|
{
|
|
@@ -9102,13 +9299,13 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9102
9299
|
}
|
|
9103
9300
|
if (state.step === "FETCH_MODELS") {
|
|
9104
9301
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9105
|
-
/* @__PURE__ */
|
|
9106
|
-
|
|
9302
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
9303
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "? " }),
|
|
9304
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Connecting to providers and fetching models..." })
|
|
9305
|
+
] }),
|
|
9306
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
9107
9307
|
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
|
-
] })
|
|
9308
|
+
state.fetchedModels.length === 0 && /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Spinner, { type: "dots" }) })
|
|
9112
9309
|
] })
|
|
9113
9310
|
] });
|
|
9114
9311
|
}
|
|
@@ -9123,41 +9320,55 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
9123
9320
|
const tierLabel = (tier, hint) => {
|
|
9124
9321
|
const isFocused = state.tierSelectFocus === tier;
|
|
9125
9322
|
const current = tier === "T1" ? state.tierT1 : tier === "T2" ? state.tierT2 : state.tierT3;
|
|
9323
|
+
if (!isFocused) {
|
|
9324
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
9325
|
+
/* @__PURE__ */ jsx(Text, { color: "green", bold: true, children: "\u2714 " }),
|
|
9326
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
9327
|
+
tier,
|
|
9328
|
+
" ",
|
|
9329
|
+
hint,
|
|
9330
|
+
": "
|
|
9331
|
+
] }),
|
|
9332
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", children: current === "auto" ? "Auto \u2014 let Cascade choose best available" : state.fetchedModels.find((m) => m.id === current)?.name || current })
|
|
9333
|
+
] }, tier);
|
|
9334
|
+
}
|
|
9126
9335
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
9127
|
-
/* @__PURE__ */ jsxs(
|
|
9128
|
-
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
current === "auto" ? "Auto" : current
|
|
9336
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
9337
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "? " }),
|
|
9338
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, children: [
|
|
9339
|
+
tier,
|
|
9340
|
+
" ",
|
|
9341
|
+
hint,
|
|
9342
|
+
": "
|
|
9343
|
+
] })
|
|
9136
9344
|
] }),
|
|
9137
|
-
|
|
9345
|
+
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
|
|
9138
9346
|
SelectInput,
|
|
9139
9347
|
{
|
|
9140
9348
|
items: modelOptions,
|
|
9141
|
-
onSelect: (item) => dispatch({ type: "SET_TIER", tier, value: item.value })
|
|
9349
|
+
onSelect: (item) => dispatch({ type: "SET_TIER", tier, value: item.value }),
|
|
9350
|
+
indicatorComponent: ({ isSelected }) => /* @__PURE__ */ jsx(Text, { color: "magenta", children: isSelected ? "\u276F " : " " }),
|
|
9351
|
+
itemComponent: ({ isSelected, label }) => /* @__PURE__ */ jsx(Text, { color: isSelected ? "magenta" : "white", children: label })
|
|
9142
9352
|
}
|
|
9143
|
-
)
|
|
9353
|
+
) })
|
|
9144
9354
|
] }, tier);
|
|
9145
9355
|
};
|
|
9146
9356
|
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: [
|
|
9357
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
9150
9358
|
tierLabel("T1", "(Administrator \u2014 complex reasoning, runs once per task)"),
|
|
9151
9359
|
tierLabel("T2", "(Manager \u2014 runs per section)"),
|
|
9152
9360
|
tierLabel("T3", "(Worker \u2014 high volume, many parallel runs)")
|
|
9153
9361
|
] }),
|
|
9154
|
-
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "
|
|
9362
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(Tab/Arrow Down to skip tier, Enter to select or save)" }) }),
|
|
9155
9363
|
state.error && /* @__PURE__ */ jsx(Text, { color: "red", children: state.error })
|
|
9156
9364
|
] });
|
|
9157
9365
|
}
|
|
9158
9366
|
if (state.step === "SAVE") {
|
|
9159
9367
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
9160
|
-
/* @__PURE__ */
|
|
9368
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
9369
|
+
/* @__PURE__ */ jsx(Text, { color: "green", bold: true, children: "\u2714 " }),
|
|
9370
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Setup complete!" })
|
|
9371
|
+
] }),
|
|
9161
9372
|
/* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
|
|
9162
9373
|
/* @__PURE__ */ jsx(Spinner, { type: "dots" }),
|
|
9163
9374
|
/* @__PURE__ */ jsx(Text, { children: " Writing .cascade/config.json..." })
|