diff --git a/apps/cli/package.json b/apps/cli/package.json index 55caa10a846..b303bc6b0f7 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -8,9 +8,9 @@ "node": ">=16" }, "scripts": { - "build": "tsc", + "build": "bun build src/cli.tsx --outfile=dist/cli.js --target=node --packages=bundle", "dev": "tsc --watch", - "start": "bun server.ts", + "start": "bun src/server.ts", "typecheck": "tsc --noEmit", "test": "prettier --check . && xo && ava", "lint": "biome check --write .", @@ -22,7 +22,10 @@ "dist" ], "dependencies": { + "commander": "^14.0.1", "ink": "^6.5.0", + "ink-select-input": "^6.2.0", + "ink-table": "^3.1.0", "lowdb": "^7.0.1", "meow": "^11.0.0", "react": "^19.1.1" @@ -32,7 +35,7 @@ "@types/react": "^19.1.11", "@vdemedes/prettier-config": "^2.0.1", "ava": "^5.2.0", - "bun-types": "^1.3.2", + "bun-types": "^1.3.1", "chalk": "^5.6.2", "chokidar": "^3.5.3", "eslint-config-xo-react": "^0.27.0", diff --git a/apps/cli/src/cli.tsx b/apps/cli/src/cli.tsx index 50974b97a66..f9bd7c2e02e 100644 --- a/apps/cli/src/cli.tsx +++ b/apps/cli/src/cli.tsx @@ -1,29 +1,263 @@ #!/usr/bin/env node +import { Command } from "commander"; import { render } from "ink"; -import meow from "meow"; import React from "react"; -import App from "./app.js"; - -const cli = meow( - ` - Usage - $ cli - - Options - --name Your name - - Examples - $ cli --name=Jane - Hello, Jane - `, - { - importMeta: import.meta, - flags: { - name: { - type: "string", - }, +import { + AgentCreate, + AgentDelete, + AgentGet, + AgentList, + AgentStop, + AgentStopAll, + ChangeCreate, + ChangeDelete, + ChangeList, + EnvCreate, + EnvDelete, + EnvGet, + EnvList, + WorkspaceCreate, + WorkspaceDelete, + WorkspaceGet, + WorkspaceList, +} from "./commands/index"; +import { AgentType, ProcessType } from "./types/process"; +import { WorkspaceType } from "./types/workspace"; + +const program = new Command(); + +program + .name("superset") + .description( + "Superset CLI - Manage environments, workspaces, agents, and changes", + ) + .version("0.1.0"); + +// Environment commands +const env = program.command("env").description("Manage environments"); + +env + .command("list") + .description("List all environments") + .action(() => { + render( process.exit(0)} />); + }); + +env + .command("get") + .description("Get environment by ID") + .argument("", "Environment ID") + .action((id: string) => { + render( process.exit(0)} />); + }); + +env + .command("create") + .description("Create a new environment") + .action(() => { + render( process.exit(0)} />); + }); + +env + .command("delete") + .description( + "Delete an environment (cascades to workspaces, processes, changes)", + ) + .argument("", "Environment ID") + .action((id: string) => { + render( process.exit(0)} />); + }); + +// Workspace commands +const workspace = program.command("workspace").description("Manage workspaces"); + +workspace + .command("list") + .description("List all workspaces") + .option("--env ", "Filter by environment ID") + .action((options: { env?: string }) => { + render( + process.exit(0)} + />, + ); + }); + +workspace + .command("get") + .description("Get workspace by ID") + .argument("", "Workspace ID") + .action((id: string) => { + render( process.exit(0)} />); + }); + +workspace + .command("create") + .description("Create a new workspace") + .argument("", "Environment ID") + .argument( + "", + `Workspace type (${Object.values(WorkspaceType).join(", ")})`, + ) + .option("--path ", "Path for local workspace") + .action((environmentId: string, type: string, options: { path?: string }) => { + // Validate workspace type + if (!Object.values(WorkspaceType).includes(type as WorkspaceType)) { + console.error( + `Invalid workspace type: ${type}. Must be one of: ${Object.values(WorkspaceType).join(", ")}`, + ); + process.exit(1); + } + + render( + process.exit(0)} + />, + ); + }); + +workspace + .command("delete") + .description("Delete a workspace (cascades to processes and changes)") + .argument("", "Workspace ID") + .action((id: string) => { + render( process.exit(0)} />); + }); + +// Agent/Process commands +const agent = program + .command("agent") + .description("Manage agents and processes"); + +agent + .command("list") + .description("List all agents/processes") + .option("--workspace ", "Filter by workspace ID") + .action((options: { workspace?: string }) => { + render( + process.exit(0)} + />, + ); + }); + +agent + .command("get") + .description("Get agent/process by ID") + .argument("", "Agent/Process ID") + .action((id: string) => { + render( process.exit(0)} />); + }); + +agent + .command("create") + .description("Create a new agent/process") + .argument("", "Workspace ID") + .argument("", `Process type (${Object.values(ProcessType).join(", ")})`) + .option( + "--agent-type ", + `Agent type (${Object.values(AgentType).join(", ")}) - required if type is 'agent'`, + ) + .action( + (workspaceId: string, type: string, options: { agentType?: string }) => { + // Validate process type + if (!Object.values(ProcessType).includes(type as ProcessType)) { + console.error( + `Invalid process type: ${type}. Must be one of: ${Object.values(ProcessType).join(", ")}`, + ); + process.exit(1); + } + + // Validate agent type if provided + let agentType: AgentType | undefined; + if (options.agentType) { + if ( + !Object.values(AgentType).includes(options.agentType as AgentType) + ) { + console.error( + `Invalid agent type: ${options.agentType}. Must be one of: ${Object.values(AgentType).join(", ")}`, + ); + process.exit(1); + } + agentType = options.agentType as AgentType; + } + + render( + process.exit(0)} + />, + ); }, - }, -); + ); + +agent + .command("stop") + .description("Stop an agent/process") + .argument("", "Agent/Process ID") + .action((id: string) => { + render( process.exit(0)} />); + }); + +agent + .command("stop-all") + .description("Stop all agents/processes") + .action(() => { + render( process.exit(0)} />); + }); + +agent + .command("delete") + .description("Delete an agent/process (cascades to agent summaries)") + .argument("", "Agent/Process ID") + .action((id: string) => { + render( process.exit(0)} />); + }); + +// Change commands +const change = program.command("change").description("Manage changes"); + +change + .command("list") + .description("List changes for a workspace") + .argument("", "Workspace ID") + .action((workspaceId: string) => { + render( + process.exit(0)} + />, + ); + }); + +change + .command("create") + .description("Create a new change") + .argument("", "Workspace ID") + .argument("", "Change summary") + .action((workspaceId: string, summary: string) => { + render( + process.exit(0)} + />, + ); + }); + +change + .command("delete") + .description("Delete a change (cascades to file diffs)") + .argument("", "Change ID") + .action((id: string) => { + render( process.exit(0)} />); + }); -render(); +program.parse(); diff --git a/apps/cli/src/commands/agent.tsx b/apps/cli/src/commands/agent.tsx new file mode 100644 index 00000000000..d7a250802bf --- /dev/null +++ b/apps/cli/src/commands/agent.tsx @@ -0,0 +1,362 @@ +import { Box, Text } from "ink"; +import React from "react"; +import Table from "../components/Table"; +import { getDb } from "../lib/db"; +import { ProcessOrchestrator } from "../lib/orchestrators/process-orchestrator"; +import { WorkspaceOrchestrator } from "../lib/orchestrators/workspace-orchestrator"; +import type { AgentType, Process, ProcessType } from "../types/process"; + +// Display type with formatted date strings +type FormattedProcess = Omit & { + createdAt: string; + updatedAt: string; + endedAt: string; +}; + +interface AgentListProps { + workspaceId?: string; + onComplete?: () => void; +} + +export function AgentList({ workspaceId, onComplete }: AgentListProps) { + const [agents, setAgents] = React.useState([]); + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + const loadAgents = async () => { + try { + const db = getDb(); + const orchestrator = new ProcessOrchestrator(db); + const processes = await orchestrator.list(workspaceId); + + if (processes.length === 0) { + setAgents([]); + } else { + // Format dates for display + const formatted = processes.map((p) => ({ + ...p, + createdAt: new Date(p.createdAt).toLocaleString(), + updatedAt: new Date(p.updatedAt).toLocaleString(), + endedAt: p.endedAt ? new Date(p.endedAt).toLocaleString() : "—", + })); + setAgents(formatted); + } + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + loadAgents(); + }, [workspaceId, onComplete]); + + if (loading) { + return Loading agents/processes...; + } + + if (error) { + return Error: {error}; + } + + if (agents.length === 0) { + const message = workspaceId + ? `No agents/processes found for workspace ${workspaceId}` + : "No agents/processes found"; + return ( + + {message}. Create one with: superset agent create <workspace-id> + <type> + + ); + } + + return ( + + + Agents/Processes{workspaceId ? ` (Workspace: ${workspaceId})` : ""} + + + + ); +} + +interface AgentGetProps { + id: string; + onComplete?: () => void; +} + +export function AgentGet({ id, onComplete }: AgentGetProps) { + const [agent, setAgent] = React.useState(null); + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + const loadAgent = async () => { + try { + const db = getDb(); + const orchestrator = new ProcessOrchestrator(db); + const process = await orchestrator.get(id); + + // Format dates + const formatted = { + ...process, + createdAt: new Date(process.createdAt).toLocaleString(), + updatedAt: new Date(process.updatedAt).toLocaleString(), + endedAt: process.endedAt + ? new Date(process.endedAt).toLocaleString() + : "—", + }; + + setAgent(formatted); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + loadAgent(); + }, [id, onComplete]); + + if (loading) { + return Loading agent/process...; + } + + if (error) { + return Error: {error}; + } + + if (!agent) { + return Error: Agent not found; + } + + return ( + + Agent/Process Details +
+ + ); +} + +interface AgentCreateProps { + workspaceId: string; + type: ProcessType; + agentType?: AgentType; + onComplete?: () => void; +} + +export function AgentCreate({ + workspaceId, + type, + agentType, + onComplete, +}: AgentCreateProps) { + const [agent, setAgent] = React.useState(null); + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + const createAgent = async () => { + try { + const db = getDb(); + const workspaceOrchestrator = new WorkspaceOrchestrator(db); + const processOrchestrator = new ProcessOrchestrator(db); + + // Get workspace to pass to create + const workspace = await workspaceOrchestrator.get(workspaceId); + + // Create the process + const process = await processOrchestrator.create( + type, + workspace, + agentType, + ); + + // Format dates + const formatted = { + ...process, + createdAt: new Date(process.createdAt).toLocaleString(), + updatedAt: new Date(process.updatedAt).toLocaleString(), + endedAt: process.endedAt + ? new Date(process.endedAt).toLocaleString() + : "—", + }; + + setAgent(formatted); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + createAgent(); + }, [workspaceId, type, agentType, onComplete]); + + if (loading) { + return Creating agent/process...; + } + + if (error) { + return Error: {error}; + } + + if (!agent) { + return Error: Failed to create agent; + } + + return ( + + ✓ Agent/Process created successfully +
+ + ); +} + +interface AgentStopProps { + id: string; + onComplete?: () => void; +} + +export function AgentStop({ id, onComplete }: AgentStopProps) { + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + const [success, setSuccess] = React.useState(false); + + React.useEffect(() => { + const stopAgent = async () => { + try { + const db = getDb(); + const orchestrator = new ProcessOrchestrator(db); + await orchestrator.stop(id); + setSuccess(true); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + stopAgent(); + }, [id, onComplete]); + + if (loading) { + return Stopping agent/process...; + } + + if (error) { + return Error: {error}; + } + + if (success) { + return ( + + ✓ Agent/Process stopped successfully + ID: {id} + + ); + } + + return null; +} + +interface AgentStopAllProps { + onComplete?: () => void; +} + +export function AgentStopAll({ onComplete }: AgentStopAllProps) { + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + const [success, setSuccess] = React.useState(false); + + React.useEffect(() => { + const stopAll = async () => { + try { + const db = getDb(); + const orchestrator = new ProcessOrchestrator(db); + await orchestrator.stopAll(); + setSuccess(true); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + stopAll(); + }, [onComplete]); + + if (loading) { + return Stopping all agents/processes...; + } + + if (error) { + return Error: {error}; + } + + if (success) { + return ( + ✓ All agents/processes stopped successfully + ); + } + + return null; +} + +interface AgentDeleteProps { + id: string; + onComplete?: () => void; +} + +export function AgentDelete({ id, onComplete }: AgentDeleteProps) { + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + const [success, setSuccess] = React.useState(false); + + React.useEffect(() => { + const deleteAgent = async () => { + try { + const db = getDb(); + const orchestrator = new ProcessOrchestrator(db); + await orchestrator.delete(id); + setSuccess(true); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + deleteAgent(); + }, [id, onComplete]); + + if (loading) { + return Deleting agent/process...; + } + + if (error) { + return Error: {error}; + } + + if (success) { + return ( + + ✓ Agent/Process deleted successfully + ID: {id} + + Note: All associated agent summaries have been removed. + + + ); + } + + return null; +} diff --git a/apps/cli/src/commands/change.tsx b/apps/cli/src/commands/change.tsx new file mode 100644 index 00000000000..79ed92a51cd --- /dev/null +++ b/apps/cli/src/commands/change.tsx @@ -0,0 +1,177 @@ +import { Box, Text } from "ink"; +import React from "react"; +import Table from "../components/Table"; +import { getDb } from "../lib/db"; +import { ChangeOrchestrator } from "../lib/orchestrators/change-orchestrator"; + +interface ChangeListProps { + workspaceId: string; + onComplete?: () => void; +} + +export function ChangeList({ workspaceId, onComplete }: ChangeListProps) { + const [changes, setChanges] = React.useState([]); + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + const loadChanges = async () => { + try { + const db = getDb(); + const orchestrator = new ChangeOrchestrator(db); + const chgs = await orchestrator.list(workspaceId); + + if (chgs.length === 0) { + setChanges([]); + } else { + // Format dates for display + const formatted = chgs.map((c) => ({ + ...c, + createdAt: new Date(c.createdAt).toLocaleString(), + })); + setChanges(formatted); + } + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + loadChanges(); + }, [workspaceId, onComplete]); + + if (loading) { + return Loading changes...; + } + + if (error) { + return Error: {error}; + } + + if (changes.length === 0) { + return ( + + No changes found for workspace {workspaceId}. Create one with: superset + change create {workspaceId} <summary> + + ); + } + + return ( + + Changes (Workspace: {workspaceId}) +
+ + ); +} + +interface ChangeCreateProps { + workspaceId: string; + summary: string; + onComplete?: () => void; +} + +export function ChangeCreate({ + workspaceId, + summary, + onComplete, +}: ChangeCreateProps) { + const [change, setChange] = React.useState(null); + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + const createChange = async () => { + try { + const db = getDb(); + const orchestrator = new ChangeOrchestrator(db); + const chg = await orchestrator.create({ + workspaceId, + summary, + createdAt: new Date(), + }); + + // Format date + const formatted = { + ...chg, + createdAt: new Date(chg.createdAt).toLocaleString(), + }; + + setChange(formatted); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + createChange(); + }, [workspaceId, summary, onComplete]); + + if (loading) { + return Creating change...; + } + + if (error) { + return Error: {error}; + } + + return ( + + ✓ Change created successfully +
+ + ); +} + +interface ChangeDeleteProps { + id: string; + onComplete?: () => void; +} + +export function ChangeDelete({ id, onComplete }: ChangeDeleteProps) { + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + const [success, setSuccess] = React.useState(false); + + React.useEffect(() => { + const deleteChange = async () => { + try { + const db = getDb(); + const orchestrator = new ChangeOrchestrator(db); + await orchestrator.delete(id); + setSuccess(true); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + deleteChange(); + }, [id, onComplete]); + + if (loading) { + return Deleting change...; + } + + if (error) { + return Error: {error}; + } + + if (success) { + return ( + + ✓ Change deleted successfully + ID: {id} + Note: All associated file diffs have been removed. + + ); + } + + return null; +} diff --git a/apps/cli/src/commands/env.tsx b/apps/cli/src/commands/env.tsx new file mode 100644 index 00000000000..f04a6f58ef6 --- /dev/null +++ b/apps/cli/src/commands/env.tsx @@ -0,0 +1,200 @@ +import { Box, Text } from "ink"; +import React from "react"; +import Table from "../components/Table"; +import { getDb } from "../lib/db"; +import { EnvironmentOrchestrator } from "../lib/orchestrators/environment-orchestrator"; + +interface EnvListProps { + onComplete?: () => void; +} + +export function EnvList({ onComplete }: EnvListProps) { + const [environments, setEnvironments] = React.useState([]); + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + const loadEnvironments = async () => { + try { + const db = getDb(); + const orchestrator = new EnvironmentOrchestrator(db); + const envs = await orchestrator.list(); + + if (envs.length === 0) { + setEnvironments([]); + } else { + setEnvironments(envs); + } + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + loadEnvironments(); + }, [onComplete]); + + if (loading) { + return Loading environments...; + } + + if (error) { + return Error: {error}; + } + + if (environments.length === 0) { + return ( + + No environments found. Create one with: superset env create + + ); + } + + return ( + + Environments +
+ + ); +} + +interface EnvGetProps { + id: string; + onComplete?: () => void; +} + +export function EnvGet({ id, onComplete }: EnvGetProps) { + const [environment, setEnvironment] = React.useState(null); + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + const loadEnvironment = async () => { + try { + const db = getDb(); + const orchestrator = new EnvironmentOrchestrator(db); + const env = await orchestrator.get(id); + setEnvironment(env); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + loadEnvironment(); + }, [id, onComplete]); + + if (loading) { + return Loading environment...; + } + + if (error) { + return Error: {error}; + } + + return ( + + Environment Details +
+ + ); +} + +interface EnvCreateProps { + onComplete?: () => void; +} + +export function EnvCreate({ onComplete }: EnvCreateProps) { + const [environment, setEnvironment] = React.useState(null); + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + const createEnvironment = async () => { + try { + const db = getDb(); + const orchestrator = new EnvironmentOrchestrator(db); + const env = await orchestrator.create(); + setEnvironment(env); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + createEnvironment(); + }, [onComplete]); + + if (loading) { + return Creating environment...; + } + + if (error) { + return Error: {error}; + } + + return ( + + ✓ Environment created successfully +
+ + ); +} + +interface EnvDeleteProps { + id: string; + onComplete?: () => void; +} + +export function EnvDelete({ id, onComplete }: EnvDeleteProps) { + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + const [success, setSuccess] = React.useState(false); + + React.useEffect(() => { + const deleteEnvironment = async () => { + try { + const db = getDb(); + const orchestrator = new EnvironmentOrchestrator(db); + await orchestrator.delete(id); + setSuccess(true); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + deleteEnvironment(); + }, [id, onComplete]); + + if (loading) { + return Deleting environment...; + } + + if (error) { + return Error: {error}; + } + + if (success) { + return ( + + ✓ Environment deleted successfully + ID: {id} + + Note: All associated workspaces, processes, and changes have been + removed. + + + ); + } + + return null; +} diff --git a/apps/cli/src/commands/index.ts b/apps/cli/src/commands/index.ts new file mode 100644 index 00000000000..3e5378259fa --- /dev/null +++ b/apps/cli/src/commands/index.ts @@ -0,0 +1,16 @@ +export { + AgentCreate, + AgentDelete, + AgentGet, + AgentList, + AgentStop, + AgentStopAll, +} from "./agent"; +export { ChangeCreate, ChangeDelete, ChangeList } from "./change"; +export { EnvCreate, EnvDelete, EnvGet, EnvList } from "./env"; +export { + WorkspaceCreate, + WorkspaceDelete, + WorkspaceGet, + WorkspaceList, +} from "./workspace"; diff --git a/apps/cli/src/commands/workspace.tsx b/apps/cli/src/commands/workspace.tsx new file mode 100644 index 00000000000..7648183acf3 --- /dev/null +++ b/apps/cli/src/commands/workspace.tsx @@ -0,0 +1,218 @@ +import { Box, Text } from "ink"; +import React from "react"; +import Table from "../components/Table"; +import { getDb } from "../lib/db"; +import { WorkspaceOrchestrator } from "../lib/orchestrators/workspace-orchestrator"; +import type { WorkspaceType } from "../types/workspace"; + +interface WorkspaceListProps { + environmentId?: string; + onComplete?: () => void; +} + +export function WorkspaceList({ + environmentId, + onComplete, +}: WorkspaceListProps) { + const [workspaces, setWorkspaces] = React.useState([]); + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + const loadWorkspaces = async () => { + try { + const db = getDb(); + const orchestrator = new WorkspaceOrchestrator(db); + const ws = await orchestrator.list(environmentId); + + if (ws.length === 0) { + setWorkspaces([]); + } else { + setWorkspaces(ws); + } + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + loadWorkspaces(); + }, [environmentId, onComplete]); + + if (loading) { + return Loading workspaces...; + } + + if (error) { + return Error: {error}; + } + + if (workspaces.length === 0) { + const message = environmentId + ? `No workspaces found for environment ${environmentId}` + : "No workspaces found"; + return ( + + {message}. Create one with: superset workspace create <env-id> + <type> + + ); + } + + return ( + + + Workspaces{environmentId ? ` (Environment: ${environmentId})` : ""} + +
+ + ); +} + +interface WorkspaceGetProps { + id: string; + onComplete?: () => void; +} + +export function WorkspaceGet({ id, onComplete }: WorkspaceGetProps) { + const [workspace, setWorkspace] = React.useState(null); + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + const loadWorkspace = async () => { + try { + const db = getDb(); + const orchestrator = new WorkspaceOrchestrator(db); + const ws = await orchestrator.get(id); + setWorkspace(ws); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + loadWorkspace(); + }, [id, onComplete]); + + if (loading) { + return Loading workspace...; + } + + if (error) { + return Error: {error}; + } + + return ( + + Workspace Details +
+ + ); +} + +interface WorkspaceCreateProps { + environmentId: string; + type: WorkspaceType; + path?: string; + onComplete?: () => void; +} + +export function WorkspaceCreate({ + environmentId, + type, + path, + onComplete, +}: WorkspaceCreateProps) { + const [workspace, setWorkspace] = React.useState(null); + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + + React.useEffect(() => { + const createWorkspace = async () => { + try { + const db = getDb(); + const orchestrator = new WorkspaceOrchestrator(db); + const ws = await orchestrator.create(environmentId, type, path); + setWorkspace(ws); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + createWorkspace(); + }, [environmentId, type, path, onComplete]); + + if (loading) { + return Creating workspace...; + } + + if (error) { + return Error: {error}; + } + + return ( + + ✓ Workspace created successfully +
+ + ); +} + +interface WorkspaceDeleteProps { + id: string; + onComplete?: () => void; +} + +export function WorkspaceDelete({ id, onComplete }: WorkspaceDeleteProps) { + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(true); + const [success, setSuccess] = React.useState(false); + + React.useEffect(() => { + const deleteWorkspace = async () => { + try { + const db = getDb(); + const orchestrator = new WorkspaceOrchestrator(db); + await orchestrator.delete(id); + setSuccess(true); + } catch (err) { + setError(err instanceof Error ? err.message : "Unknown error"); + } finally { + setLoading(false); + onComplete?.(); + } + }; + + deleteWorkspace(); + }, [id, onComplete]); + + if (loading) { + return Deleting workspace...; + } + + if (error) { + return Error: {error}; + } + + if (success) { + return ( + + ✓ Workspace deleted successfully + ID: {id} + + Note: All associated processes and changes have been removed. + + + ); + } + + return null; +} diff --git a/apps/cli/src/components/Table.tsx b/apps/cli/src/components/Table.tsx new file mode 100644 index 00000000000..f0ee80d90a6 --- /dev/null +++ b/apps/cli/src/components/Table.tsx @@ -0,0 +1,32 @@ +import { Box, Text } from "ink"; +import React from "react"; + +interface TableProps { + data: Record[]; +} + +export default function Table({ data }: TableProps) { + if (!data || data.length === 0) { + return null; + } + + const keys = Object.keys(data[0]!); + + return ( + + {data.map((row, index) => ( + + {keys.map((key) => ( + + {key}: {String(row[key])} + + ))} + + ))} + + ); +} diff --git a/apps/cli/src/lib/db.ts b/apps/cli/src/lib/db.ts new file mode 100644 index 00000000000..517d0e69060 --- /dev/null +++ b/apps/cli/src/lib/db.ts @@ -0,0 +1,27 @@ +import { existsSync, mkdirSync } from "node:fs"; +import { homedir } from "node:os"; +import { join } from "node:path"; +import { LowdbAdapter } from "./storage/lowdb-adapter"; + +let adapter: LowdbAdapter | null = null; + +/** + * Get or create the database adapter instance. + * Database is stored at ~/.superset/db.json + */ +export function getDb(): LowdbAdapter { + if (adapter) { + return adapter; + } + + // Create ~/.superset directory if it doesn't exist + const supersetDir = join(homedir(), ".superset"); + if (!existsSync(supersetDir)) { + mkdirSync(supersetDir, { recursive: true }); + } + + const dbPath = join(supersetDir, "db.json"); + adapter = new LowdbAdapter(dbPath); + + return adapter; +} diff --git a/apps/cli/src/lib/orchestrators/__tests__/change-orchestrator.test.ts b/apps/cli/src/lib/orchestrators/__tests__/change-orchestrator.test.ts index 70570d980bc..f0944a0af1b 100644 --- a/apps/cli/src/lib/orchestrators/__tests__/change-orchestrator.test.ts +++ b/apps/cli/src/lib/orchestrators/__tests__/change-orchestrator.test.ts @@ -2,11 +2,11 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { WorkspaceType } from "../../../types/workspace.js"; -import { LowdbAdapter } from "../../storage/lowdb-adapter.js"; -import { ChangeOrchestrator } from "../change-orchestrator.js"; -import { EnvironmentOrchestrator } from "../environment-orchestrator.js"; -import { WorkspaceOrchestrator } from "../workspace-orchestrator.js"; +import { WorkspaceType } from "../../../types/workspace"; +import { LowdbAdapter } from "../../storage/lowdb-adapter"; +import { ChangeOrchestrator } from "../change-orchestrator"; +import { EnvironmentOrchestrator } from "../environment-orchestrator"; +import { WorkspaceOrchestrator } from "../workspace-orchestrator"; describe("ChangeOrchestrator", () => { let tempDir: string; diff --git a/apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts b/apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts index c17b6f5dcf6..cd760ec1767 100644 --- a/apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts +++ b/apps/cli/src/lib/orchestrators/__tests__/environment-orchestrator.test.ts @@ -2,12 +2,12 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { ProcessType } from "../../../types/process.js"; -import { WorkspaceType } from "../../../types/workspace.js"; -import { LowdbAdapter } from "../../storage/lowdb-adapter.js"; -import { EnvironmentOrchestrator } from "../environment-orchestrator.js"; -import { ProcessOrchestrator } from "../process-orchestrator.js"; -import { WorkspaceOrchestrator } from "../workspace-orchestrator.js"; +import { ProcessType } from "../../../types/process"; +import { WorkspaceType } from "../../../types/workspace"; +import { LowdbAdapter } from "../../storage/lowdb-adapter"; +import { EnvironmentOrchestrator } from "../environment-orchestrator"; +import { ProcessOrchestrator } from "../process-orchestrator"; +import { WorkspaceOrchestrator } from "../workspace-orchestrator"; describe("EnvironmentOrchestrator", () => { let tempDir: string; @@ -92,9 +92,7 @@ describe("EnvironmentOrchestrator", () => { }); test("throws error for non-existent environment", async () => { - expect( - orchestrator.update("non-existent", {}), - ).rejects.toThrow(); + expect(orchestrator.update("non-existent", {})).rejects.toThrow(); }); }); diff --git a/apps/cli/src/lib/orchestrators/__tests__/process-orchestrator.test.ts b/apps/cli/src/lib/orchestrators/__tests__/process-orchestrator.test.ts index edfc36855fd..956c8a4bf8e 100644 --- a/apps/cli/src/lib/orchestrators/__tests__/process-orchestrator.test.ts +++ b/apps/cli/src/lib/orchestrators/__tests__/process-orchestrator.test.ts @@ -2,13 +2,13 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import type { Agent } from "../../../types/process.js"; -import { AgentType, ProcessType } from "../../../types/process.js"; -import { WorkspaceType } from "../../../types/workspace.js"; -import { LowdbAdapter } from "../../storage/lowdb-adapter.js"; -import { EnvironmentOrchestrator } from "../environment-orchestrator.js"; -import { ProcessOrchestrator } from "../process-orchestrator.js"; -import { WorkspaceOrchestrator } from "../workspace-orchestrator.js"; +import type { Agent } from "../../../types/process"; +import { AgentType, ProcessType } from "../../../types/process"; +import { WorkspaceType } from "../../../types/workspace"; +import { LowdbAdapter } from "../../storage/lowdb-adapter"; +import { EnvironmentOrchestrator } from "../environment-orchestrator"; +import { ProcessOrchestrator } from "../process-orchestrator"; +import { WorkspaceOrchestrator } from "../workspace-orchestrator"; describe("ProcessOrchestrator", () => { let tempDir: string; diff --git a/apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts b/apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts index 80331fa98e8..55670b865ad 100644 --- a/apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts +++ b/apps/cli/src/lib/orchestrators/__tests__/workspace-orchestrator.test.ts @@ -2,12 +2,12 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import type { LocalWorkspace } from "../../../types/workspace.js"; -import { WorkspaceType } from "../../../types/workspace.js"; -import { LowdbAdapter } from "../../storage/lowdb-adapter.js"; -import { ChangeOrchestrator } from "../change-orchestrator.js"; -import { EnvironmentOrchestrator } from "../environment-orchestrator.js"; -import { WorkspaceOrchestrator } from "../workspace-orchestrator.js"; +import type { LocalWorkspace } from "../../../types/workspace"; +import { WorkspaceType } from "../../../types/workspace"; +import { LowdbAdapter } from "../../storage/lowdb-adapter"; +import { ChangeOrchestrator } from "../change-orchestrator"; +import { EnvironmentOrchestrator } from "../environment-orchestrator"; +import { WorkspaceOrchestrator } from "../workspace-orchestrator"; describe("WorkspaceOrchestrator", () => { let tempDir: string; diff --git a/apps/cli/src/lib/orchestrators/change-orchestrator.ts b/apps/cli/src/lib/orchestrators/change-orchestrator.ts index 1a02832da13..b3df4d8818e 100644 --- a/apps/cli/src/lib/orchestrators/change-orchestrator.ts +++ b/apps/cli/src/lib/orchestrators/change-orchestrator.ts @@ -2,8 +2,8 @@ import { randomUUID } from "node:crypto"; import type { Change, ChangeOrchestrator as IChangeOrchestrator, -} from "../../types/change.js"; -import type { StorageAdapter } from "../storage/adapter.js"; +} from "../../types/change"; +import type { StorageAdapter } from "../storage/adapter"; /** * Change orchestrator implementation using storage adapter @@ -37,7 +37,12 @@ export class ChangeOrchestrator implements IChangeOrchestrator { } // Filter out immutable fields to prevent desync - const { id: _, workspaceId: __, createdAt: ___, ...updatesWithoutImmutable } = updates; + const { + id: _, + workspaceId: __, + createdAt: ___, + ...updatesWithoutImmutable + } = updates; const updated = { ...existing, ...updatesWithoutImmutable }; await this.storage.set("changes", id, updated); diff --git a/apps/cli/src/lib/orchestrators/environment-orchestrator.ts b/apps/cli/src/lib/orchestrators/environment-orchestrator.ts index 53de029aa46..2497d570c79 100644 --- a/apps/cli/src/lib/orchestrators/environment-orchestrator.ts +++ b/apps/cli/src/lib/orchestrators/environment-orchestrator.ts @@ -2,8 +2,8 @@ import { randomUUID } from "node:crypto"; import type { Environment, EnvironmentOrchestrator as IEnvironmentOrchestrator, -} from "../../types/environment.js"; -import type { StorageAdapter } from "../storage/adapter.js"; +} from "../../types/environment"; +import type { StorageAdapter } from "../storage/adapter"; /** * Environment orchestrator implementation using storage adapter diff --git a/apps/cli/src/lib/orchestrators/index.ts b/apps/cli/src/lib/orchestrators/index.ts index 6e69dae4eed..03945b56093 100644 --- a/apps/cli/src/lib/orchestrators/index.ts +++ b/apps/cli/src/lib/orchestrators/index.ts @@ -1,4 +1,4 @@ -export { ChangeOrchestrator } from "./change-orchestrator.js"; -export { EnvironmentOrchestrator } from "./environment-orchestrator.js"; -export { ProcessOrchestrator } from "./process-orchestrator.js"; -export { WorkspaceOrchestrator } from "./workspace-orchestrator.js"; +export { ChangeOrchestrator } from "./change-orchestrator"; +export { EnvironmentOrchestrator } from "./environment-orchestrator"; +export { ProcessOrchestrator } from "./process-orchestrator"; +export { WorkspaceOrchestrator } from "./workspace-orchestrator"; diff --git a/apps/cli/src/lib/orchestrators/process-orchestrator.ts b/apps/cli/src/lib/orchestrators/process-orchestrator.ts index a6a1be089ab..ee7f186e3da 100644 --- a/apps/cli/src/lib/orchestrators/process-orchestrator.ts +++ b/apps/cli/src/lib/orchestrators/process-orchestrator.ts @@ -6,9 +6,9 @@ import { type Process, ProcessType, type Terminal, -} from "../../types/process.js"; -import type { Workspace } from "../../types/workspace.js"; -import type { StorageAdapter } from "../storage/adapter.js"; +} from "../../types/process"; +import type { Workspace } from "../../types/workspace"; +import type { StorageAdapter } from "../storage/adapter"; /** * Process orchestrator implementation using storage adapter @@ -70,7 +70,12 @@ export class ProcessOrchestrator implements IProcessOrchestrator { const existing = await this.get(id); // Filter out immutable fields to prevent desync - const { id: _, workspaceId: __, createdAt: ___, ...updatesWithoutImmutable } = updates; + const { + id: _, + workspaceId: __, + createdAt: ___, + ...updatesWithoutImmutable + } = updates; const updated = { ...existing, diff --git a/apps/cli/src/lib/orchestrators/workspace-orchestrator.ts b/apps/cli/src/lib/orchestrators/workspace-orchestrator.ts index 468dd8c3cd9..53285d99e66 100644 --- a/apps/cli/src/lib/orchestrators/workspace-orchestrator.ts +++ b/apps/cli/src/lib/orchestrators/workspace-orchestrator.ts @@ -4,8 +4,8 @@ import type { LocalWorkspace, Workspace, WorkspaceType, -} from "../../types/workspace.js"; -import type { StorageAdapter } from "../storage/adapter.js"; +} from "../../types/workspace"; +import type { StorageAdapter } from "../storage/adapter"; /** * Workspace orchestrator implementation using storage adapter diff --git a/apps/cli/src/lib/storage/__tests__/lowdb-adapter.test.ts b/apps/cli/src/lib/storage/__tests__/lowdb-adapter.test.ts index 5a8fc710f2b..5febc307525 100644 --- a/apps/cli/src/lib/storage/__tests__/lowdb-adapter.test.ts +++ b/apps/cli/src/lib/storage/__tests__/lowdb-adapter.test.ts @@ -3,9 +3,9 @@ import { existsSync } from "node:fs"; import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import type { Change, Environment, Process } from "../../../types/index.js"; -import { ProcessType } from "../../../types/process.js"; -import { LowdbAdapter } from "../lowdb-adapter.js"; +import type { Change, Environment, Process } from "../../../types/index"; +import { ProcessType } from "../../../types/process"; +import { LowdbAdapter } from "../lowdb-adapter"; describe("LowdbAdapter", () => { let tempDir: string; diff --git a/apps/cli/src/lib/storage/adapter.ts b/apps/cli/src/lib/storage/adapter.ts index e057b6f110d..a3d841240b5 100644 --- a/apps/cli/src/lib/storage/adapter.ts +++ b/apps/cli/src/lib/storage/adapter.ts @@ -1,4 +1,4 @@ -import type { Database } from "./types.js"; +import type { Database } from "./types"; /** * Generic storage adapter interface diff --git a/apps/cli/src/lib/storage/index.ts b/apps/cli/src/lib/storage/index.ts index 916b22617f3..7fc92faa735 100644 --- a/apps/cli/src/lib/storage/index.ts +++ b/apps/cli/src/lib/storage/index.ts @@ -1,5 +1,5 @@ -export type { StorageAdapter } from "./adapter.js"; -export { getDbPath, getStorageDir } from "./config.js"; -export { LowdbAdapter } from "./lowdb-adapter.js"; -export type { Database, SerializedDatabase } from "./types.js"; -export { createEmptyDatabase } from "./types.js"; +export type { StorageAdapter } from "./adapter"; +export { getDbPath, getStorageDir } from "./config"; +export { LowdbAdapter } from "./lowdb-adapter"; +export type { Database, SerializedDatabase } from "./types"; +export { createEmptyDatabase } from "./types"; diff --git a/apps/cli/src/lib/storage/lowdb-adapter.ts b/apps/cli/src/lib/storage/lowdb-adapter.ts index a301ce295aa..8abd9ca3ea4 100644 --- a/apps/cli/src/lib/storage/lowdb-adapter.ts +++ b/apps/cli/src/lib/storage/lowdb-adapter.ts @@ -2,13 +2,13 @@ import { existsSync } from "node:fs"; import { mkdir } from "node:fs/promises"; import { dirname } from "node:path"; import { JSONFilePreset } from "lowdb/node"; -import type { StorageAdapter } from "./adapter.js"; -import { getDbPath } from "./config.js"; +import type { StorageAdapter } from "./adapter"; +import { getDbPath } from "./config"; import { createEmptyDatabase, type Database, type SerializedDatabase, -} from "./types.js"; +} from "./types"; /** * Lowdb implementation of StorageAdapter diff --git a/apps/cli/src/lib/storage/types.ts b/apps/cli/src/lib/storage/types.ts index ead99711518..124d3765d82 100644 --- a/apps/cli/src/lib/storage/types.ts +++ b/apps/cli/src/lib/storage/types.ts @@ -6,7 +6,7 @@ import type { FileDiff, Process, Workspace, -} from "../../types/index.js"; +} from "../../types/index"; /** * Database schema for JSON storage diff --git a/apps/cli/server.ts b/apps/cli/src/server.ts similarity index 95% rename from apps/cli/server.ts rename to apps/cli/src/server.ts index ab733e01726..9f37afd235d 100644 --- a/apps/cli/server.ts +++ b/apps/cli/src/server.ts @@ -10,7 +10,7 @@ function startApp() { } console.log("\x1b[36m[watch] Starting app...\x1b[0m"); - child = spawn("node", ["dist/cli.js"], { + child = spawn("bun", ["dist/cli.js"], { stdio: "inherit", shell: false, }); diff --git a/bun.lock b/bun.lock index 6146f09b4ee..c205b5fdbde 100644 --- a/bun.lock +++ b/bun.lock @@ -4,6 +4,9 @@ "workspaces": { "": { "name": "@superset/repo", + "dependencies": { + "react-devtools-core": "^7.0.1", + }, "devDependencies": { "@biomejs/biome": "^2.2.6", "sherif": "^1.7.0", @@ -44,7 +47,10 @@ "version": "0.0.0", "bin": "dist/cli.js", "dependencies": { + "commander": "^14.0.1", "ink": "^6.5.0", + "ink-select-input": "^6.2.0", + "ink-table": "^3.1.0", "lowdb": "^7.0.1", "meow": "^11.0.0", "react": "^19.1.1", @@ -54,7 +60,7 @@ "@types/react": "^19.1.11", "@vdemedes/prettier-config": "^2.0.1", "ava": "^5.2.0", - "bun-types": "^1.3.2", + "bun-types": "^1.3.1", "chalk": "^5.6.2", "chokidar": "^3.5.3", "eslint-config-xo-react": "^0.27.0", @@ -2187,6 +2193,10 @@ "ink": ["ink@6.5.0", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.2.1", "ansi-escapes": "^7.2.0", "ansi-styles": "^6.2.1", "auto-bind": "^5.0.1", "chalk": "^5.6.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^5.1.1", "code-excerpt": "^4.0.0", "es-toolkit": "^1.39.10", "indent-string": "^5.0.0", "is-in-ci": "^2.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.33.0", "signal-exit": "^3.0.7", "slice-ansi": "^7.1.0", "stack-utils": "^2.0.6", "string-width": "^8.1.0", "type-fest": "^4.27.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0", "ws": "^8.18.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=19.0.0", "react": ">=19.0.0", "react-devtools-core": "^6.1.2" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-abn3rYIxepGKD/h4ZH6sQHgJxBi/EISY/1fIxHODlF5LPvw0wKv2S2uOMIMTfJdBwy9DsWndCfKDCcWSRclp/w=="], + "ink-select-input": ["ink-select-input@6.2.0", "", { "dependencies": { "figures": "^6.1.0", "to-rotated": "^1.0.0" }, "peerDependencies": { "ink": ">=5.0.0", "react": ">=18.0.0" } }, "sha512-304fZXxkpYxJ9si5lxRCaX01GNlmPBgOZumXXRnPYbHW/iI31cgQynqk2tRypGLOF1cMIwPUzL2LSm6q4I5rQQ=="], + + "ink-table": ["ink-table@3.1.0", "", { "dependencies": { "object-hash": "^2.0.3" }, "peerDependencies": { "ink": ">=3.0.0", "react": ">=16.8.0" } }, "sha512-qxVb4DIaEaJryvF9uZGydnmP9Hkmas3DCKVpEcBYC0E4eJd3qNgNe+PZKuzgCERFe9LfAS1TNWxCr9+AU4v3YA=="], + "ink-testing-library": ["ink-testing-library@3.0.0", "", { "peerDependencies": { "@types/react": ">=18.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-ItyyoOmcm6yftb7c5mZI2HU22BWzue8PBbO3DStmY8B9xaqfKr7QJONiWOXcwVsOk/6HuVQ0v7N5xhPaR3jycA=="], "inline-style-parser": ["inline-style-parser@0.2.6", "", {}, "sha512-gtGXVaBdl5mAes3rPcMedEBm12ibjt1kDMFfheul1wUAOVEJW60voNdMVzVkfLN06O7ZaD/rxhfKgtlgtTbMjg=="], @@ -2771,6 +2781,8 @@ "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + "object-hash": ["object-hash@2.2.0", "", {}, "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw=="], + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], @@ -2969,6 +2981,8 @@ "react-compiler-runtime": ["react-compiler-runtime@19.1.0-rc.1-rc-af1b7da-20250421", "", { "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^0.0.0-experimental" } }, "sha512-Til/juI+Zfq+eYpGYn9lFxqW5RyJDs3ThOxmg0757aMrPpfx/Zb0SnGMVJhF3vw+bEQjJiD+xPFD3+kE0WbyeA=="], + "react-devtools-core": ["react-devtools-core@7.0.1", "", { "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" } }, "sha512-C3yNvRHaizlpiASzy7b9vbnBGLrhvdhl1CbdU6EnZgxPNbai60szdLtl+VL76UNOt5bOoVTOz5rNWZxgGt+Gsw=="], + "react-dnd": ["react-dnd@16.0.1", "", { "dependencies": { "@react-dnd/invariant": "^4.0.1", "@react-dnd/shallowequal": "^4.0.1", "dnd-core": "^16.0.1", "fast-deep-equal": "^3.1.3", "hoist-non-react-statics": "^3.3.2" }, "peerDependencies": { "@types/hoist-non-react-statics": ">= 3.3.1", "@types/node": ">= 12", "@types/react": ">= 16", "react": ">= 16.14" }, "optionalPeers": ["@types/hoist-non-react-statics", "@types/node", "@types/react"] }, "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q=="], "react-dnd-html5-backend": ["react-dnd-html5-backend@16.0.1", "", { "dependencies": { "dnd-core": "^16.0.1" } }, "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw=="], @@ -3177,6 +3191,8 @@ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], + "sherif": ["sherif@1.8.0", "", { "optionalDependencies": { "sherif-darwin-arm64": "1.8.0", "sherif-darwin-x64": "1.8.0", "sherif-linux-arm64": "1.8.0", "sherif-linux-arm64-musl": "1.8.0", "sherif-linux-x64": "1.8.0", "sherif-linux-x64-musl": "1.8.0", "sherif-windows-arm64": "1.8.0", "sherif-windows-x64": "1.8.0" }, "bin": { "sherif": "index.js" } }, "sha512-UFCWT1StP477dK8u2Lg17ynTif9mYIi0EgJoIWlU2ErScxctbd4IWrHbpopgwwloANkR2fnloMTjEfKVHTv37Q=="], "sherif-darwin-arm64": ["sherif-darwin-arm64@1.8.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-by2H02Wd9DfL9rEpJ0XIINkvuQYrQNHNAZeakUD0yZFxywlpD8Z0ljAkyhTFDzjRFNyfNwzLXYy/nxg8efgi8A=="], @@ -3369,6 +3385,8 @@ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "to-rotated": ["to-rotated@1.0.0", "", {}, "sha512-KsEID8AfgUy+pxVRLsWp0VzCa69wxzUDZnzGbyIST/bcgcrMvTYoFBX/QORH4YApoD89EDuUovx4BTdpOn319Q=="], + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], "trim-newlines": ["trim-newlines@4.1.1", "", {}, "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ=="], @@ -3565,7 +3583,7 @@ "write-file-atomic": ["write-file-atomic@5.0.1", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw=="], - "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], @@ -3909,6 +3927,10 @@ "ink/react-reconciler": ["react-reconciler@0.33.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA=="], + "ink/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + + "ink-select-input/figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="], + "is-get-set-prop/lowercase-keys": ["lowercase-keys@1.0.1", "", {}, "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="], "is-obj-prop/lowercase-keys": ["lowercase-keys@1.0.1", "", {}, "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="], diff --git a/package.json b/package.json index 4cf9f5e5bd9..2317943e6f7 100644 --- a/package.json +++ b/package.json @@ -38,5 +38,8 @@ "@biomejs/biome": "^2.2.6", "sherif": "^1.7.0", "turbo": "^2.5.8" + }, + "dependencies": { + "react-devtools-core": "^7.0.1" } }