Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { Socket } from "phoenix";
import { LiveSocket } from "phoenix_live_view";
import topbar from "../vendor/topbar";
import { createLiveToastHook } from "../../deps/live_toast/priv/static/live_toast.esm.js";
import { hooks as colocatedHooks } from "../../_build/dev/phoenix-colocated/jido_code/index.js";
const csrfToken = document
.querySelector("meta[name='csrf-token']")
.getAttribute("content");
Expand All @@ -32,6 +33,7 @@ const liveSocket = new LiveSocket("/live", Socket, {
},
hooks: {
LiveToast: createLiveToastHook(),
...colocatedHooks,
},
});
// Show progress bar on live navigation and form submits
Expand Down
4 changes: 2 additions & 2 deletions lib/jido_code/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ defmodule JidoCode.Application do
{AshAuthentication.Supervisor, [otp_app: :jido_code]},
# Forge supervision tree
{Registry, keys: :unique, name: JidoCode.Forge.SessionRegistry},
{DynamicSupervisor, name: JidoCode.Forge.SpriteSupervisor, strategy: :one_for_one},
{DynamicSupervisor, name: JidoCode.Forge.InfraSupervisor, strategy: :one_for_one},
{DynamicSupervisor, name: JidoCode.Forge.ExecSessionSupervisor, strategy: :one_for_one},
JidoCode.Forge.Manager
] ++ forge_dev_children()
Expand All @@ -38,7 +38,7 @@ defmodule JidoCode.Application do
end

if Application.compile_env(:jido_code, :runtime_mode, :prod) in [:dev, :test] do
defp forge_dev_children, do: [{JidoCode.Forge.SpriteClient.Fake, []}]
defp forge_dev_children, do: [{JidoCode.Forge.InfraClient.Fake, []}]
else
defp forge_dev_children, do: []
end
Expand Down
16 changes: 8 additions & 8 deletions lib/jido_code/forge.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ defmodule JidoCode.Forge do
@moduledoc """
Jido Forge - Generic parallel sandbox execution.

Forge manages sprite sessions with pluggable runners.
Forge manages infrastructure sessions with pluggable runners.
"""

alias JidoCode.Forge.{Manager, Operations, SpriteSession}
alias JidoCode.Forge.{Manager, Operations, InfraSession}

defmodule SessionHandle do
@moduledoc """
Expand Down Expand Up @@ -69,7 +69,7 @@ defmodule JidoCode.Forge do
"""
@spec status(String.t()) :: {:ok, map()} | {:error, term()}
def status(session_id) do
SpriteSession.status(session_id)
InfraSession.status(session_id)
end

# Execution
Expand All @@ -79,20 +79,20 @@ defmodule JidoCode.Forge do
"""
@spec run_iteration(String.t(), keyword()) :: {:ok, map()} | {:error, term()}
def run_iteration(session_id, opts \\ []) do
SpriteSession.run_iteration(session_id, opts)
InfraSession.run_iteration(session_id, opts)
end

@doc """
Executes a command directly in the sprite.
Executes a command directly in the environment.
"""
@spec exec(String.t(), String.t(), keyword()) ::
{String.t(), non_neg_integer()} | {:error, term()}
def exec(session_id, command, opts \\ []) do
SpriteSession.exec(session_id, command, opts)
InfraSession.exec(session_id, command, opts)
end

@doc """
Execute a command synchronously in the session's sprite (Sprites-style API).
Execute a command synchronously in the session's environment (Sprites-style API).

Unlike `exec/3` which takes a raw command string, this takes command and args
separately for proper escaping and consistency with the Sprites SDK.
Expand Down Expand Up @@ -129,7 +129,7 @@ defmodule JidoCode.Forge do
"""
@spec apply_input(String.t(), term()) :: :ok | {:error, term()}
def apply_input(session_id, input) do
SpriteSession.apply_input(session_id, input)
InfraSession.apply_input(session_id, input)
end

@doc """
Expand Down
18 changes: 9 additions & 9 deletions lib/jido_code/forge/bootstrap.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
defmodule JidoCode.Forge.Bootstrap do
@moduledoc """
Execute bootstrap steps to set up a sprite environment.
Execute bootstrap steps to set up an infrastructure environment.

Bootstrap steps prepare the sprite for running iterations by executing
Bootstrap steps prepare the environment for running iterations by executing
commands, writing files, and configuring the environment.
"""

Expand All @@ -21,8 +21,8 @@ defmodule JidoCode.Forge.Bootstrap do

## Options

* `:sprite_client` - The sprite client module to use (defaults to JidoCode.Forge.SpriteClient)
* `:sprite_id` - The sprite identifier for command execution
* `:infra_client` - The infra client module to use (defaults to JidoCode.Forge.InfraClient)
* `:infra_id` - The infrastructure identifier for command execution
* `:on_step` - Optional callback `fn step, index -> :ok end` called before each step

## Examples
Expand All @@ -32,7 +32,7 @@ defmodule JidoCode.Forge.Bootstrap do
%{type: "file", path: "config.json", content: "{}"}
]

Bootstrap.execute(client, steps, sprite_id: "abc123")
Bootstrap.execute(client, steps, infra_id: "abc123")
#=> :ok

"""
Expand Down Expand Up @@ -63,9 +63,9 @@ defmodule JidoCode.Forge.Bootstrap do
def execute_step(client, %{type: "exec", command: command} = step, opts) do
Logger.debug("Executing bootstrap command: #{command}")

sprite_client = Keyword.get(opts, :sprite_client, JidoCode.Forge.SpriteClient)
infra_client = Keyword.get(opts, :infra_client, JidoCode.Forge.InfraClient)

case sprite_client.exec(client, command, opts) do
case infra_client.exec(client, command, opts) do
{_output, 0} ->
:ok

Expand All @@ -77,9 +77,9 @@ defmodule JidoCode.Forge.Bootstrap do
def execute_step(client, %{type: "file", path: path, content: content}, opts) do
Logger.debug("Writing bootstrap file: #{path}")

sprite_client = Keyword.get(opts, :sprite_client, JidoCode.Forge.SpriteClient)
infra_client = Keyword.get(opts, :infra_client, JidoCode.Forge.InfraClient)

case sprite_client.write_file(client, path, content) do
case infra_client.write_file(client, path, content) do
:ok -> :ok
{:error, reason} -> {:error, {:write_failed, path, reason}}
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
defmodule JidoCode.Forge.SpriteClient do
defmodule JidoCode.Forge.InfraClient do
@moduledoc """
Facade for sprite client operations.
Facade for infrastructure client operations.

Delegates all calls to the appropriate implementation module based on
the client struct type. For `create/1`, uses the configured implementation.

Configure via:

config :jido_code, :forge_sprite_client, MyApp.SpriteClient.Impl
config :jido_code, :forge_infra_client, MyApp.InfraClient.Impl

Defaults to `JidoCode.Forge.SpriteClient.Fake` for development and testing.
Defaults to `JidoCode.Forge.InfraClient.Fake` for development and testing.
"""

@behaviour JidoCode.Forge.SpriteClient.Behaviour
@behaviour JidoCode.Forge.InfraClient.Behaviour

alias JidoCode.Forge.SpriteClient.Fake
alias JidoCode.Forge.InfraClient.Fake

defp impl do
Application.get_env(:jido_code, :forge_sprite_client, Fake)
Application.get_env(:jido_code, :forge_infra_client, Fake)
end

defp impl_for(%module{} = _client) when is_atom(module) do
Expand All @@ -29,7 +29,7 @@ defmodule JidoCode.Forge.SpriteClient do
end

defp impl_for(client) do
raise ArgumentError, "Unknown sprite client struct: #{inspect(client)}"
raise ArgumentError, "Unknown infra client struct: #{inspect(client)}"
end

@impl true
Expand Down Expand Up @@ -66,7 +66,7 @@ defmodule JidoCode.Forge.SpriteClient do
end

@impl true
def destroy(client, sprite_id) do
impl_for(client).destroy(client, sprite_id)
def destroy(client, infra_id) do
impl_for(client).destroy(client, infra_id)
end
end
78 changes: 78 additions & 0 deletions lib/jido_code/forge/infra_client/behaviour.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
defmodule JidoCode.Forge.InfraClient.Behaviour do
@moduledoc """
Behaviour defining the interface for infrastructure client implementations.

An infra client provides an isolated execution environment (container, VM, or local sandbox)
where Forge runners execute commands and manage files.

## Progress Reporting

The `create/1` callback receives a spec map that may include an `:on_progress` key —
a function `(stage, metadata) -> :ok` called during long-running provisioning to report
progress. Stages: `:ssh_key`, `:server_creating`, `:server_booting`, `:ssh_waiting`,
`:ssh_connected`, `:session_starting`. Fast-provisioning implementations (like local
sandboxes) may ignore this key.
"""

@type client :: term()
@type infra_id :: String.t()
@type spec :: map()
@type command :: String.t()
@type path :: String.t()
@type content :: binary()
@type env_map :: %{String.t() => String.t()}
@type handle :: term()
@type opts :: keyword()

@doc """
Create a new infrastructure environment from the given specification.

The spec map may include an `:on_progress` key with a function `(stage, metadata) -> :ok`
for reporting provisioning progress.

Returns the client state and a unique infrastructure identifier.
"""
@callback create(spec()) :: {:ok, client(), infra_id()} | {:error, term()}

@doc """
Execute a command synchronously in the environment.

Returns the output and exit code.
"""
@callback exec(client(), command(), opts()) :: {String.t(), non_neg_integer()}

@doc """
Spawn an asynchronous command in the environment.

Returns a handle for monitoring or interacting with the process.
"""
@callback spawn(client(), command(), args :: [String.t()], opts()) ::
{:ok, handle()} | {:error, term()}

@doc """
Write content to a file in the environment.
"""
@callback write_file(client(), path(), content()) :: :ok | {:error, term()}

@doc """
Read content from a file in the environment.
"""
@callback read_file(client(), path()) :: {:ok, content()} | {:error, term()}

@doc """
Inject environment variables into the environment.

These should be available to all subsequent commands.
"""
@callback inject_env(client(), env_map()) :: :ok | {:error, term()}

@doc """
Destroy the environment and clean up resources.
"""
@callback destroy(client(), infra_id()) :: :ok | {:error, term()}

@doc """
Returns the implementation module for this client type.
"""
@callback impl_module() :: module()
end
Loading