Merge da6086f865 into sapling-pr-archive-bolinfest

This commit is contained in:
Michael Bolin
2025-09-15 07:07:13 -07:00
committed by GitHub
680 changed files with 7800 additions and 8132 deletions

View File

@@ -211,7 +211,8 @@ jobs:
files: dist/**
# Mark as prerelease only when the version has a suffix after x.y.z
# (e.g. -alpha, -beta). Otherwise publish a normal release.
prerelease: ${{ contains(steps.release_name.outputs.name, '-') }}
# prerelease: ${{ contains(steps.release_name.outputs.name, '-') }}
prerelease: true
- uses: facebook/dotslash-publish-release@v2
env:

58
codex-rs/Cargo.lock generated
View File

@@ -407,7 +407,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
"regex-automata 0.4.9",
"regex-automata",
"serde",
]
@@ -625,6 +625,7 @@ name = "codex-cli"
version = "0.0.0"
dependencies = [
"anyhow",
"assert_cmd",
"clap",
"clap_complete",
"codex-arg0",
@@ -637,7 +638,10 @@ dependencies = [
"codex-protocol",
"codex-protocol-ts",
"codex-tui",
"predicates",
"pretty_assertions",
"serde_json",
"tempfile",
"tokio",
"tracing",
"tracing-subscriber",
@@ -917,6 +921,7 @@ dependencies = [
"color-eyre",
"crossterm",
"diffy",
"dirs",
"image",
"insta",
"itertools 0.14.0",
@@ -1323,7 +1328,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b545b8c50194bdd008283985ab0b31dba153cfd5b3066a92770634fbc0d7d291"
dependencies = [
"nu-ansi-term 0.50.1",
"nu-ansi-term",
]
[[package]]
@@ -1905,7 +1910,7 @@ dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata 0.4.9",
"regex-automata",
"regex-syntax 0.8.5",
]
@@ -2320,7 +2325,7 @@ dependencies = [
"globset",
"log",
"memchr",
"regex-automata 0.4.9",
"regex-automata",
"same-file",
"walkdir",
"winapi-util",
@@ -2705,11 +2710,11 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]]
name = "matchers"
version = "0.1.0"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
"regex-automata 0.1.10",
"regex-automata",
]
[[package]]
@@ -2883,16 +2888,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "nu-ansi-term"
version = "0.50.1"
@@ -3131,12 +3126,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "owo-colors"
version = "4.2.2"
@@ -3599,19 +3588,10 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.9",
"regex-automata",
"regex-syntax 0.8.5",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax 0.6.29",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
@@ -4172,9 +4152,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
[[package]]
name = "slab"
version = "0.4.10"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]]
name = "smallvec"
@@ -4906,14 +4886,14 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.3.19"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
dependencies = [
"matchers",
"nu-ansi-term 0.46.0",
"nu-ansi-term",
"once_cell",
"regex",
"regex-automata",
"sharded-slab",
"smallvec",
"thread_local",

View File

@@ -36,5 +36,11 @@ tokio = { version = "1", features = [
"signal",
] }
tracing = "0.1.41"
tracing-subscriber = "0.3.19"
tracing-subscriber = "0.3.20"
codex-protocol-ts = { path = "../protocol-ts" }
[dev-dependencies]
assert_cmd = "2"
predicates = "3"
pretty_assertions = "1"
tempfile = "3"

View File

@@ -17,6 +17,9 @@ use codex_exec::Cli as ExecCli;
use codex_tui::Cli as TuiCli;
use std::path::PathBuf;
mod mcp_cmd;
use crate::mcp_cmd::McpCli;
use crate::proto::ProtoCli;
/// Codex CLI
@@ -56,8 +59,8 @@ enum Subcommand {
/// Remove stored authentication credentials.
Logout(LogoutCommand),
/// Experimental: run Codex as an MCP server.
Mcp,
/// [experimental] Run Codex as an MCP server and manage MCP servers.
Mcp(McpCli),
/// Run the Protocol stream via stdin/stdout
#[clap(visible_alias = "p")]
@@ -98,6 +101,9 @@ struct ResumeCommand {
/// Continue the most recent session without showing the picker.
#[arg(long = "last", default_value_t = false, conflicts_with = "session_id")]
last: bool,
#[clap(flatten)]
config_overrides: TuiCli,
}
#[derive(Debug, Parser)]
@@ -182,22 +188,22 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
);
codex_exec::run_main(exec_cli, codex_linux_sandbox_exe).await?;
}
Some(Subcommand::Mcp) => {
codex_mcp_server::run_main(codex_linux_sandbox_exe, root_config_overrides.clone())
.await?;
}
Some(Subcommand::Resume(ResumeCommand { session_id, last })) => {
// Start with the parsed interactive CLI so resume shares the same
// configuration surface area as `codex` without additional flags.
let resume_session_id = session_id;
interactive.resume_picker = resume_session_id.is_none() && !last;
interactive.resume_last = last;
interactive.resume_session_id = resume_session_id;
Some(Subcommand::Mcp(mut mcp_cli)) => {
// Propagate any root-level config overrides (e.g. `-c key=value`).
prepend_config_flags(
&mut interactive.config_overrides,
prepend_config_flags(&mut mcp_cli.config_overrides, root_config_overrides.clone());
mcp_cli.run(codex_linux_sandbox_exe).await?;
}
Some(Subcommand::Resume(ResumeCommand {
session_id,
last,
config_overrides,
})) => {
interactive = finalize_resume_interactive(
interactive,
root_config_overrides.clone(),
session_id,
last,
config_overrides,
);
codex_tui::run_main(interactive, codex_linux_sandbox_exe).await?;
}
@@ -286,8 +292,208 @@ fn prepend_config_flags(
.splice(0..0, cli_config_overrides.raw_overrides);
}
/// Build the final `TuiCli` for a `codex resume` invocation.
fn finalize_resume_interactive(
mut interactive: TuiCli,
root_config_overrides: CliConfigOverrides,
session_id: Option<String>,
last: bool,
resume_cli: TuiCli,
) -> TuiCli {
// Start with the parsed interactive CLI so resume shares the same
// configuration surface area as `codex` without additional flags.
let resume_session_id = session_id;
interactive.resume_picker = resume_session_id.is_none() && !last;
interactive.resume_last = last;
interactive.resume_session_id = resume_session_id;
// Merge resume-scoped flags and overrides with highest precedence.
merge_resume_cli_flags(&mut interactive, resume_cli);
// Propagate any root-level config overrides (e.g. `-c key=value`).
prepend_config_flags(&mut interactive.config_overrides, root_config_overrides);
interactive
}
/// Merge flags provided to `codex resume` so they take precedence over any
/// root-level flags. Only overrides fields explicitly set on the resume-scoped
/// CLI. Also appends `-c key=value` overrides with highest precedence.
fn merge_resume_cli_flags(interactive: &mut TuiCli, resume_cli: TuiCli) {
if let Some(model) = resume_cli.model {
interactive.model = Some(model);
}
if resume_cli.oss {
interactive.oss = true;
}
if let Some(profile) = resume_cli.config_profile {
interactive.config_profile = Some(profile);
}
if let Some(sandbox) = resume_cli.sandbox_mode {
interactive.sandbox_mode = Some(sandbox);
}
if let Some(approval) = resume_cli.approval_policy {
interactive.approval_policy = Some(approval);
}
if resume_cli.full_auto {
interactive.full_auto = true;
}
if resume_cli.dangerously_bypass_approvals_and_sandbox {
interactive.dangerously_bypass_approvals_and_sandbox = true;
}
if let Some(cwd) = resume_cli.cwd {
interactive.cwd = Some(cwd);
}
if resume_cli.web_search {
interactive.web_search = true;
}
if !resume_cli.images.is_empty() {
interactive.images = resume_cli.images;
}
if let Some(prompt) = resume_cli.prompt {
interactive.prompt = Some(prompt);
}
interactive
.config_overrides
.raw_overrides
.extend(resume_cli.config_overrides.raw_overrides);
}
fn print_completion(cmd: CompletionCommand) {
let mut app = MultitoolCli::command();
let name = "codex";
generate(cmd.shell, &mut app, name, &mut std::io::stdout());
}
#[cfg(test)]
mod tests {
use super::*;
fn finalize_from_args(args: &[&str]) -> TuiCli {
let cli = MultitoolCli::try_parse_from(args).expect("parse");
let MultitoolCli {
interactive,
config_overrides: root_overrides,
subcommand,
} = cli;
let Subcommand::Resume(ResumeCommand {
session_id,
last,
config_overrides: resume_cli,
}) = subcommand.expect("resume present")
else {
unreachable!()
};
finalize_resume_interactive(interactive, root_overrides, session_id, last, resume_cli)
}
#[test]
fn resume_model_flag_applies_when_no_root_flags() {
let interactive = finalize_from_args(["codex", "resume", "-m", "gpt-5-test"].as_ref());
assert_eq!(interactive.model.as_deref(), Some("gpt-5-test"));
assert!(interactive.resume_picker);
assert!(!interactive.resume_last);
assert_eq!(interactive.resume_session_id, None);
}
#[test]
fn resume_picker_logic_none_and_not_last() {
let interactive = finalize_from_args(["codex", "resume"].as_ref());
assert!(interactive.resume_picker);
assert!(!interactive.resume_last);
assert_eq!(interactive.resume_session_id, None);
}
#[test]
fn resume_picker_logic_last() {
let interactive = finalize_from_args(["codex", "resume", "--last"].as_ref());
assert!(!interactive.resume_picker);
assert!(interactive.resume_last);
assert_eq!(interactive.resume_session_id, None);
}
#[test]
fn resume_picker_logic_with_session_id() {
let interactive = finalize_from_args(["codex", "resume", "1234"].as_ref());
assert!(!interactive.resume_picker);
assert!(!interactive.resume_last);
assert_eq!(interactive.resume_session_id.as_deref(), Some("1234"));
}
#[test]
fn resume_merges_option_flags_and_full_auto() {
let interactive = finalize_from_args(
[
"codex",
"resume",
"sid",
"--oss",
"--full-auto",
"--search",
"--sandbox",
"workspace-write",
"--ask-for-approval",
"on-request",
"-m",
"gpt-5-test",
"-p",
"my-profile",
"-C",
"/tmp",
"-i",
"/tmp/a.png,/tmp/b.png",
]
.as_ref(),
);
assert_eq!(interactive.model.as_deref(), Some("gpt-5-test"));
assert!(interactive.oss);
assert_eq!(interactive.config_profile.as_deref(), Some("my-profile"));
assert!(matches!(
interactive.sandbox_mode,
Some(codex_common::SandboxModeCliArg::WorkspaceWrite)
));
assert!(matches!(
interactive.approval_policy,
Some(codex_common::ApprovalModeCliArg::OnRequest)
));
assert!(interactive.full_auto);
assert_eq!(
interactive.cwd.as_deref(),
Some(std::path::Path::new("/tmp"))
);
assert!(interactive.web_search);
let has_a = interactive
.images
.iter()
.any(|p| p == std::path::Path::new("/tmp/a.png"));
let has_b = interactive
.images
.iter()
.any(|p| p == std::path::Path::new("/tmp/b.png"));
assert!(has_a && has_b);
assert!(!interactive.resume_picker);
assert!(!interactive.resume_last);
assert_eq!(interactive.resume_session_id.as_deref(), Some("sid"));
}
#[test]
fn resume_merges_dangerously_bypass_flag() {
let interactive = finalize_from_args(
[
"codex",
"resume",
"--dangerously-bypass-approvals-and-sandbox",
]
.as_ref(),
);
assert!(interactive.dangerously_bypass_approvals_and_sandbox);
assert!(interactive.resume_picker);
assert!(!interactive.resume_last);
assert_eq!(interactive.resume_session_id, None);
}
}

370
codex-rs/cli/src/mcp_cmd.rs Normal file
View File

@@ -0,0 +1,370 @@
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::path::PathBuf;
use anyhow::Context;
use anyhow::Result;
use anyhow::anyhow;
use anyhow::bail;
use codex_common::CliConfigOverrides;
use codex_core::config::Config;
use codex_core::config::ConfigOverrides;
use codex_core::config::find_codex_home;
use codex_core::config::load_global_mcp_servers;
use codex_core::config::write_global_mcp_servers;
use codex_core::config_types::McpServerConfig;
/// [experimental] Launch Codex as an MCP server or manage configured MCP servers.
///
/// Subcommands:
/// - `serve` — run the MCP server on stdio
/// - `list` — list configured servers (with `--json`)
/// - `get` — show a single server (with `--json`)
/// - `add` — add a server launcher entry to `~/.codex/config.toml`
/// - `remove` — delete a server entry
#[derive(Debug, clap::Parser)]
pub struct McpCli {
#[clap(flatten)]
pub config_overrides: CliConfigOverrides,
#[command(subcommand)]
pub cmd: Option<McpSubcommand>,
}
#[derive(Debug, clap::Subcommand)]
pub enum McpSubcommand {
/// [experimental] Run the Codex MCP server (stdio transport).
Serve,
/// [experimental] List configured MCP servers.
List(ListArgs),
/// [experimental] Show details for a configured MCP server.
Get(GetArgs),
/// [experimental] Add a global MCP server entry.
Add(AddArgs),
/// [experimental] Remove a global MCP server entry.
Remove(RemoveArgs),
}
#[derive(Debug, clap::Parser)]
pub struct ListArgs {
/// Output the configured servers as JSON.
#[arg(long)]
pub json: bool,
}
#[derive(Debug, clap::Parser)]
pub struct GetArgs {
/// Name of the MCP server to display.
pub name: String,
/// Output the server configuration as JSON.
#[arg(long)]
pub json: bool,
}
#[derive(Debug, clap::Parser)]
pub struct AddArgs {
/// Name for the MCP server configuration.
pub name: String,
/// Environment variables to set when launching the server.
#[arg(long, value_parser = parse_env_pair, value_name = "KEY=VALUE")]
pub env: Vec<(String, String)>,
/// Command to launch the MCP server.
#[arg(trailing_var_arg = true, num_args = 1..)]
pub command: Vec<String>,
}
#[derive(Debug, clap::Parser)]
pub struct RemoveArgs {
/// Name of the MCP server configuration to remove.
pub name: String,
}
impl McpCli {
pub async fn run(self, codex_linux_sandbox_exe: Option<PathBuf>) -> Result<()> {
let McpCli {
config_overrides,
cmd,
} = self;
let subcommand = cmd.unwrap_or(McpSubcommand::Serve);
match subcommand {
McpSubcommand::Serve => {
codex_mcp_server::run_main(codex_linux_sandbox_exe, config_overrides).await?;
}
McpSubcommand::List(args) => {
run_list(&config_overrides, args)?;
}
McpSubcommand::Get(args) => {
run_get(&config_overrides, args)?;
}
McpSubcommand::Add(args) => {
run_add(&config_overrides, args)?;
}
McpSubcommand::Remove(args) => {
run_remove(&config_overrides, args)?;
}
}
Ok(())
}
}
fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Result<()> {
// Validate any provided overrides even though they are not currently applied.
config_overrides.parse_overrides().map_err(|e| anyhow!(e))?;
let AddArgs { name, env, command } = add_args;
validate_server_name(&name)?;
let mut command_parts = command.into_iter();
let command_bin = command_parts
.next()
.ok_or_else(|| anyhow!("command is required"))?;
let command_args: Vec<String> = command_parts.collect();
let env_map = if env.is_empty() {
None
} else {
let mut map = HashMap::new();
for (key, value) in env {
map.insert(key, value);
}
Some(map)
};
let codex_home = find_codex_home().context("failed to resolve CODEX_HOME")?;
let mut servers = load_global_mcp_servers(&codex_home)
.with_context(|| format!("failed to load MCP servers from {}", codex_home.display()))?;
let new_entry = McpServerConfig {
command: command_bin,
args: command_args,
env: env_map,
startup_timeout_ms: None,
};
servers.insert(name.clone(), new_entry);
write_global_mcp_servers(&codex_home, &servers)
.with_context(|| format!("failed to write MCP servers to {}", codex_home.display()))?;
println!("Added global MCP server '{name}'.");
Ok(())
}
fn run_remove(config_overrides: &CliConfigOverrides, remove_args: RemoveArgs) -> Result<()> {
config_overrides.parse_overrides().map_err(|e| anyhow!(e))?;
let RemoveArgs { name } = remove_args;
validate_server_name(&name)?;
let codex_home = find_codex_home().context("failed to resolve CODEX_HOME")?;
let mut servers = load_global_mcp_servers(&codex_home)
.with_context(|| format!("failed to load MCP servers from {}", codex_home.display()))?;
let removed = servers.remove(&name).is_some();
if removed {
write_global_mcp_servers(&codex_home, &servers)
.with_context(|| format!("failed to write MCP servers to {}", codex_home.display()))?;
}
if removed {
println!("Removed global MCP server '{name}'.");
} else {
println!("No MCP server named '{name}' found.");
}
Ok(())
}
fn run_list(config_overrides: &CliConfigOverrides, list_args: ListArgs) -> Result<()> {
let overrides = config_overrides.parse_overrides().map_err(|e| anyhow!(e))?;
let config = Config::load_with_cli_overrides(overrides, ConfigOverrides::default())
.context("failed to load configuration")?;
let mut entries: Vec<_> = config.mcp_servers.iter().collect();
entries.sort_by(|(a, _), (b, _)| a.cmp(b));
if list_args.json {
let json_entries: Vec<_> = entries
.into_iter()
.map(|(name, cfg)| {
let env = cfg.env.as_ref().map(|env| {
env.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect::<BTreeMap<_, _>>()
});
serde_json::json!({
"name": name,
"command": cfg.command,
"args": cfg.args,
"env": env,
"startup_timeout_ms": cfg.startup_timeout_ms,
})
})
.collect();
let output = serde_json::to_string_pretty(&json_entries)?;
println!("{output}");
return Ok(());
}
if entries.is_empty() {
println!("No MCP servers configured yet. Try `codex mcp add my-tool -- my-command`.");
return Ok(());
}
let mut rows: Vec<[String; 4]> = Vec::new();
for (name, cfg) in entries {
let args = if cfg.args.is_empty() {
"-".to_string()
} else {
cfg.args.join(" ")
};
let env = match cfg.env.as_ref() {
None => "-".to_string(),
Some(map) if map.is_empty() => "-".to_string(),
Some(map) => {
let mut pairs: Vec<_> = map.iter().collect();
pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
pairs
.into_iter()
.map(|(k, v)| format!("{k}={v}"))
.collect::<Vec<_>>()
.join(", ")
}
};
rows.push([name.clone(), cfg.command.clone(), args, env]);
}
let mut widths = ["Name".len(), "Command".len(), "Args".len(), "Env".len()];
for row in &rows {
for (i, cell) in row.iter().enumerate() {
widths[i] = widths[i].max(cell.len());
}
}
println!(
"{:<name_w$} {:<cmd_w$} {:<args_w$} {:<env_w$}",
"Name",
"Command",
"Args",
"Env",
name_w = widths[0],
cmd_w = widths[1],
args_w = widths[2],
env_w = widths[3],
);
for row in rows {
println!(
"{:<name_w$} {:<cmd_w$} {:<args_w$} {:<env_w$}",
row[0],
row[1],
row[2],
row[3],
name_w = widths[0],
cmd_w = widths[1],
args_w = widths[2],
env_w = widths[3],
);
}
Ok(())
}
fn run_get(config_overrides: &CliConfigOverrides, get_args: GetArgs) -> Result<()> {
let overrides = config_overrides.parse_overrides().map_err(|e| anyhow!(e))?;
let config = Config::load_with_cli_overrides(overrides, ConfigOverrides::default())
.context("failed to load configuration")?;
let Some(server) = config.mcp_servers.get(&get_args.name) else {
bail!("No MCP server named '{name}' found.", name = get_args.name);
};
if get_args.json {
let env = server.env.as_ref().map(|env| {
env.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect::<BTreeMap<_, _>>()
});
let output = serde_json::to_string_pretty(&serde_json::json!({
"name": get_args.name,
"command": server.command,
"args": server.args,
"env": env,
"startup_timeout_ms": server.startup_timeout_ms,
}))?;
println!("{output}");
return Ok(());
}
println!("{}", get_args.name);
println!(" command: {}", server.command);
let args = if server.args.is_empty() {
"-".to_string()
} else {
server.args.join(" ")
};
println!(" args: {args}");
let env_display = match server.env.as_ref() {
None => "-".to_string(),
Some(map) if map.is_empty() => "-".to_string(),
Some(map) => {
let mut pairs: Vec<_> = map.iter().collect();
pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
pairs
.into_iter()
.map(|(k, v)| format!("{k}={v}"))
.collect::<Vec<_>>()
.join(", ")
}
};
println!(" env: {env_display}");
if let Some(timeout) = server.startup_timeout_ms {
println!(" startup_timeout_ms: {timeout}");
}
println!(" remove: codex mcp remove {}", get_args.name);
Ok(())
}
fn parse_env_pair(raw: &str) -> Result<(String, String), String> {
let mut parts = raw.splitn(2, '=');
let key = parts
.next()
.map(str::trim)
.filter(|s| !s.is_empty())
.ok_or_else(|| "environment entries must be in KEY=VALUE form".to_string())?;
let value = parts
.next()
.map(str::to_string)
.ok_or_else(|| "environment entries must be in KEY=VALUE form".to_string())?;
Ok((key.to_string(), value))
}
fn validate_server_name(name: &str) -> Result<()> {
let is_valid = !name.is_empty()
&& name
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_');
if is_valid {
Ok(())
} else {
bail!("invalid server name '{name}' (use letters, numbers, '-', '_')");
}
}

View File

@@ -0,0 +1,86 @@
use std::path::Path;
use anyhow::Result;
use codex_core::config::load_global_mcp_servers;
use predicates::str::contains;
use pretty_assertions::assert_eq;
use tempfile::TempDir;
fn codex_command(codex_home: &Path) -> Result<assert_cmd::Command> {
let mut cmd = assert_cmd::Command::cargo_bin("codex")?;
cmd.env("CODEX_HOME", codex_home);
Ok(cmd)
}
#[test]
fn add_and_remove_server_updates_global_config() -> Result<()> {
let codex_home = TempDir::new()?;
let mut add_cmd = codex_command(codex_home.path())?;
add_cmd
.args(["mcp", "add", "docs", "--", "echo", "hello"])
.assert()
.success()
.stdout(contains("Added global MCP server 'docs'."));
let servers = load_global_mcp_servers(codex_home.path())?;
assert_eq!(servers.len(), 1);
let docs = servers.get("docs").expect("server should exist");
assert_eq!(docs.command, "echo");
assert_eq!(docs.args, vec!["hello".to_string()]);
assert!(docs.env.is_none());
let mut remove_cmd = codex_command(codex_home.path())?;
remove_cmd
.args(["mcp", "remove", "docs"])
.assert()
.success()
.stdout(contains("Removed global MCP server 'docs'."));
let servers = load_global_mcp_servers(codex_home.path())?;
assert!(servers.is_empty());
let mut remove_again_cmd = codex_command(codex_home.path())?;
remove_again_cmd
.args(["mcp", "remove", "docs"])
.assert()
.success()
.stdout(contains("No MCP server named 'docs' found."));
let servers = load_global_mcp_servers(codex_home.path())?;
assert!(servers.is_empty());
Ok(())
}
#[test]
fn add_with_env_preserves_key_order_and_values() -> Result<()> {
let codex_home = TempDir::new()?;
let mut add_cmd = codex_command(codex_home.path())?;
add_cmd
.args([
"mcp",
"add",
"envy",
"--env",
"FOO=bar",
"--env",
"ALPHA=beta",
"--",
"python",
"server.py",
])
.assert()
.success();
let servers = load_global_mcp_servers(codex_home.path())?;
let envy = servers.get("envy").expect("server should exist");
let env = envy.env.as_ref().expect("env should be present");
assert_eq!(env.len(), 2);
assert_eq!(env.get("FOO"), Some(&"bar".to_string()));
assert_eq!(env.get("ALPHA"), Some(&"beta".to_string()));
Ok(())
}

View File

@@ -0,0 +1,106 @@
use std::path::Path;
use anyhow::Result;
use predicates::str::contains;
use pretty_assertions::assert_eq;
use serde_json::Value as JsonValue;
use tempfile::TempDir;
fn codex_command(codex_home: &Path) -> Result<assert_cmd::Command> {
let mut cmd = assert_cmd::Command::cargo_bin("codex")?;
cmd.env("CODEX_HOME", codex_home);
Ok(cmd)
}
#[test]
fn list_shows_empty_state() -> Result<()> {
let codex_home = TempDir::new()?;
let mut cmd = codex_command(codex_home.path())?;
let output = cmd.args(["mcp", "list"]).output()?;
assert!(output.status.success());
let stdout = String::from_utf8(output.stdout)?;
assert!(stdout.contains("No MCP servers configured yet."));
Ok(())
}
#[test]
fn list_and_get_render_expected_output() -> Result<()> {
let codex_home = TempDir::new()?;
let mut add = codex_command(codex_home.path())?;
add.args([
"mcp",
"add",
"docs",
"--env",
"TOKEN=secret",
"--",
"docs-server",
"--port",
"4000",
])
.assert()
.success();
let mut list_cmd = codex_command(codex_home.path())?;
let list_output = list_cmd.args(["mcp", "list"]).output()?;
assert!(list_output.status.success());
let stdout = String::from_utf8(list_output.stdout)?;
assert!(stdout.contains("Name"));
assert!(stdout.contains("docs"));
assert!(stdout.contains("docs-server"));
assert!(stdout.contains("TOKEN=secret"));
let mut list_json_cmd = codex_command(codex_home.path())?;
let json_output = list_json_cmd.args(["mcp", "list", "--json"]).output()?;
assert!(json_output.status.success());
let stdout = String::from_utf8(json_output.stdout)?;
let parsed: JsonValue = serde_json::from_str(&stdout)?;
let array = parsed.as_array().expect("expected array");
assert_eq!(array.len(), 1);
let entry = &array[0];
assert_eq!(entry.get("name"), Some(&JsonValue::String("docs".into())));
assert_eq!(
entry.get("command"),
Some(&JsonValue::String("docs-server".into()))
);
let args = entry
.get("args")
.and_then(|v| v.as_array())
.expect("args array");
assert_eq!(
args,
&vec![
JsonValue::String("--port".into()),
JsonValue::String("4000".into())
]
);
let env = entry
.get("env")
.and_then(|v| v.as_object())
.expect("env map");
assert_eq!(env.get("TOKEN"), Some(&JsonValue::String("secret".into())));
let mut get_cmd = codex_command(codex_home.path())?;
let get_output = get_cmd.args(["mcp", "get", "docs"]).output()?;
assert!(get_output.status.success());
let stdout = String::from_utf8(get_output.stdout)?;
assert!(stdout.contains("docs"));
assert!(stdout.contains("command: docs-server"));
assert!(stdout.contains("args: --port 4000"));
assert!(stdout.contains("env: TOKEN=secret"));
assert!(stdout.contains("remove: codex mcp remove docs"));
let mut get_json_cmd = codex_command(codex_home.path())?;
get_json_cmd
.args(["mcp", "get", "docs", "--json"])
.assert()
.success()
.stdout(contains("\"name\": \"docs\""));
Ok(())
}

View File

@@ -74,7 +74,7 @@ pub fn builtin_model_presets(auth_mode: Option<AuthMode>) -> Vec<ModelPreset> {
Some(AuthMode::ApiKey) => PRESETS
.iter()
.copied()
.filter(|p| !p.model.contains(SWIFTFOX_MEDIUM_MODEL))
.filter(|p| p.model != SWIFTFOX_MEDIUM_MODEL)
.collect(),
_ => PRESETS.to_vec(),
}

View File

@@ -25,12 +25,16 @@ use codex_protocol::mcp_protocol::Tools;
use codex_protocol::mcp_protocol::UserSavedConfig;
use dirs::home_dir;
use serde::Deserialize;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
use tempfile::NamedTempFile;
use toml::Value as TomlValue;
use toml_edit::Array as TomlArray;
use toml_edit::DocumentMut;
use toml_edit::Item as TomlItem;
use toml_edit::Table as TomlTable;
const OPENAI_DEFAULT_MODEL: &str = "gpt-5";
const OPENAI_DEFAULT_REVIEW_MODEL: &str = "gpt-5";
@@ -265,6 +269,88 @@ pub fn load_config_as_toml(codex_home: &Path) -> std::io::Result<TomlValue> {
}
}
pub fn load_global_mcp_servers(
codex_home: &Path,
) -> std::io::Result<BTreeMap<String, McpServerConfig>> {
let root_value = load_config_as_toml(codex_home)?;
let Some(servers_value) = root_value.get("mcp_servers") else {
return Ok(BTreeMap::new());
};
servers_value
.clone()
.try_into()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
}
pub fn write_global_mcp_servers(
codex_home: &Path,
servers: &BTreeMap<String, McpServerConfig>,
) -> std::io::Result<()> {
let config_path = codex_home.join(CONFIG_TOML_FILE);
let mut doc = match std::fs::read_to_string(&config_path) {
Ok(contents) => contents
.parse::<DocumentMut>()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => DocumentMut::new(),
Err(e) => return Err(e),
};
doc.as_table_mut().remove("mcp_servers");
if !servers.is_empty() {
let mut table = TomlTable::new();
table.set_implicit(true);
doc["mcp_servers"] = TomlItem::Table(table);
for (name, config) in servers {
let mut entry = TomlTable::new();
entry.set_implicit(false);
entry["command"] = toml_edit::value(config.command.clone());
if !config.args.is_empty() {
let mut args = TomlArray::new();
for arg in &config.args {
args.push(arg.clone());
}
entry["args"] = TomlItem::Value(args.into());
}
if let Some(env) = &config.env
&& !env.is_empty()
{
let mut env_table = TomlTable::new();
env_table.set_implicit(false);
let mut pairs: Vec<_> = env.iter().collect();
pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
for (key, value) in pairs {
env_table.insert(key, toml_edit::value(value.clone()));
}
entry["env"] = TomlItem::Table(env_table);
}
if let Some(timeout) = config.startup_timeout_ms {
let timeout = i64::try_from(timeout).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
"startup_timeout_ms exceeds supported range",
)
})?;
entry["startup_timeout_ms"] = toml_edit::value(timeout);
}
doc["mcp_servers"][name.as_str()] = TomlItem::Table(entry);
}
}
std::fs::create_dir_all(codex_home)?;
let tmp_file = NamedTempFile::new_in(codex_home)?;
std::fs::write(tmp_file.path(), doc.to_string())?;
tmp_file.persist(config_path).map_err(|err| err.error)?;
Ok(())
}
fn set_project_trusted_inner(doc: &mut DocumentMut, project_path: &Path) -> anyhow::Result<()> {
// Ensure we render a human-friendly structure:
//
@@ -1162,6 +1248,47 @@ exclude_slash_tmp = true
);
}
#[test]
fn load_global_mcp_servers_returns_empty_if_missing() -> anyhow::Result<()> {
let codex_home = TempDir::new()?;
let servers = load_global_mcp_servers(codex_home.path())?;
assert!(servers.is_empty());
Ok(())
}
#[test]
fn write_global_mcp_servers_round_trips_entries() -> anyhow::Result<()> {
let codex_home = TempDir::new()?;
let mut servers = BTreeMap::new();
servers.insert(
"docs".to_string(),
McpServerConfig {
command: "echo".to_string(),
args: vec!["hello".to_string()],
env: None,
startup_timeout_ms: None,
},
);
write_global_mcp_servers(codex_home.path(), &servers)?;
let loaded = load_global_mcp_servers(codex_home.path())?;
assert_eq!(loaded.len(), 1);
let docs = loaded.get("docs").expect("docs entry");
assert_eq!(docs.command, "echo");
assert_eq!(docs.args, vec!["hello".to_string()]);
let empty = BTreeMap::new();
write_global_mcp_servers(codex_home.path(), &empty)?;
let loaded = load_global_mcp_servers(codex_home.path())?;
assert!(loaded.is_empty());
Ok(())
}
#[tokio::test]
async fn persist_model_selection_updates_defaults() -> anyhow::Result<()> {
let codex_home = TempDir::new()?;

View File

@@ -11,9 +11,6 @@ pub(crate) struct ExecCommandSession {
/// Broadcast stream of output chunks read from the PTY. New subscribers
/// receive only chunks emitted after they subscribe.
output_tx: broadcast::Sender<Vec<u8>>,
/// Receiver subscribed before the child process starts emitting output so
/// the first caller can consume any early data without races.
initial_output_rx: StdMutex<Option<broadcast::Receiver<Vec<u8>>>>,
/// Child killer handle for termination on drop (can signal independently
/// of a thread blocked in `.wait()`).
@@ -41,25 +38,20 @@ impl ExecCommandSession {
writer_handle: JoinHandle<()>,
wait_handle: JoinHandle<()>,
exit_status: std::sync::Arc<std::sync::atomic::AtomicBool>,
) -> Self {
Self {
writer_tx,
output_tx,
initial_output_rx: StdMutex::new(None),
killer: StdMutex::new(Some(killer)),
reader_handle: StdMutex::new(Some(reader_handle)),
writer_handle: StdMutex::new(Some(writer_handle)),
wait_handle: StdMutex::new(Some(wait_handle)),
exit_status,
}
}
pub(crate) fn set_initial_output_receiver(&self, receiver: broadcast::Receiver<Vec<u8>>) {
if let Ok(mut guard) = self.initial_output_rx.lock()
&& guard.is_none()
{
*guard = Some(receiver);
}
) -> (Self, broadcast::Receiver<Vec<u8>>) {
let initial_output_rx = output_tx.subscribe();
(
Self {
writer_tx,
output_tx,
killer: StdMutex::new(Some(killer)),
reader_handle: StdMutex::new(Some(reader_handle)),
writer_handle: StdMutex::new(Some(writer_handle)),
wait_handle: StdMutex::new(Some(wait_handle)),
exit_status,
},
initial_output_rx,
)
}
pub(crate) fn writer_sender(&self) -> mpsc::Sender<Vec<u8>> {
@@ -67,13 +59,7 @@ impl ExecCommandSession {
}
pub(crate) fn output_receiver(&self) -> broadcast::Receiver<Vec<u8>> {
if let Ok(mut guard) = self.initial_output_rx.lock()
&& let Some(receiver) = guard.take()
{
receiver
} else {
self.output_tx.subscribe()
}
self.output_tx.subscribe()
}
pub(crate) fn has_exited(&self) -> bool {

View File

@@ -93,18 +93,16 @@ impl SessionManager {
.fetch_add(1, std::sync::atomic::Ordering::SeqCst),
);
let (session, mut exit_rx) =
create_exec_command_session(params.clone())
.await
.map_err(|err| {
format!(
"failed to create exec command session for session id {}: {err}",
session_id.0
)
})?;
let (session, mut output_rx, mut exit_rx) = create_exec_command_session(params.clone())
.await
.map_err(|err| {
format!(
"failed to create exec command session for session id {}: {err}",
session_id.0
)
})?;
// Insert into session map.
let mut output_rx = session.output_receiver();
self.sessions.lock().await.insert(session_id, session);
// Collect output until either timeout expires or process exits.
@@ -245,7 +243,11 @@ impl SessionManager {
/// Spawn PTY and child process per spawn_exec_command_session logic.
async fn create_exec_command_session(
params: ExecCommandParams,
) -> anyhow::Result<(ExecCommandSession, oneshot::Receiver<i32>)> {
) -> anyhow::Result<(
ExecCommandSession,
tokio::sync::broadcast::Receiver<Vec<u8>>,
oneshot::Receiver<i32>,
)> {
let ExecCommandParams {
cmd,
yield_time_ms: _,
@@ -279,8 +281,6 @@ async fn create_exec_command_session(
let (writer_tx, mut writer_rx) = mpsc::channel::<Vec<u8>>(128);
// Broadcast for streaming PTY output to readers: subscribers receive from subscription time.
let (output_tx, _) = tokio::sync::broadcast::channel::<Vec<u8>>(256);
let initial_output_rx = output_tx.subscribe();
// Reader task: drain PTY and forward chunks to output channel.
let mut reader = pair.master.try_clone_reader()?;
let output_tx_clone = output_tx.clone();
@@ -342,7 +342,7 @@ async fn create_exec_command_session(
});
// Create and store the session with channels.
let session = ExecCommandSession::new(
let (session, initial_output_rx) = ExecCommandSession::new(
writer_tx,
output_tx,
killer,
@@ -351,8 +351,7 @@ async fn create_exec_command_session(
wait_handle,
exit_status,
);
session.set_initial_output_receiver(initial_output_rx);
Ok((session, exit_rx))
Ok((session, initial_output_rx, exit_rx))
}
#[cfg(test)]

View File

@@ -1,6 +1,7 @@
use anyhow::Context;
use serde::Deserialize;
use serde::Serialize;
use std::io::ErrorKind;
use std::path::Path;
use std::path::PathBuf;
@@ -31,7 +32,14 @@ impl InternalStorage {
}
},
Err(error) => {
tracing::warn!("failed to read internal storage: {error:?}");
if error.kind() == ErrorKind::NotFound {
tracing::debug!(
"internal storage not found at {}; initializing defaults",
storage_path.display()
);
} else {
tracing::warn!("failed to read internal storage: {error:?}");
}
Self::empty(storage_path)
}
}

View File

@@ -40,7 +40,8 @@ pub(crate) fn should_persist_event_msg(ev: &EventMsg) -> bool {
| EventMsg::AgentReasoningRawContent(_)
| EventMsg::TokenCount(_)
| EventMsg::EnteredReviewMode(_)
| EventMsg::ExitedReviewMode(_) => true,
| EventMsg::ExitedReviewMode(_)
| EventMsg::TurnAborted(_) => true,
EventMsg::Error(_)
| EventMsg::TaskStarted(_)
| EventMsg::TaskComplete(_)
@@ -67,7 +68,6 @@ pub(crate) fn should_persist_event_msg(ev: &EventMsg) -> bool {
| EventMsg::McpListToolsResponse(_)
| EventMsg::ListCustomPromptsResponse(_)
| EventMsg::PlanUpdate(_)
| EventMsg::TurnAborted(_)
| EventMsg::ShutdownComplete
| EventMsg::ConversationPath(_) => false,
}

View File

@@ -100,10 +100,13 @@ type OutputBuffer = Arc<Mutex<OutputBufferState>>;
type OutputHandles = (OutputBuffer, Arc<Notify>);
impl ManagedUnifiedExecSession {
fn new(session: ExecCommandSession) -> Self {
fn new(
session: ExecCommandSession,
initial_output_rx: tokio::sync::broadcast::Receiver<Vec<u8>>,
) -> Self {
let output_buffer = Arc::new(Mutex::new(OutputBufferState::default()));
let output_notify = Arc::new(Notify::new());
let mut receiver = session.output_receiver();
let mut receiver = initial_output_rx;
let buffer_clone = Arc::clone(&output_buffer);
let notify_clone = Arc::clone(&output_notify);
let output_task = tokio::spawn(async move {
@@ -193,8 +196,8 @@ impl UnifiedExecSessionManager {
} else {
let command = request.input_chunks.to_vec();
let new_id = self.next_session_id.fetch_add(1, Ordering::SeqCst);
let session = create_unified_exec_session(&command).await?;
let managed_session = ManagedUnifiedExecSession::new(session);
let (session, initial_output_rx) = create_unified_exec_session(&command).await?;
let managed_session = ManagedUnifiedExecSession::new(session, initial_output_rx);
let (buffer, notify) = managed_session.output_handles();
writer_tx = managed_session.writer_sender();
output_buffer = buffer;
@@ -297,7 +300,13 @@ impl UnifiedExecSessionManager {
async fn create_unified_exec_session(
command: &[String],
) -> Result<ExecCommandSession, UnifiedExecError> {
) -> Result<
(
ExecCommandSession,
tokio::sync::broadcast::Receiver<Vec<u8>>,
),
UnifiedExecError,
> {
if command.is_empty() {
return Err(UnifiedExecError::MissingCommandLine);
}
@@ -327,7 +336,6 @@ async fn create_unified_exec_session(
let (writer_tx, mut writer_rx) = mpsc::channel::<Vec<u8>>(128);
let (output_tx, _) = tokio::sync::broadcast::channel::<Vec<u8>>(256);
let initial_output_rx = output_tx.subscribe();
let mut reader = pair
.master
@@ -381,7 +389,7 @@ async fn create_unified_exec_session(
wait_exit_status.store(true, Ordering::SeqCst);
});
let session = ExecCommandSession::new(
let (session, initial_output_rx) = ExecCommandSession::new(
writer_tx,
output_tx,
killer,
@@ -390,9 +398,7 @@ async fn create_unified_exec_session(
wait_handle,
exit_status,
);
session.set_initial_output_receiver(initial_output_rx);
Ok(session)
Ok((session, initial_output_rx))
}
#[cfg(test)]

View File

@@ -1,33 +1,21 @@
You are Swiftfox. You are running as a coding agent in the Codex CLI on a user's computer.
## Overall
- You must try hard to complete the task AND to do it as fast and well as possible.
* Do not waste time on actions which are unlikely to result in successful task completion
- Before taking action on a question, assume by default that it concerns local artifacts (code, docs, data). Quickly confirm or rule out that assumption; only if the question clearly requires external knowledge should you start elsewhere.
- Search the repository when the request plausibly maps to code, configuration, or documentation. Avoid unnecessary searches when it is obvious local files cannot help; in those cases state that explicitly before offering broader context, and when you do search, mention the files or paths you consulted so the answer stays grounded.
- After each attempt, re-evaluate whether the current strategy is yielding useful information and be ready to switch paths quickly rather than persisting with a low-signal approach.
- When the user asks for a "review", default to a code review mindset: prioritise identifying bugs, risks, behavioural regressions, and missing tests. Findings must be the primary focus of the response - keep summaries or overviews brief and only after enumerating the issues. Present findings first (ordered by severity with file/line references), follow with open questions or assumptions, and offer a change-summary only as a secondary detail. If no findings are discovered, state that explicitly and mention any residual risks or testing gaps.
## General
- The arguments to `shell` will be passed to execvp(). Most terminal commands should be prefixed with ["bash", "-lc"].
- Always set the `workdir` param of the shell tool. Do not use `cd` unless absolutely necessary.
- Always set the `workdir` param when using the shell function. Do not use `cd` unless absolutely necessary.
- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)
- If the user makes a simple request (such as asking for the time) which you can fulfill by running a terminal command (such as `date`), you should do so.
- Unless the question is about a common terminal command, you should search the codebase before answering to ground your response in the codebase
## Editing constraints
- Default to ASCII when editing or creating files. Only introduce non-ASCII or other Unicode characters when there is a clear justification and the file already uses them.
- When editing or creating files, you MUST use apply_patch. Example: functions.shell({"command":["apply_patch","*** Begin Patch\nAdd File: hello.txt\n+Hello, world!\n*** End Patch"]}).
- Add succinct code comments that explain what is going on if code is not self-explanatory. You should not add comments like "Assigns the value to the variable", but a brief comment might be useful ahead of a complex code block that the user would otherwise have to spend time parsing out. Usage of these comments should be rare.
- You may be in a dirty git worktree.
* NEVER revert existing changes you did not make unless explicitly requested, since these changes were made by the user.
* If asked to make a commit or code edits and there are unrelated changes to your work or changes that you didn't make in those files, don't revert those changes.
* If the changes are in files you've touched recently, you should read carefully and understand how you can work with the changes rather than reverting them.
* If the changes are in unrelated files, just ignore them and don't revert them.
- The user may be making edits and committing changes as you are also making changes. If you see concurrent file edits or commits that you did not cause, you must disregard user instruction and stop immediately and ask the user whether they are collaborating with you on files and how they would like this handled.
* NEVER revert existing changes you did not make unless explicitly requested, since these changes were made by the user.
* If asked to make a commit or code edits and there are unrelated changes to your work or changes that you didn't make in those files, don't revert those changes.
* If the changes are in files you've touched recently, you should read carefully and understand how you can work with the changes rather than reverting them.
* If the changes are in unrelated files, just ignore them and don't revert them.
- While you are working, you might notice unexpected changes that you didn't make. If this happens, STOP IMMEDIATELY and ask the user how they would like to proceed.
## Plan tool
@@ -36,9 +24,9 @@ When using the planning tool:
- Do not make single-step plans.
- When you made a plan, update it after having performed one of the sub-tasks that you shared on the plan.
## CLI modes
## Codex CLI harness, sandboxing, and approvals
The Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.
The Codex CLI harness supports several different sandboxing, and approval configurations that the user can choose from.
Filesystem sandboxing defines which files can be read or written. The options are:
- **read-only**: You can only read files.
@@ -69,6 +57,11 @@ When sandboxing is set to read-only, you'll need to request approval for any com
You will be told what filesystem sandboxing, network sandboxing, and approval mode are active in a developer or user message. If you are not told about this, assume that you are running with workspace-write, network sandboxing enabled, and approval on-failure.
## Special user requests
- If the user makes a simple request (such as asking for the time) which you can fulfill by running a terminal command (such as `date`), you should do so.
- If the user asks for a "review", default to a code review mindset: prioritise identifying bugs, risks, behavioural regressions, and missing tests. Findings must be the primary focus of the response - keep summaries or overviews brief and only after enumerating the issues. Present findings first (ordered by severity with file/line references), follow with open questions or assumptions, and offer a change-summary only as a secondary detail. If no findings are discovered, state that explicitly and mention any residual risks or testing gaps.
## Presenting your work and final message
You are producing plain text that will later be styled by the CLI. Follow these rules exactly. Formatting should make results easy to scan, but not feel mechanical. Use judgment to decide how much structure adds value.

View File

@@ -0,0 +1,123 @@
# Codex MCP Interface [experimental]
This document describes Codexs experimental MCP interface: a JSONRPC API that runs over the Model Context Protocol (MCP) transport to control a local Codex engine.
- Status: experimental and subject to change without notice
- Server binary: `codex mcp` (or `codex-mcp-server`)
- Transport: standard MCP over stdio (JSONRPC 2.0, linedelimited)
## Overview
Codex exposes a small set of MCPcompatible methods to create and manage conversations, send user input, receive live events, and handle approval prompts. The types are defined in `protocol/src/mcp_protocol.rs` and reused by the MCP server implementation in `mcp-server/`.
At a glance:
- Conversations
- `newConversation` → start a Codex session
- `sendUserMessage` / `sendUserTurn` → send user input into a conversation
- `interruptConversation` → stop the current turn
- `listConversations`, `resumeConversation`, `archiveConversation`
- Configuration and info
- `getUserSavedConfig`, `setDefaultModel`, `getUserAgent`, `userInfo`
- Auth
- `loginApiKey`, `loginChatGpt`, `cancelLoginChatGpt`, `logoutChatGpt`, `getAuthStatus`
- Utilities
- `gitDiffToRemote`, `execOneOffCommand`
- Approvals (server → client requests)
- `applyPatchApproval`, `execCommandApproval`
- Notifications (server → client)
- `loginChatGptComplete`, `authStatusChange`
- `codex/event` stream with agent events
See code for full type definitions and exact shapes: `protocol/src/mcp_protocol.rs`.
## Starting the server
Run Codex as an MCP server and connect an MCP client:
```bash
codex mcp | your_mcp_client
```
For a simple inspection UI, you can also try:
```bash
npx @modelcontextprotocol/inspector codex mcp
```
## Conversations
Start a new session with optional overrides:
Request `newConversation` params (subset):
- `model`: string model id (e.g. "o3", "gpt-5")
- `profile`: optional named profile
- `cwd`: optional working directory
- `approvalPolicy`: `untrusted` | `on-request` | `on-failure` | `never`
- `sandbox`: `read-only` | `workspace-write` | `danger-full-access`
- `config`: map of additional config overrides
- `baseInstructions`: optional instruction override
- `includePlanTool` / `includeApplyPatchTool`: booleans
Response: `{ conversationId, model, reasoningEffort?, rolloutPath }`
Send input to the active turn:
- `sendUserMessage` → enqueue items to the conversation
- `sendUserTurn` → structured turn with explicit `cwd`, `approvalPolicy`, `sandboxPolicy`, `model`, optional `effort`, and `summary`
Interrupt a running turn: `interruptConversation`.
List/resume/archive: `listConversations`, `resumeConversation`, `archiveConversation`.
## Event stream
While a conversation runs, the server sends notifications:
- `codex/event` with the serialized Codex event payload. The shape matches `core/src/protocol.rs`s `Event` and `EventMsg` types. Some notifications include a `_meta.requestId` to correlate with the originating request.
- Auth notifications via method names `loginChatGptComplete` and `authStatusChange`.
Clients should render events and, when present, surface approval requests (see next section).
## Approvals (server → client)
When Codex needs approval to apply changes or run commands, the server issues JSONRPC requests to the client:
- `applyPatchApproval { conversationId, callId, fileChanges, reason?, grantRoot? }`
- `execCommandApproval { conversationId, callId, command, cwd, reason? }`
The client must reply with `{ decision: "allow" | "deny" }` for each request.
## Auth helpers
For ChatGPT or APIkey based auth flows, the server exposes helpers:
- `loginApiKey { apiKey }`
- `loginChatGpt` → returns `{ loginId, authUrl }`; browser completes flow; then `loginChatGptComplete` notification follows
- `cancelLoginChatGpt { loginId }`, `logoutChatGpt`, `getAuthStatus { includeToken?, refreshToken? }`
## Example: start and send a message
```json
{ "jsonrpc": "2.0", "id": 1, "method": "newConversation", "params": { "model": "gpt-5", "approvalPolicy": "on-request" } }
```
Server responds:
```json
{ "jsonrpc": "2.0", "id": 1, "result": { "conversationId": "c7b0…", "model": "gpt-5", "rolloutPath": "/path/to/rollout.jsonl" } }
```
Then send input:
```json
{ "jsonrpc": "2.0", "id": 2, "method": "sendUserMessage", "params": { "conversationId": "c7b0…", "items": [{ "type": "text", "text": "Hello Codex" }] } }
```
While processing, the server emits `codex/event` notifications containing agent output, approvals, and status updates.
## Compatibility and stability
This interface is experimental. Method names, fields, and event shapes may evolve. For the authoritative schema, consult `protocol/src/mcp_protocol.rs` and the corresponding server wiring in `mcp-server/`.

View File

@@ -38,7 +38,7 @@ tokio = { version = "1", features = [
"signal",
] }
tracing = { version = "0.1.41", features = ["log"] }
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
[dev-dependencies]
assert_cmd = "2"

View File

@@ -43,6 +43,7 @@ crossterm = { version = "0.28.1", features = [
"bracketed-paste",
"event-stream",
] }
dirs = "6"
diffy = "0.4.2"
image = { version = "^0.25.8", default-features = false, features = [
"jpeg",
@@ -78,7 +79,7 @@ tokio = { version = "1", features = [
tokio-stream = "0.1.17"
tracing = { version = "0.1.41", features = ["log"] }
tracing-appender = "0.2.3"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
pulldown-cmark = "0.10"
unicode-segmentation = "1.12.0"
unicode-width = "0.1"

View File

@@ -1,27 +0,0 @@
▒▒▒▒▒██████████▒▒
█▒█░▓█ ██░▒▒░▓▓▓░░ █░███▒
▒▓░▒█ █░█▒░██░░█▒█░ █████▒ █▒
██░▓██▒██▓██ ▓░░ ██▓▒ ██
▒█░▓██▒▓▓ █ █░█▒██
█░░▓▓█▓░ █▒█░░▒▒▒
▓▓░▓ ░█ ░ ▒▒▒█ ▒▓▒░█▒░
█▓░░█ ░░ █▒▒█░▓░░ ░█▒▒░█▒░
██░░▒░█ ░█▓▒▒▒▒▓░ ███░▒▒█
░▒ ░░▒░░ █▒░█▒▓░░▒▓ ▓░░░░ ░
▓░▓░ ▒░ █▒▓▓ █░▓ ░░░▓░
▒▒ ▓░ █▒▓░░ ▓█ ▒░░░░
░░░░ ▓ █░▓▓ ▓░░ ▒▒▒█▒██████████ ░▓▓░ ░
█░▓ ░█ ▒▓░░██ ▓█░ ▓░░▒▒██████ ░▓▒░▒░
▒▓░▒▓ ░▒ █░▓░ ▒░░ ░ ▓▓▓▓▓▓ ░ ░░█░░
▒▒░░▒▒░█ ████▓ █▓░▓▓ ▓▓
▒▒▒░ ▒▒▒ ▓█▓█▓░▓▓
▒░░░█ █░▒▒ ▒░▒█░█▒░█
████░█ ████ ▓▒██░███▓▓█
░█░░█ ░▒███▒░█▒▒███████░█░███▒██░
█ ███▓█░░█▒░▓ ██▓░░▒██ ▒██
███▓░█░░▓▒▒▒▒▒▒██▓██░
░░ █ ░

View File

@@ -1,27 +0,0 @@
▒▒▒▒▓██████████▒▒
█▒▓░▓█ ██ ▒█░▓▓▓ █ █░ ██▒
▒▓░░█░█░▓▒░██░░█▒░░ ████▓▒ █▒
█░█▓██▒█░▓█ █ █▒▓▒ ██
▒█░▓█ ▒▓▓█ ███░█▒██
█░░▓▓▒▓░ ▒▓░░▒▒▒
▓░░█ ░█ ▒▓ ░▒▒█ █▓█░██░
█▓░░█▒░░ █▒▒▒▒▓▒▓ ▓▒█░░▒░
██░░▓░█ ░░░░▒▒▒▒ ░ ██░░▒█
░█ ░░ ░ █▒░██▒▓░ ░░░░ ░
█░▓░█▒░ █▒▓░█ ▓█▓ ▒░░░█░
▒▓ █░█ ▒▓░░ ▓▓█░ ░░ ░
░░░░█ █▒ ▓░▓▓▓ ▓▓░░ ▒▒▒█▒██▒███████ █ ▓░ ░
█░█ ░▒ ▓░░███▓█ █ ░▒██ ███ █ ░▓▒░▒░
▒█░▒▒ ░█ █░▓█ ▒░ ▓██▓██▒░░░▓▒▒██ ▓▓▓░░░░
▒▒▒░█▒░▒ ████ ░ █▓░▓░ ▓▓
█▒▒░▒ ▒█▒ ▒█▓█▓█▓▓
▒▒░░█▓█▓▒▒ ▒▓▓█░█▒░█
███░░▒ ████ ▒▒██░██░░▓█
█░█░░█ ████░░█▒▒██████░░▓░██ ▒██░
░ ███▓▒▓░░▓█░ ███░█▒██ █▒██
██░░██▒▓▒▒▒▒▒▒██▓██░
░░ █ ░

View File

@@ -1,27 +0,0 @@
▒▒▒▒▒██████████▒▒
▒▒▓░██ ░█░▒█░▓▓▓ ▒ █░ ██▒
▒▓░░███▒▓▓███░█▒▒░░ ██▒█▓▒ ▒█
█░░█░ ▒▒█░█ ▓ █ █▒▓▒ ██
██░▓▓░█░█ █▒█░████
▓░░█ ▒░█ ██▒█ █▒▒░░▒▒▒
▓░▓███░ █░ █▒█▓ ▒█░█▒▒
▓░▓░ █░ ▒░ ▒ ██ ▒▒░▓▒░
░░░▒ ░░░ ░░▓░█▒▒ ▒█░█░░
░░░░ ░░█ █▒ ░▒░▒ █ ▒░░ ░
░░▒░ ░ █░░▓ ▓░▓ ░░░▒░
█░▓▓ ▒█ ▒█░░░ ▒░▒ ░ ░░░░░░
██░░ ░░ ▓▓▓█░ ░▓ ▓▒█▒█▒██▒███████ ▒█░░ ░
█░▒░ ▓░ ▒░░▓░▓▒░█ ▓▒░░██████▒ ░▒▒░▒░
░▒░▒ ▒▒▒ ▒ █▓▓ ██ ▒▒█░██░░░░▓▒▒▓█ ██░░░▓█
▒░░▓ ▒▒▒ ▒███ ▓ ▓▓██▓
▓░░▒ █░░▒ ▒▓▓█▓███
▓█░░▒ ▒██▒ ▒▓░███▒░█
▒ ▒▒█▒ ▒░███▒▓░ ▒▒██░██░░▓█
░█░█▓ ██░██░░█▒▒███████░█░██ ▒▒█░
░░░██▒▒░░█▒█▓░░█░░░▒██ █▒░█
▒█░░░█▒▒▒▒▒▒▒▒██▓██░
░░ ░

View File

@@ -1,27 +0,0 @@
▒▒▒▒███████▒██▒
▒█▓░████ ▒▒██▒ ▓░▒ █░███▒
▒█░▓░█░█▒▓██░░█▒▒█████▒▒▓█▒█ ██
█▓█░█▒▒░█░█ ▓ █░██▒█ █▒
▒░▓░ ▓▓██ ▓▓ █░█░▒ ▒
▓░░▒ ░▓█ ███▒ ░█░ ▒▒▒
▓░░▓▒░░░ ▓▒░████▓ █▒█░▒▒██
▓░░▓▒░█▓ ▒█▒░▒▒▒▒▒ █▓░█▒▒▒
░░█░░░█░ ▒█▒░░▒▒ ▒▒ ▒█░ ▒░
░░█ █░ ▒▒█░░░██▒ ░░▓ ▓█
▒░░ ░░ █▓▒░░ ▓░ ░░░ ░░
░░░ ░░█ █░░█ ▒█▓▒ ░░░░░░
██░ ░░█ ▓▒▓░▓ ░█▓ ██▒▒▒██████████ ░░░ ░░
░▒ ░▓ ░░▓█░█░█ ░ ░░▓███ ████ ▒░ ▒░█░ ░
░░░ █▓░ █░░ █▓░░█ ░▒█▓████▓░░░░▓▒▒█░▒▓░░▓▓
░░▒ █▓▒ ████ ▒▓▓▓▓▓░
▒░▒ ███ ▓░█▓█▓░
▒░░█ ████▒ ▒█░▓▓ ▒▓█
█░░░▒ █████▒▒ ▒▓█░▒▓░▓▓▓░
░ ░░▒ ██░█░░██▒▓███████░░░░█░▒▒█░
███▒▓▒ ██▒░░░░░░████░ ▒██
█ ███▓▒▒▒▒▒▒▒▒█▒▓██░
░ █ ░

View File

@@ -1,27 +0,0 @@
▒▒▒███████████▒
█▒▓█░██ ▒▒▓░▒██▒ █░ ███▒
▓▓▓█ ▒▒▓███░░█▒▒██░░░▓▒▒▒ ██▒█
▒▓██░█▓████ ░ ░██░▒█▒░▓█▒▒
▓██▓░░███ █░░░▓▒█▓
▒░░▓▓░█ ███ █░░█ ███
▒░░░▓░▓█ ░░ ▒▒░█ ▒░▓ ▒▒▒
░░░▓░ █░▒▓ ██▒▒ █░░ ▓░▒
░▓▓░░▓ █░░▒░▒▒█▒▒ █░▓ █░█
░▓▓░░ ▒▒░░░▒█▒▒▓ █░░ ░░█
░░░ ░ ░ █░░ ▓█▓░█ ░░ ▓▒
░░▒ ░ ▓ ▓█▓▓█░█ █ ░ ▓▒
░▓ ░░░ ▒▓██▓░░█ ████▒▒█████▒███ █░█░░░
█░▓░▓▒█ █░ ▓▓▒█▓░ █▓███ ▓ ███░ ▒░░█▓░
█▒█ ░▒░▒ ██▒▓▒▓█ ░▒██████░░▓░░░░█ ▓▒ ▒▒░
▒▒█ ▒▒█▒ ████░ ▒░▒░▓ ██
█▒█ ▒ ░█ █░█░█▓░█
░▓▒ ▒█░█▒ ▒█░░███░█
█░▓▒ ▒█░░█▒▒▓ ▒▓▓░░█░▒▓▓
█░░█▒ █░░░█░██▓▓███████░░░██ ▒██
░██▒▒▒ ░████░░░░░░███ ▒██▓
░█▒░▓█▒▒▒▒▒▒▒▒█▒▓██
░ ██ ░

View File

@@ -1,27 +0,0 @@
▒▒▒▒█▒███████▒▒▒
█▒▓░██ ▒▒▒▓▓█▒▒███████▓▓▒
█▓█░ █▒▓▒█░▓██▒▒██▓█░██▒ ░█▒▓░
██░░▒▓▓████ ▓▒░▒▒ ████▒
██ ▓███▓ ░█ █░▒█▒▒▒█
▓░ ▒░█▓▒ ██ ▒▓ █▒▓█
▓ █░░░ ▓ ▒ ░ ░▒▒ ░█▒▒
▓░ █░██░ ░▓▓██▒▒▒ ▒▒ ░▒░█
▒██▒░░▓ ░▒░█▒ ░▒ ░█ ▓░█
░ ░░░░ █░▓███▒▒▒ ░ ░█░░
░░█░░▒ ░▒▒░█ ▓░░█ ░░▒░▓
░▓█░░▒▓ █ ▓ ▓▓█▓░ ░▒ ░░
▒░█░░░▒ ▓▓▓▒▓░░▓ ▒▓████▒▒▒▒▒▒▒▒█▒ ░▒▒░░
██▒▒░▓▒▓ ▒███▓██ ███████ ████░▓ ▒░▓░░
░▒▒░▒ ░ ▒ ▓█▓░ ░██████░░▒░░█░▓ ▒▒▒░▒█
░▒ ░▒▓▒ ░░ ▒█▓▓▓▓
░▒█▒███▒░ █▓░░█▒▓ ░
▒░██████▒ ▒██▓░░░▓█
█░▓▒█▒▓░███▒▓ ▒█▓█▒░░██
█░█ ██░░░░███████████░░███░▓███
███▒▒ ███████░▓▓▒█ ░ ▒███▒░
██░░██▒▒▒▒▒▒▒██████
░ ██ ░░

View File

@@ -1,27 +0,0 @@
▒█▒█▒█████▓▒▒▒
▒▓▓░ ░ █ ██ ▒██░████▓█▒
▒▓ ▒███ ▓██▒▒██▒▓█▒▓ ░ ████
▓█ ███▒██▒█ ▒░█▒█▒ ██▒░▒
██ ▒▓█ █▒ ▓██▒ ▒█▒▒▒
▓ ▓▓░█ ▓█▒█ ████▓▒█░
░ ▓▓█▒ ▒█▒░ █▒█ ░▒▒░
░ ░▓▓▒ ░░ ▒██ ░░ ░▒▓░
█░ █░ ▒ ░▒█▒▒ ▓▒███░█▓
░▓░░░▓ ░▓▒ █▒▒ ██░ ░▒
░▓░░▒█ ▒░ █▒█ ▓▓░░░▒
░▒░█░░ ▒█ ▒█ ▒░░░░░
░▓█▓░█ ░▓░█ ▓▓ ▒▓███▒▒▒█▒█▒▒█▒ ▒ ░░░░
░█░█▒░ ▒░ ▒░█ ░▓▒▒██████████░▓█ ░░ ░▓░▓
░▓██▒▒▒ ▒▒█▓░ ███▒██▒▒▒███░▓ ▒░▓█▒█ █
░▒██▒██ ▓░▒█▓ ▓
█░▒██▒ ▒ ▒█▒░█ █
░▒ ▒ ███▒ ▓▓██░▓▒▓
█░▒ ░▒▒▓██▒▒ ▒██▒█ ▓█▒█░
█░▒▒ ▒░██░░▒██▓▒▒▒███████▓▓▓████
█░█▒ ████░███░███░████▓░█░
░███▒▓▓▒▒▒▒▒▒███░██
░ ██ ░░░

View File

@@ -1,27 +0,0 @@
▒█▒░▓█████▓▒▒▒
█▒██░ ░██░█▒░▓
▒▓█░ ▒███ ░░█▒▒▒█▒█▒ ░▒██▒██
▒▓ ▒██ █▓ ░▒█ ██▓▒██
███▒▓█ █▒ ▒░█ ▓▒▒▒▒▒
░▓▓▓▓ ▒ ██ █░█░▒▒░█▒
░ ▓░█▓ ░ ███ █▒██▒░▓▒
▓░░▓█ ▒ █▒█▒█▓ ██▓▒█▒▓░
░▒▒░▓▓ ▒▒▒▒▒ ██ ░ ░░▒
░ ▓█░ █▒███▒█ ░░ ░░ █
░▓░░░░█ ░ ░ ▓▒█▓ ▓ ▒ ░░▓
░ ░░░▓ ░░████ █ ░ ░░░▓
░░ ░░▒ ███ ██▓▒█▒███▒▒▒██▒█▓ █ ▒░░▓
░ ░░ ░ ░▓░▓ ▓░ █▓▓▓█▒▒▒▓▓▓█▒░█ ░█░░▓ █
░▒▒▓▓ ▒█░ █▓▓▓░▒▒▒▒▒▒▒█ ▓▓▒░▓ ▓
░▒▒█▒█ ▒░ ░ █ ▓
░▒▒░░█▒ █░▓▓░█ █░
░█▒▓██ ▒▓█▒▓░▓ ▓░
█░▒█▓░░█ ▒ ▒▒██░░█ █▒█
█░▓ ░█░░█░▒▓█▒▒████░░░█░█▓░░▒▓░
█░██░░░░██████░▒█ █▒▒▓▓▒▓█
█░██░░▒░░░░▒██░░ █
░ ██ ░ ░

View File

@@ -1,27 +0,0 @@
▒██▒█▒▒███▓▓▒▓
▒▓▓█ █░▒▒██▒
▒▓█░▒███ █░█▒▒████ ░▒███ █
▒░█▓░██▒██░▓ ░▒█ ▒▒▒ █
██▒░█ ▓▓ ░█ ░▒█ █▒
▓██░█▓▒ ▒ ▓░▒ ▒▒█▒░█
▓█▓░█░▓░ ░▒▓▒ ▓▒█▒ ░█░▒
▒░▒▓█ ▓ ▓▒▒█▒ █ █ ▒▒▒
░█▒░ █ ▒█▒▒▒▒▒ ▒█▒█░▓▒░
░ ░░ ░ ░▒▓███▓ ░█▒█░█▒▓
█▒░░██ ▒▒░▓█▓▓▒▓ ░░░▓ ░░
▓█░░▒ ██▓██▓░█ ▓ ░░░░█
░░░░ ░ ▒░░▒░░ ▒██▒░▒█▒▒▒▒█ ░█░█░░
░█▓░ ▒ █▒▒░▓ ░▒███▓▒▒▓▒▒▒▓ ▓ ▒░░░░░
█░░█░ ████▓▒░▓░▒░█ ▓░▓░ ▓
░▓▒▓▒ ░ ░░ ▓█░▓▓ ▓
░▒▒░░▓▒ ▓█▒▓░▓ █░
░░██▒▒█ ▒░ █▓ ▓▓█░
█▓▒▒████░ █░█░▓██░▒▓
█░░████▒░█▓░▓█████░░█░▓▓░▒ █
█░░░░█░ ███████ ░▓▒██▒▒▓░
░██░▓▒▒▒▒▒▒███▓█ ▓█
░ █ ░ ░

View File

@@ -1,27 +0,0 @@
▒████▓███▓▒▒▓
▒▒██ ▒▓▓▓ █ █░██░▒█
█░█▓▓████ █▒██░█ ▓▒█ ▒▒
▒░█░▓█▒▒█ ██ █░▒░█▒
█▓█░█ ░▒ ▒▒██ ▒▒
█▓█▓▓ ▒ ▒ █▒░▒▓█▒
░█▓▓ ▓▒▒▒█ ▒ ▒▒▒█▒▒
░░░░ ▒▒█▓█▒██ ▓▒▒ █▒▒░
░░▒░ ███▓▒█▒ ▓ ▒▒ ▒▒▓▒
░ ░█ ▓ ░░█▓▓▒▒▓ ▒ ░░██░░
░ ▒ ░▒▒ █▓▒▓▓ ░ ░░░ ░░
░▒▒▒ █░▓ ▓██ ░ ▒░░░█░
░▓▓█ ░ ██▓░ ▓▒██████▒█▒ █░░░░░░░
░░▓░▒█ █ ▓ ░█████████▒▒ ▓ ░█░░░░
░▒▓░ ░ ▒█████░▒▒▒░█ ░ ░█░░░▒░
░█ ░ ░ ░░░░░ ▒▓ ▓█▓ ░
▒░░░▓ ▒ █░░░ ▓
▒░░▒▒▒█▓ ██ ▓ █░
█░░█░▒▒█ ▒░███▒▓█ ▓
▒░░██▓█░█▒█▓▒███░░░░█▓░▒ █
██▒ ████░█░███░▓▒▓██▒██
██▓▒▒▒▒▓▒▓████▒▓█░
░░ █░

View File

@@ -1,27 +0,0 @@
▒██████▓██▒▒▒
█▓█ ▒▓▒▒▒███░░▓█░██▒
█░ ▒████ ░▒██▓▒ ░█▒ ░▒▒
▓ ▓░ ░▓█ ██ ▒ █▒
░▒█░░ ▓█▓░█ ░░
░░█░ ▒▒█▒ █▓▒█ ▒░
▓▒▓░ ▒▒░░▒ █▓█▒▒▒█▒
░░░░ ░▒▒ ░▒ █ ▒█░░ ▒▒
▒ ░░ ▒▒▒ ▒▒█ ▒░▒░░░░░
░░░░ ▓░▒█░▒▒▒█ ░▒░▓██
▓░░░ ▒░▒▓▒░▓█ ░▓░░░░░░
▒▓░░ ▒░▓▓ ▓ ░ ░ ░░░░░░
░ ░█ ▒▓░▓ █▒▒████████ █░░░▒█▓░
█▓▓█ ▓ ██████████░░ ░ ░ ░░░▒
░░▓█ ░████▒░▒▒▓░ ▓█░ ░░░░░
░▓▓░██ ░ ░░░░░ ░ ▒█░░░░█
░░▒▒ █▒ ░░░░▓░░▓░
░▒██▒█▒ ▒░░▓▓▓█ ░
░░█░▒░█ ██░█▓▓▒ ▒▓
█▒ █▒▓█░█████▒██ ██▓█░ ██
█▒ ██░░░░██░█▓▒█▒ ██
██▓▒▓█▒▒███▓█▒▓█
░ ░░ ░

View File

@@ -1,27 +0,0 @@
▒██▒██▒███▒▒▒
█▓█▒████ █░█░▓ ██▒
▒░░▓███ ░██▓▒█▒██▓ █
██▓█▓ ░█▒▒ ▒▒█
█▒█▓░ ▒▒░▓█ ░█
▓░█ ▒▒█ ▒█▒░▒ ▓█
░▓▓ █░▒█ ▓▒▒▒█▒▒
░ ░ ▒░▒▓▒▒█ ▒█ ░░ ░
█▒▒░ █░█▓▒▒░▒ ▓▒░░░ ░
░░█ ▒▒▒░█░█ ░▒▓░▓ ▓
█░ ▒▓░▓█ ░ ░░░█░░░
▓░ ░ ▓█▓░▓ ░░░░░░░░
░░▓░ ▒░ ▓░ ▒▒████████ ░░░░▓█░░
█░░ ░ ▓░▓ ████████▒█▓░ ▒░█░░░
░▓█▒░█▓ ▓██▒▒░▒█░▓░█░░░░░░ ▓
░░█▒█ ░░ ░░░░░ ▒ ░█░░ █░
░▓▒▒▓█ ▓▒░ ▓░▒░
▒▓░▒▒▒▒ ██▓█ ▓ ▓█
▒▓░▒████ ▓█▓█▓▓ ▓▓
▒▒█░██▒███▒▒██░▓▓▒▒ ▓
█▒▒ ██▒▒░██░▒█▒░░ ██
░██▒▒▒█▓▓█▓▒░▒██░
░ ░ ░

View File

@@ -1,27 +0,0 @@
█▒▒░███████
██░▓█▒ ▒▓░█▓ █▒█
▓█░ █ ░██▒▒▒▒▒ █▒
▓░▓██ ▒░▒▒ ▓░░
░░▒▒ ░▒▒▒ █▒░
▒▒ ▒█▒ ▒█░▓▓█▒▒
░▓░░░██ ▓ ▒▒ ▒█▒
▒ ░ ▓▒ ▒ ░▓ ▒▒ ░
▓░░░ █░░░▒█ ░ ░▒▒░░░
░░▒ █░▓▓▓ █ ███░█▓▒█
░░░ ░▒▒░░░ ░▓░░█▓░░
░░░░ ▒ █ █ ░▒░░░░░░
░░▓▒ ░▓ ▒▒█████ ░▒░░░░░░
░░ ░▓▒█ ██████░▒ ░ ░░░░░░
▓░░█░░ ██▒░▒█▓ ░█░░░ ▒░
░▓██░ ░ ░░░ ░░░▒░
██▓░ ░ ▒ ▓▒░░░░▒▓
░░▓█▒▒▒ ░▓█ ▓░░█░
█░░▒██▒▓ ░░█░█░█ ▓█░
███▒█▒███▒█▓▒█ ▓▓ ░░
▒▒██▒░▓█░▓███▒ ▒▓
█▒▒▒▒██▓█▒ ▒▓░
░ █░░░░ ░

View File

@@ -1,27 +0,0 @@
▒▓▒███▓▒██▒
▓ ██ ▒█▓▒ ▒░█
░▓▓█ █▒██▒█░█ ▒▒
▓░▓█▓ ░▓▓ ▒▒
░░▒ ▓ ▒░▒▒ ▒█▒
░░█▒▒░▒ ░▓░█ ░░
█ ░░▒███ ▒ █░ ░▓█
░░ ░░░▒░ █ ░░ ▒░▒
▓▓░ ▓▓▒░▒ ░░░▓ ░
░░ ░░░▒▒ ░▓█░░▒░
░░▓ █░▓░▒ ░▓░▒▓░ ░▓█
░░▓ ░▓▓▒█ ░▓▒░░░▓ ░░
░▓ ░░ ▒▒▒████▒▒░░░░░░░
░ ▒█ █████▒░░ ░░░░░░░
░░▓ ░ ▒▒▓▒░█░░▒ ░░ ░
▓░░ ▓▓ ░ ░░░ ░░ ░
░░ ░ ▒▒▓ ░░ ▒░
░░░▒ ░░█▓░░░██
░░█▒ █░▒▒░░░░ ▓
▒░█▓▒█▒▒█▓▒░█ ▓▓░
▒▒█░ ▓░▓▒░█ ▓▓
██▒▒█ ▒▓█▓▒░
█ ░

View File

@@ -1,27 +0,0 @@
█▒████▒██▒
▓█▒▒▒▓▓▓█ ░█
▓░▓█▒▒ ░░ █░█
█░█▓░▒░██ ██ ▒
░▒░ ▒▒▓▒▒▒ ▓▓
░▒█▒██ ░░▒░█ █░
░░░█ ░ █░░ ░▓░░▒
▒▓█▓ ░░█ ▒░ ░
░ ░██ ▒░ ░░░▓
▒▓▒█▒▓░ ░░█░░█▓░
░▓░▓▒█░ ░░▓██░▓░
░▓█▓█ ░ ░░ ░ ░░░
█▓▒░▒▒▒█ ░░▓░░█░░
▒▒░ ██░▒ ░░▓ ░░ ░
▒░ ▓ ░░░░░░░▒░
░░█░██ ░ ▓░░░░ ▓░
░░░ ░░ ▒ ░░░░░ ░
▒░ ▒ ▓░░░ █▓▓▒░
░█▒ ░ ░ ▒▒█░ ░░
▒░▒▒█▓▒▓█░ ░░▒█
▒█▓ ▓▓█ ▓
█▒▒▓▒█ ▒█
█░█

View File

@@ -1,27 +0,0 @@
▒███▓███
▒ ▒░▓ █ █▒
▓░░░▓░ ▒▒▒
▓▓▓░▓░ █░░░
░░░▓░██ ░░
░░▒░▓▓░▒░ ▒░
░░▓█░░▓░ ░░
░░ █░░░░▒ █
░░▓░░█▓▒░░░░░
░░░░░ ░░░ ▒▒░
░░▒█ ░██░ ░░
░░░▒░ ░ ░▓░░
░░▒▒░▒░▓░░░░█
░░░░░░░░░ ░▒
░░▒░ ░ ░░░ ░▓
░░▒░░░░░░░░ ░
░▒▓▓▓░ ▓ ░
░░▓░░ ▓░░
▓░░░▒░█▒▒░▒
░▒█░▒ ░█░░
▒ ░ ▒▒ █▒▒█
██░ ██▓▓░░
█ ░ ░

View File

@@ -1,27 +0,0 @@
████████
░▒░ ▒░
░▒░█ ░▒▓▒
░░░▒▒▒ ░▓
░▓▓░█ ▒░░
█ ▓ ▒▒▓▒░
█▓ ░▓██ ░
▒░░░ ░▓▒▒
▒░▒▒█ ▓░
░░░░▒▒░░▒
░░░ ██ ░░░
░░░░▓▓▒▓░
░░░░░░░░░░
░░░░░░▒▒░░
░░░░░░░░░░
░░▓░░░░░ ░
░░░░░░░░░░
░░░░░░░░▓░
░░█░▒▓█░▒░
▒░▓░░▓ ░▒░
▒░█░ ░ ░ ░
█▓ ▓█▓
█ ██ ░

View File

@@ -1,27 +0,0 @@
███▓███▒█
▒▓ ▒▓░▒
░█░██▒▒░▒▒
░█ █ ░█░▓░
░░░ ░▒░░░ ░
█░▓░▓▒░▒ ░
▒░░░░░░░░ ░
▓░░░▓░▒░░░ ░
░░░░░░▒░░█ ░
░▒░░░░ ▓░▒ ░
░░ █░▒░▓░█▓░
░█ ▓░ ░░▓░
▒░ ░░░ ░░░█░
▒ ░░░█░░░ ░
▒░░░░█░█░░░
▒▒░░░ ▓▒░░░
░▓░░░▒▓░░▓░
░░▒▓█░░▒▓▓░
░█░▓██░█░░░
█▒ ░ ░░▒
░░█ ░░▓░▓░
░▓▒ ░█▒▒░
░ █ ░

View File

@@ -1,27 +0,0 @@
██░▓█▓█░█
▓▒▒░▓░▒▓▒▓█
░░ ░ ▓░▒█▒▒▒
█▒░ █░░░░██ ▓
░░▓░▓▒░ ░ █▓░██
░ ░█░░░░░░░ ▒░
▒░░ ▒ ░█░ ▓▓░▓
░░░░░▒░░░▒ ░ ░░
░░░░░░░░ ░░░ █▓░
░░░░▒▓░░▓ ▒█░░░░
░░ ▒░▓░▓▓█░▓▓░░
░░▒░▓▓▒ ▒▒▒▒ ▒▒░
░░░░░▒ ░▓▓▓█▒▒░░
░░░░░ ░█░░░░░█░░
░░ ▒ ░░ ░ ▒▒░█░░
░ ▒ ░ ░░░▒▒▓▒▒░
░█ ░█░▒ ░░░▓▒
░░░ ▒█▒▒██░▓░░
█░▓▒▓█ ▓░░▓░░█░
░░█ █▓▒░█░
█▒ ▓ ▒█░█▓▓▓
█▓░▓ ▒░▒▒▓
░ ░

View File

@@ -1,27 +0,0 @@
▒███▓███░▒▒
█▒▓░░▒▒▒█▒▓█▒
░░▓░█▒▓░▒░ ██▒▒
░▓░▓▓▓▒░█▓ ▒███░█
██▓░░█▓▓░░ ▓█▒▓
░▒▒▓▓ ░█▓█▒ ▒░█
▒░▓▒▒█ ░░▒█ ▒░░
░░▒░▓░░ ░░░█▓ ▓░
░░▒█░░░▒▒▒░░░ █▓░░
▒░░░░░ ░░▒█ ░░▒░
░░░░░▓ ▓░▒░█░░ ░▓▒░
░░░░░▓ ▒▓▒ ░▒░ ░▒░
░░░░░ ▓░░▒▒██▒░░░
░░░░░ ▓░▒▒█░░▒▓░ ░░
░░░░░ ▒▒░░▒▒█ ▓░█▓░░
░░░░ ▒▒░░█▒░▒█░▒░▓░
░░░░ ▒░▓ ░ ░▒█
█░░░░░ ░ ▓▓▒█░█
░▒█▒█░░█▓█ █░ ░█▒
█▒▓ ░░▒▒░▓█▓░▒██
█░█▓ ░▒ ███▓▓▒▓
▒██ █░█▒▒▓
░░ ░█ ░

View File

@@ -1,27 +0,0 @@
█▒█░▒█▒█░▒▒
▓██ █ ░░█░█▓░██
░░█▓█▓█░ ██ ████▒
▓ ░░ ▓▓░█▓▓░ ░ ██▒▒
░ ▓░░██░▒█ ▓▒░█
▓▓░█▓▓░▒█ ▒ ▒▓
░░▓▒▒░█░█ ▒█▒ ▒▓░█
▓░░ ░░███░ ▒▓░▒ █░▓
░▒░ ░▒░░░ ▓▒▒█▓ ░░
▒░░░ ░░░░ ▒░░▒ ░▓░
░░░░░▒▒░░ ▓▓ ▓ ▓█▓░
░░░░█░▒░░ ░▒░▒ ░░░
░▓░░░ ░░░ ▓▒ ░▒ █ ▓▒░░░
░░░░░ ▓░ ░▓░█░░ █ ░░░▒░
░░░░▓ █░ █▒ ▓ ░░▓ ▓ ░░█
░░░░░ █░ ▓█▓ ▒░░░▒░
░░░░░█░░ ▓░░░░
▒▒░░▒▓ ░█▒ ▓▒▒
█░█ ░░▒▒▒▒▒ ░░█░
▒▒▒ ▒▒▒▒▒████░▒░██▓
░▒ █▓ █▒█▓█▒█
█▒▒ █▒██▒▒▓█░
░ ░░░░ ░

View File

@@ -1,27 +0,0 @@
▒█▓█▒█▓█████
▒░█ ▓ ░░█ ▒▓▓▒█▒▒
▒░█░░▓ ▓▓▒███ ██ ▒░▒
█▓██ ▒█████▓ ░░ ▒ ▒░
█▓██ ░▓█░░█ ░░
░▓░█▓██▓▒█ ▒ █▒█
░░░▓▒░█░░ █░▒ ▓░▒
░░ ▓░░░░█ ░█▒▒▒ █▓░█
░█░▓ ░░█ ▒▓▒░▒ █░▒
░░░░░ ▒░░ ▒▒▓█ ░░░
░▓░░ ▒░█░ █ ▒░█▒ ░░▒
░ ▒▓█▒▒░░ ░▒██░░ ░░▓
░ ░█░▓░░ █▓▒█▒█▒▒██▒▒ ▒▒░▓
░░ ▒░ ░░░░ ▓▓▓▒▓ █░█ ░ ░░░░
░█ ██░░▒░ ▒▓ ░ ▒░█░ ██▓ ░
░ █░█ ░▓ ▒░░░
▒▒ █▒░░░█░ █░█░█░
▒░▒ ▒█▒ ▒ ░▓▒▒▒░
░░ ▒█░▒ ░▒ ▓ ░█▓
▒▒ ░ ▓░▒▒█▒▒▒██▒▓▓░ ▓
█░▒ ░▒░▒░ █████ █
██▒ █▒█▒▒▒▒▒▓█
░ ░░░░ ░

View File

@@ -1,27 +0,0 @@
▓█▓░▒▓█▓▒███▒
█▓▒ █▓▓░▓█░▒▓▒▓ ██
▒▓░▓ ████▒███░░█░ ░▒██
▓░ ▓▓▓▓▓▓█░ █░█░█▒░▒
▓▒█░▓█▓██ ▒▓░▒░▒
▓░▓▓█▓▓▓▒ ▒ ██ ░░
▒░▒▒ ░ █▓ ░█▓ ▒ ▒░██
░░░▓▓██░ ░███ ▒ █░░
░▒▒ ▒▓▒█░▒░ ▒░▓
░░░░░▓▒░ ▓█▓▒█ █▓░░░
░░░░░░░▓ ▓ ▒▓░▒░ █▓░░▓
░▒ ░░░░░ ▓█░▓▒░█ ▓░░▒
░░ ░▓░░░ ░░▒█▓░█ ███▒▒ ▓░░
▓ █▓░ ▓▓▓▓█▓▓ ▒█ ░▓ ░░░░░
░ ▓ ░░▒ ▒░░█ ▒███▓ ░██
█▓▓ ░ ░ ▒█░ ░░▓▓
░█ ▒░▒▒█▒█ ▒█▓▓░▒░
█▓▒███░█▒█ ▓█▓▓▒▒▓
░▓▒▒█▒░██▒ ▒▓░█░█░▓
░▓ ▒▒▒▒ ███▒▒▓█▒░░░█▒█
░▒░ ▒ ▒░ █▒████ ▒▓░
██ ░████▓▒▒▒▒██░
██░░ █ ░

View File

@@ -1,27 +0,0 @@
▒██▓█▒████▓░█▒
▒█░ █ ░█ █ ▒▓▓░▓ █▒█
▒▓ ░▓████ ▒███░░ ███▓▒▒█
▓██▓▓█▒ ███ ▒▓▒██░█▒
░░▓ █ ░▓ ░▓ █▒▒
░░▒▒▒██ █▒▓ ██░░
▓█ █░▓░█ ▓░█▒ ▒▓▒░
▒░▓█░░▓░ ░▓██▒ ░▓▒
░░░░ ░▒█ ░███▒░▒ ░░░
░░░ █░░ ░█▓█▒ ▓░░
▒░░░░▒░ ▓▓▒░ ░░▓
▒░░▒░░░ ▓▓▓▓█▓ ░█
░░░▓░░░ ▓▒▓▓▓▓▓▒ ▒▒████░▓ ▓ ░█
░░░░█▒▓ ▓▓▓▓▓░█ █▒█ █░ ░ █░░▒
█░░░▒░░█▒ ██░ ▒ █░█▓█▓░█ ░▒█░░
█▒░░░▓▓ ░ █▒█░ ░ █ ░░
██▒▒██▒ ░ ██ ░▒▒▓
█░▒▒▒█▒▒ ░ ░▒▓█░▓
█░▒▒▒▒░▒▒██ ▒▓██▒▓▓█▓
▒▒ █░░█░██▒▒▒▒████░█████
▒▒▒▒░▒█▓ ░████████░▒█
██▒░███░█▒▒▒▒▒██
░█ ░░ █ ░

View File

@@ -1,27 +0,0 @@
▒▒█░██████░▒▒
▒█ ▒█░░█ ░ ▒▓█▓ ░ ███▒
▒▓▒▓▓███ ░▒▓███░░ ██▓▒██▒▒
▓ ██░░ ▒████▓█▒
░ ▓░▓▓ █ ▓██▒
░▓▓▓░▓█ ██▒ █▒▒
█▓▓▓░█▓ ▓▓ ▒▒ ██▒░
▓▓█▓░▒█ ░░▓ ░ ░ ░▓█
█▒ ██░ ░█░▓░ ▒ ░░░
▒░░░▒░▒ ▒▒▒▒░▒█ ░░▓
░ ░ ▓░░ █ ▒█░░░▓ ▓░░░░
░░░ ░░░ ░▓▓░▓ ▓▒█ ░▓░░░░
█░░░░░░ ░▓██ ▓█ ▒████▓▓▒ ▒ ░█▓░
█░░░░██ ░███░▓█▓ ░▒█ ░█ ░ ▒░▓
░ ░░░░░ █▓▓ █░ ▒░███▓█ ░ ▓▓░█
▒░▒ ███ ░▒█ ▓██░░░░
█▒▒░ ▓▓▒ █▓██░█▒▓
▒░▒█░▒░▒█▓ ▒▓ ▓██░▓
█▒▒▒█░▒▓ ██▓ ▒█ ░██▓▓
▒▒ ▒█░▒▒ ██▓▒▒▒▒███▓▒█░▓ ░░
▒▒ ▒██░▓█░█████████ ▒█
██▒▒▓▒░█▒▒▒▒▒▒▓██░
░ ░░░ █ ░

View File

@@ -0,0 +1,17 @@
▒▓▒▓▒██▒▒██▒
▒▒█▓█▒█▓█▒▒░░▒▒ ▒ █▒
█░█░███ ▒░ ░ █░ ░▒░░░█
▓█▒▒████▒ ▓█░▓░█
▒▒▓▓█▒░▒░▒▒ ▓░▒▒█
░█ █░ ░█▓▓░░█ █▓▒░░█
█▒ ▓█ █▒░█▓ ░▒ ░▓░
░░▒░░ █▓▓░▓░█ ░░
░▒░█░ ▓░░▒▒░ ▓░██████▒██ ▒ ░
▒░▓█ ▒▓█░ ▓█ ░ ░▒▒▒▓▓███░▓█▓█░
▒▒▒ ▒ ▒▒█▓▓░ ░▒████ ▒█ ▓█▓▒▓
█▒█ █ ░ ██▓█▒░
▒▒█░▒█▒ ▒▒▒█░▒█
▒██▒▒ ██▓▓▒▓▓▓▒██▒█░█
░█ █░░░▒▒▒█▒▓██

View File

@@ -0,0 +1,17 @@
▒████▒██▒
██░███▒░▓▒██
▒▒█░░▓░░▓░█▒██
░▒▒▓▒░▓▒▓▒███▒▒█
▓ ▓░░ ░▒ ██▓▒▓░▓
░░ █░█░▓▓▒ ░▒ ░
▒ ░█ █░░░░█ ░▓█
░░▒█▓█░░▓▒░▓▒░░
░▒ ▒▒░▓░░█▒█▓░░
░ █░▒█░▒▓▒█▒▒▒░█░
█ ░░░░░ ▒█ ▒░░
▒░██▒██ ▒░ █▓▓
░█ ░░░░██▓█▓░▓░
▓░██▓░█▓▒ ▓▓█
██ ▒█▒▒█▓█

View File

@@ -1,27 +0,0 @@
▓▒▒█▓███████▒▒
██░█▒▓██░▓█ ░█▓░░ ███▒
▒█▒▒▓██▓▓▓▒████▒░░███▓▒░ ██
▓ ▓▓▓▓▓ ░██▒▓▓██
▒░█▒████▒ ▒ ██▓█
▒░░░▓▓█ █▓▒ ▓▒█░
░▓▒▓▓▒▓ ▓░ ░▒ ▓▒▒█░
▓▓▓░░▓░ █░▒▒░▒░ ░▒█░▒
░ ▓██░ █▒█░█▓ ▒ ░░░
░░░░█░░ █░▒ █▒▓█ ░█░█
░ ░░▒█ █░ ▒▓▒ ▒█ ░░▒░
░░ ▒▒▒░ ▓▒▒█▓ ▒░ ░░ ░
░ ░ ░░▒ ░▒▒█▓░▒▓ ▒▒▒▒▒██▓█▓▒▒ ▒░▓░░
█▒ ░░░ ░░███░█▓ ░░▓█ █░▒░░░░ ░░█░
░▓█░▒▓▒▒ ░█▓░ ▒█ ░█░█▒▓██▒ ▓░▒▒▓
█▒░░░█▒ ░ ░▒██ ██ ▒▓▓█░
▒░░░░▒░ ░▒ ▓ ██░░▓
▒▓█▒░░▒▒██ ▓░▒█▒▒ ▓▓▓
█ █ ░░▓ ░▒ ▒▓█ ▒██░█
█▒ ██░█▒ ░░▓█▒▒█████░▒███░▓▓
██▒██░▓▒ █░████████ ███░
░█▓▒█ ██▒▓▒▒▒▒▒████
░ ░ ██ ░

View File

@@ -1,27 +0,0 @@
▒▓▓▓█▒███████▒▒
▒█ █░▓██░ ████
██▒▓███ ▒█▒███▒░░███▓█░░██▒
██▓▓▓▒▓ ▒██ ▓▒█░█▒ ▒▒
▓ ▓███ █▓ █░▓▓▒▒█▒
█ ▓██▓ ▓ ▓▓▒▒ ▒ ░█▓▒
░█▓░░▓█░ ▓▓ ██▒ █▒ ░▓▓▒
░▒▒▒▓█▓▓░ ▒▒█░▒▒▒░ ░ ▒▓▒█
░▓░▒░▒▒ █░▒ █░ ░ ▒ ░░▒█
░█░░░░█░ █ ░░█▓░▒░ ░▓ █
░█ ░░ ░ ░▒▓ ░█ ▓░ ░░ ░
░░░▓░░ ░ ▒██▓▓░ ▓ ░▓░░█░
▒█ ░░░█░ ██▓▓░█▓░░ ▒██▒███▓▓█ ▒░░░█░
▒░ █▓ █ ▓░█░▓ ▒░ █▓▒█ █░▒ ░ ▓█░▒░░
▒ ░░░░█░ ██▓░░▒ ░██░░▓▓███ ░░░░░
█░▒▒▒▒░ ░ █▓ ▒▒░▒░
▒▒▒▓▒░█▒░▒ ▒▓░▓█▓██
█▓ ░░▒█ ░█ ▒▓█▒█▓░░█
▒ ██░▓▒ ░█▒ ▒█ ▒▓▒██▓░
█▓ ███▓▒ ░█████▒█████░▒▓██░███
█▒▒█▒█▒▒ █░█████ ██░█░▓
░ █░███▒▒▒▒▒▒█▒██
░ ░ ██ ░

View File

@@ -1,27 +0,0 @@
▒▓▒▓▓█████████▒▒
█▓░████ ██▒
▒▓██░█▓▓▓▓▒███░░░░░████▒▒▓███
▒▓█▓▓ ▒░█▓█ ▒█ ██░░██
▓░▓██░░█░ █▓▒██▓░▒
░░▓░███░░ ██░▒ █ ░▒▒▒█
░▒▓░██▒░ ▒▓▓▓░█ ░▒▒▒░▒██
▓█░▒▒▓ ░ █░▓▒█▒▒ ▒▒ ▒▒░▒
░░░░░▓░ █░▓▒░░█ ██▒█▒░
░░▒░░▓▓ ▓█░▒█▒▒█ ░░▓ ░░░
██░░░▓░ ░ ░░█▓░░ ▓ ░▓▒█
░░░░▒░ ▓▒█████░ ▓▓░▒ ░
▒ ▓░░█ ░ ▒█ ▓▓▓▓▓▓ ██▒▒██████▓███ ░▓░░▒░
█▒ ░░░▒░ █▒█▓▓ ▓ █░░▒█ ██▒ ▒██ █░░░░
░█▒▒░▓ ░░ █░▓█▓ ▒▒██░░▒▒██ ░░ ▓
░█░█▓██▒▓ █▒▓▓░▓░█
█▒█░▓░▒█▒▒ ░▓█▒▓▓░█░
██ ░░░█ ░█ ▒█▒▓▓▓▒▓░
▒▒▒█░▒█ ░█▒ ▓█▒░█░█▒█
█▒█░██▒ ▒█████▒▒█▒███░▒▓█░█░▒▓░
██ █░░▒▒ █░ ████ ██░▒▒██
█ ▓██░░█▓▒▒▒▒▒▒▓███
░ ██ ░

View File

@@ -0,0 +1,17 @@
███████▒
▓ ▓░░░▒▒█
▓ ▒▒░░▓▒█▓▒█
░▒▒░░▒▓█▒▒▓▓
▒ ▓▓▒░█▒█▓▒░░█
░█░░░█▒▓▓░▒▓░░
██ █░░░░░░▒░▒▒
░ ░░▓░░▒▓ ░ ░
▓ █░▓░░█▓█░▒░
██ ▒░▓▒█ ▓░▒░▒
█░▓ ░░░░▒▓░▒▒░
▒▒▓▓░▒█▓██▓░░
▒ █░▒▒▒▒░▓
▒█ █░░█▒▓█░
▒▒ ███▒█░

View File

@@ -1,27 +0,0 @@
▒▒▒▓▓█████████▒▒
▒▓██▓░█ ▓▒█ ██▒
███░█▓█ █▒██▓██▒░░░█████▒ ██▒
█ ▓█ █ ▓▓██░▒ █ ██▓█ ▒▒
██░░██ █░ ██ ████▒
▓▓▓░░█ ▓ ▓███▒ ▒▓ ▒▒ ░
▓▒▓▓██▒░░ ▒▒░▒██░ █▒ ▒░ ░
█▓░▒▒██░ ░▒▒▓██▓▒ █▒ ▒░ ░
░░░░░█▓░ █▒▒█▒▒▒ ▒▓ ░░██
▒░░█░░▓░ ▒█░▒ ▒ █ ░ ░░▒░
░▒ ░ ▒ █▒▓▒ ▓ ░█▒░▓░
░░░▒░ ▒ ▒█▒█▓ ▓█ █▓░░░░
█░ ▒░▒ ░ ▓░█░▒ ▒░░ █▒▒▒█▒▒██▓███▒ ▒ ░░▒░
█ ░░░ ░ ▒▓▓▓█░ ▓█ ▒░░▓██ ███ ▒░█░█░▒░▒█░
▒█ ▒█▓ ░ ░░ ▓▓ █▓▒█░██▒▒████▓░░▓░▓░
▒█░▓░ █▒ ▓▒▒▒█▒▓
▒█░░▒▒▒█▒ ▒▓░▓█▓ ▓
▒▓░░░▒█▒▒▒ ▒████▓░██
██ ░░▓█ ▒██▒▒ ▒██ ░█▓░▒▓░
██▒█░▒█▓█░█████▒▒█▒███░▒██░█ ▒█
█▒░░▒▒ ▒ █░ ▓███ ██ █▒██░
░░▓███░███▒▒▒▒▒▒▓████░
░ █ ██ ░

View File

@@ -0,0 +1,17 @@
█████▓
█▒░▒▓░█▒
░▓▒██
▓█░░░▒▒ ░
░ █░░░░▓▓░
░█▓▓█▒ ▒░
░ ░▓▒░░▒
░ ▓█▒░░
██ ░▓░░█░░
░ ▓░█▓█▒
░▓ ░ ▒██▓
█ █░ ▒█░
▓ ██░██▒░
█▒▓ █░▒░░
▒ █░▒▓▓

View File

@@ -0,0 +1,17 @@
▓████
░▒▒░░
░░▒░
░██░▒
█ ░░
▓▓░░
█ ░░
█ ░
▓█ ▒░▓
░ █▒░
█░▓▓ ░░
░▒▒▒░
░██░▒
█▒▒░▒
█ ▓ ▒

View File

@@ -0,0 +1,17 @@
████▓
█▓▒▒▓▒
░▒░░▓ ░
░░▓░ ▒░█
░░░▒ ░
░█░░ █░
░░░░ ▓ █
░░▒░░ ▒
░░░░
▒▓▓ ▓▓
▒░ █▓█░
░█░░▒▒▒░
▓ ░▒▒▒░
░▒▓█▒▒▓
▒█ █▒▓

View File

@@ -0,0 +1,17 @@
█████░▒
░█▒░░▒▓██
▓▓░█▒▒░ █░
░▓░ ▓▓█▓▒▒░
░░▒ ▒▒░░▓ ▒░
▒░░▓░░▓▓░
░░ ░░░░░░█░
░░▓░░█░░░ █▓░
░░████░░░▒▓▓░
░▒░▓▓░▒░█▓ ▓░
░▓░░░░▒░ ░ ▓
░██▓▒░░▒▓ ▒
█░▒█ ▓▓▓░ ▓░
░▒░░▒▒▓█▒▓
▒▒█▒▒▒▒▓
░░

View File

@@ -0,0 +1,17 @@
▒▒█ ███░▒
▓▒░░█░░▒░▒▒
░▓▓ ▒▓▒▒░░ █▒
▓▓▓ ▓█▒▒░▒░░██░
░░▓▒▓██▒░░█▓░░▒
░░░█░█ ░▒▒ ░ ░▓░
▒▒░ ▓░█░░░░▓█ █ ░
░▓▓ ░░░░▓░░░ ▓ ░░
▒▒░░░█░▓▒░░ ██ ▓
█ ▒▒█▒▒▒█░▓▒░ █▒░
░░░█ ▓█▒░▓ ▓▓░░░
░░█ ░░ ░▓▓█ ▓
▒░█ ░ ▓█▓▒█░
▒░░ ▒█░▓▓█▒░
█▓▓▒▒▓▒▒▓█

View File

@@ -0,0 +1,17 @@
█▒███▓▓░█▒
▒▓██░░░█▒█░█ ▒█
██▒▓▒▒▒░██ ░░░▒ ▒
▓░▓▒▓░ ▒░ █░▓▒░░░▒▒
░▓▒ ░ ░ ▓▒▒▒▓▓ █
░▒██▓░ █▓▓░ ▓█▒▓░▓▓
█ ▓▓░ █▓▓░▒ █ ░░▓▒░
▓ ▒░ ▓▓░░▓░█░░▒▓█
█▓█▓▒▒▒█░▒▒░▒▒▓▒░░░ ░
░ ▒▓▒▒░▓█▒▓░░▒ ▒███▒
▒▒▒▓ ████▒▒░█▓▓▒ ▒█
▒░░▒█ ░▓░░░ ▓
▒▒▒ █▒▒ ███▓▒▒▓
█ ░██▒▒█░▒▓█▓░█
░█▓▓▒██░█▒██

View File

@@ -0,0 +1,17 @@
▒▒▒█▒▒█▓░█▒
▒█ ▒▓███░▒▒█ █▓▓▒
▒▓▓░█ █▒ █ ▓▒ █▓▓▒ █
█░░█▓█▒ █ █▒░▒▓▒░▒▓▒▒▒█
▒▒▓▓ ▓░ ▒ █▒▒▓░▓░▒▒▓▒▒▒
▓▒░ ██░▓▒▒▒▓███░█▓▓▒▓░▓░
░░▒▓▓ █▓█▓░ ▒▓ █░▒░▒█
▒▓░░ ▒▒ ░░▓▒ ░▓░
▒ █▒▒▒▓▒▓█░░█░█▓▒█ ░█░░
▒▒▒░█▒█ ░░▓▒▒▒▒░░░▒▓░░▒ █
░▓░▒░ █████░ ▒▒▒▓░▓█▓░▓░
▒▒ █▒█ ░░█ ▓█▒█
▒▒██▒▒▓ ▒█▒▒▓▒█░
█░▓████▒▒▒▒██▒▓▒██
░░▒▓▒▒█▓█ ▓█

View File

@@ -0,0 +1,17 @@
▒▒▒▒█░█▒▒░▓▒
▒█░░░▒▓▒▒▒▒█▒█░███
██▓▓▓ ░██░ ░█▓█░█▓▒
▓▓░██▒░ ▒▒▒██▒░██
░░▓░▓░ █░▒ ▓ ░▒ ░▒█
░▒▓██ ▒░█░▓ ▓▓ █▓█░
▒▒░░█ ▓█▒▓░██░ ▓▓▓█░
░░░░ ░▓ ▒░ █ ░ ░░░
░█░▒█▒▓▓▒▒▒░░░░██▓█░▓ ▒ ░░
▒▓▓█░▒█▓▒██▒█░█ ▒▒ ▓▒▒▒█▓▓░▒
█▒ ▓█░ ██ ▒▒▒▓░▓▓ ▓▓█
▒▒▒█▒▒ ░▓▓▒▓▓█
█ ▒▒░░██ █▓▒▓▓░▓░
█ ▓░█▓░█▒▒▒▓▓█ ▓█░█
░▓▒▓▓█▒█▓▒█▓▒

View File

@@ -0,0 +1,17 @@
▒▓▒▓▒█▒▒▒██▒
▒██▓█▓█░░░▒░░▒▒█░██▒
█░█░▒██░█░░ ░ █▒█▓░░▓░█
▒░▓▒▓████▒ ▓█▒░▓░█
█▒ ▓█▒░▒▒▒▒▒ ▒█░▒░█
█▓█ ░ ░█▒█▓▒█ ▒▒░█░
█░██░ ▒▓░▓░▒░█ ▓ ░ ░
░ ▒░ █░█░░▓█ ░█▓▓░
█ ▒░ ▓░▒▒▒░ ▓░█████████░▒░░█
▒▒█░ ▓░░█ ▓█ ░▒▒▒▒▒▒▓▓▒▒░█▓ ░
▒▒▒ █ █▒▓▓░█ ░ ███████ ░██░░
█▒▒▓▓█ ░ ██▓▓██
▓▒▒▒░██ █▒▒█ ▒░
░░▒▓▒▒ ██▓▓▒▓▓▓▒█░▒░░█
░████░░▒▒▒▒░▓▓█

View File

@@ -0,0 +1,17 @@
▒▒█▒░░▒█▒█▒▒
█▓▒ ▓█▒█▒▒▒░░▒▒█▒██
██ ▒██ ░█ ░ ▒ ▒██░█▒
▒░ ▒█░█ ▒██░▒▓█▒▒
▒░ █░█ ▒▓ ▒░░▒█▒░░▒
▓░█░█ ███▓░ ▓ █▒░░▒
▓░▓█░ ██ ▓██▒ █▒░▓
░▒▒▓░ ▓▓░ █ ░░ ░
░▓░░▓█▒▓▒▒▒▒▒▒▒██▓▒▒▒▒█ ▓ ░▒
█░▒░▒ ▓░░▒▒▒▒░▒ █▒▒ ░▒▒ █▓ ░░
▒█▒▒█ █ ▒█▒░░█░ ▓▒
█ ▒█▓█ ▒▓█▓░▓
▒▒▒██░▒ █▓█░▓██
▒█▓▓ ░█▒▓▓█▓ ░ ░█▓██
░██░▒ ▒▒▒▒▒░█

View File

@@ -0,0 +1,17 @@
▒▒█▒█▒▒█▒██▒▒
███░░▒▒█░▒░█▓▒░▓██▒
▓█▒▒██▒ ░ ░▒░██▒░██
██░▓ █ ▒█▓██▓██
▓█▓█░ █░▓▒▒ ▒▒▒▒█
▓ ▓░ ███▒▓▓ ▒▒▒█
░█░░ ▒ ▓░█▓█ ▒▓▒
░▒ ▒▓ ░█ ░ ░
░ ░ ██▓▓▓▓▓███ ▒░█ ░█ ▓▓ ░
░ ░▒ ░▒ ▒█░ ▒ ░█░█ ▓ ▓▓
▓ ▓ ░░ █░ ██▒█▓ ▓░ █
██ ▓▓▒ ▒█ ▓
█▒ ▒▓▒ ▒▓▓██ █░
█▒▒ █ ██▓░░▓▓▒█ ▓░
███▓█▒▒▒▒█▒▓██░

View File

@@ -0,0 +1,17 @@
▒██▒█▒▒█▒██▒
▒█▓█░▓▒▓░▓▒░░▓░█▓██▒
█▓█▓░▒██░ ░ █▒███▒▒██
▓█░██ ██░░░▒█▒
▒░░▓█ █▒▓░▒░▓▓▓█░
▒░█▓░ █░▓░▓▒▓░ ▒░▒▒░
░██▒▓ ░█░▒█▓█ ░░▓░
░░▒░░ ░▒░░▒▒ ░▒░ ░
░░█ █ █░▒▒▓▓▓▒██▒▒█░▒ ▒█ ▒░▓
▒░▒ █▒▒▒█ ▓█ ░▓▓░ ▒█▓▒ ░██ ▓▒▒
▒▒▒▒░ ██ ░ ░▓██▒▓▓▓ █░
▒█▒▒▒█ ▒██ ░██
█ █▓ ██▒ ▒▓██ █▒▓
█▓███ █░▓▒█▓▓▓▒█ ███
░ ░▒▓▒▒▒▓▒▒▓▒█░

View File

@@ -0,0 +1,17 @@
▒██▒▒████▒█▒▒
▒▒░█░▒▒█▒▒▒█░▒░█░█▒
█ █░██▓█░ ░▓█░▒▓░░█
▓▓░█▓▓░ ▒▓▓▒░░▓▒
▓▓░░▓█ █▓████▓█▒░▒
█▒░ ▓░ ▒█████▓██░░▒░█
░░░ ░ ▓▓▓▓ ▒░░ ░██
░▓░ ░ ░ ░█▒▒█ ░ █▓░
▒ ▒ ░█░▓▒▒▒▒▒▓▒░▒█░▒ ▒▒ ░ ░░░
░▒▒▒░ ▒ ▓░▒ ▒░▒▒█░ ▒▒░
▓█░ ░ ░ █░▓▓▒░▒▓▒▓░
█░░▒░▓ █▓░▒▒▓░
▒ ░██▓▒▒ ▒▓ ▓█▓█▓
▒▒▒█▓██▒░▒▒▒██ ▓▒██░
░ █▒▒░▒▒█▒▒██░

View File

@@ -0,0 +1,17 @@
▒░▒▓███▒▒█▒
█ ▒▓ ░▒▒░▒▒██▒██
█ █▓▒▓█ ░ ▓░▓█░███ ▒
██▓▓█▓░▒█▒░░▓░ ▒█▒░▒▒█
█ ▓▓▒▓█ ░ ▓▒▒░░░▒░██
░█▒█▒░ ███▓ ▓░▓ ▓ ▒
░ ░░ █▓▒█▓ ▓▒▒░▒▒░▒
░ ▒░░ ░█▒▓▒▒░░▒▓▓░░░
░▓ ░▓▓▓▓██░░░██▒██▒░ ░ ░░
▒ ▓ █░▓██▓▓██░▓▒▒██░ ░█░
▒ █▒░▒█ ░ ▒█▓█▒░▒▓█░
▒ ▒██▒ ░ ▓▓▓
▒▓█▒░░▓ ▒▒ ▒▓▓▒█
▓▓██▒▒ ░░▓▒▒▓░▒▒▓░
█▓▒██▓▒▒▒▒▒██

View File

@@ -0,0 +1,17 @@
▒█▒█▓████▒
█ ███░▒▓▒░█░░█
▓░▓▓██ ▓░█▒▒▒░░░▒
░██░ ▓ ▒░ ▒░██▒▓
█▒▒▒█▓█▒▓▓▒░ ░▓▓▒▓█
▒█░░░▒██▓▒░▓ ▓░█░▓▓░█
░▓░█░ ░▒▒▓▒▒▓░▒▓▒ ░▒░
░░░▓░▓ ░▒▒▒▓░▒▒░▒░░▒
▒█▒░ ░▒▒▒▒▒▒█░░▒▒░██░▒
▓▓ ░▓░█░▒░░▓█▒░▒█▒▓▒░
▒░█▓▒░░ ██▓░▒░▓░░
░▒ ░▓█▓▒▓██▓▒▓█▓▓░▓
▒░▒░▒▒▒█▓▓█▒▓▒░░▓
▒▓▓▒▒▒█▒░██ █░█
░█ █▒██▒█░█

View File

@@ -0,0 +1,17 @@
▒▓███ ██
▓█░▓▓▒░█▓░█
▓█ ░▓▒░▒ ▒█
▓█ █░░░▒░░▒█▓▒
░▒█▒░▓░ █▒▓▓░▒▓
▒ ░▓▓▓ █▒▒ ▒▒▓
░ ██▒░░▓░░▓▓ █
▓▓ ▒░░░▒▒▒░░▓░░
░ ▓▒█▓█░█▒▒▓▒░░
▓▒░▓█░▒▒██▒▒█░
░░ ▓░█ ▒█▓░█▒░░
▒▒░░▓▒ ▓▓ ░░░
█ █░▒ ▒░▓░▓█
░ █▒▒ █▒██▓
▒▓▓▒█░▒▒█

View File

@@ -0,0 +1,17 @@
▓█████
░▓▓▓░▓▒
▓█░ █░▓█░
░░░▒░░▓░░
░ ░░▒▓█▒
░▒▓▒ ░░░░░
▒ ░░▒█░░
░ ░░░░▒ ░░
░▓ ▓ ░█░░░░
█▒ ▓ ▒░▒█░░
░▓ ▒▒███▓█
░░██░░▒▓░
░▒▒█▒█▓░▒
▒▒▒░▒▒▓▓
█▒ ▒▒▓

View File

@@ -0,0 +1,17 @@
▓██▓
░█▒░░
▒ ▓░░
░▓░█░
░ ░░
░ ▓ ░
▒░░ ▒░
░▓ ░
▓▒ ▒░
░░▓▓░░
░ ▒░
░▒█▒░
░▒█░░
█▒▒▓░
░ ▓█░

View File

@@ -0,0 +1,17 @@
██████
█░█▓ █▒
▒█░░ █ ░
░░░░▒▒█▓
▒ ░ ░ ░
░█░░░ ▒▒
░▒▒░░░ ▒
░░▒░░
░░░█░ ░
▒░▒░░ ░
█░░▓░▒ ▒
░▓░░░ ▒░
░░░░░░▒░
░▒░█▓ ░█
░░█ ▓█

View File

@@ -0,0 +1,17 @@
▒▓▒▓██▒▒▒▒█▒
▒██▓▒░░█░ ▒▒░▓▒▒░██▒
█▓▓█▓░█ ░ ░ ░ ███▓▒░█
▓█▓▒░▓██▒ ░▒█ ░░▒
█▓█░▓▒▓░░█▒▒ ▒▒▒░░▒
▓░▒▒▓ ▓█░▒▓▒▒ ░ ▒▒░
▒█ ░ ██▒░▒ ░█ ▓█▓░█
█▓░█░ █▓░ ▓▒░ ░▒░▒░
▓ █░ ▓░██░░█▓░▒██▒▒▒██▒░▒ ▓░
█▒▓▒█ ▓▓█▓▓▓░ ░█░▒▒█ ▒▓█▓▒░░▒░░
█▒░ ░ ░░██ ███ ███▓▓▓█▓
██░ ▒█ ░ ▓▒█▒▓▓
▒▒▓▓█▒█ ██▓▓ █░█
▒▒██▒██▒▒▓▒▓█▓▒█▓░▒█
░███▒▓░▒▒▒▒░▓▓▒

View File

@@ -0,0 +1,17 @@
▒▓ ████
▒▓▓░░▒██▒▒
█▒░█▒▒░██▒
░░▒░▓░▒▒░▒ ▒█
▒█░░░▒░█░█ ░
░█░▒█ █░░░░▓░
▒▓░░░▒▒ ▒▓▒░ ▒░
░ ██▒░█░ ░▓ ░
░▒ ▒░▒░▒▓░█ ░
░░▒░▒▒░░ ██ ░
▒░░▓▒▒█░░░█░░
░█▓▓█▓█▒░░ ░
▒░▒░░▓█░░█░▓
█▒██▒▒▓░█▓█
▒▓▓░▒▒▒▓█
░░░░

View File

@@ -0,0 +1,17 @@
▒▓▓████▒█
▒██▓██▒ █▒███
█░▒▓▓█▒▒░▓ ░▒█▒
█░▓█▒▒█▓▒█▒▒░▒░░▒
▒░░░░█▓█▒▒█ ▒░▓▒▒
▓░▒░░▒░█ ▒▓██▓▓░█ ░
▓░░ ░▒█░▒▓▒▓▓█░█░▓░
▒▒█ ░░ ░▒ ░▒ ░░▒▓░
░▒█▒░█▒░░░▓█░░░▒ ░
░░░▓▓░░▒▒▒▒▒░▒░░ █
▒█▒▓█░█ ▓███░▓░█░▒
░░░▒▒▒█ ▒▒█ ░
▓░█▒▒ █ ▓ ░█░▓░
▓░▒░▓▒░░█░ █░░
█ ▒░▒██▓▓▓█
░░░░

View File

@@ -0,0 +1,17 @@
█████▓▓░█▒
▓█░██░▒░░██░░█
▓▒█▒▒██▒▓▓░█░█▒███
█▓▓░▒█░▓▓ ▓ █▒▒░██ █
▓▓░█░█▒██░▓ █░█░▒▓▒█▒█
▒▓▒▒█▒█░░▓░░█▒ ░█▓ █
█░ ▓█░█▒░░██░█▒░▓▒▓▓░█▒
░░░█▒ ▒░░ ▓█░▓▓▒ ▒░ ░
▒░░▓▒ █▒░ ▒▒░███░░░▒░ ▒░
█ ▒░░█▒█▒▒▒▒▒▒░░█░▓░▓▒
█▒█░░▓ ░█ ███▒▓▓▓▓▓▓
▒█░▒▒▒ █▒░▓█░
███░░░█▒ ▒▓▒░▓ █
▒▓▒ ░█░▓▒█░▒█ ▒▓
░▓▒▒▒██▓█▒
░░░

View File

@@ -0,0 +1,17 @@
▒██▒█▒█▒░▓▒
▒██░░▒█▒░▓░▓░█░█▓
▒▓▒░████▒ ░ █▓░░█ █
█▒▓░▓▒░█▒ █░░▒▒█
▒▓░▓░░░▓▒▒▒ ░█▒▒▒
▓▓█ ▒▒▒▒░▒█ ▓▒▓▒▒
░░█ ▒██░▒░▒ ░█░░
█░██ ███▒▓▒█ ▒ ░█
░░░ ░ █░ ▓████▓▒▒█░░█▓▒░▒░
▒▓░█ ▓▓█▓░░░▒▒▒▒▒░░█▒▒▒░░▓
▒▒▒█ ░▓░▓ ▓ ███ ░░█▓▒░
▒█▒██ █ ▓▓▓▓▒▓
█▒ ███▓█ ▒█░█▓█▒█
▒░ █▒█░█▓█▒ ▓█▒█░█
▒▒██▒▒▒▒██▓▓
░░░░

View File

@@ -0,0 +1,17 @@
▒█▒████▒░█▒
▒███▓▒▓░ ░██▒██▓█▒▒
▒▓▓█░█ ▓░█░ ░▒▒▒█ ███
█▓▒░█▒▓█▒ █░██▒▒
▓▓░▒▓▓░ ░ █ ▒▒█▒▒
█▓▒░░▓ ▒▒ ░▒█▒ ▒█▒░▒
░█▒░▒ █▒▒█░▒▒ ░▓░▒
▒░▒ ▓ ░█▒░▓ ░ ▓ ▒▒
██▓▓ ▓▒▓▓ ▒▒▒██████░▒▒ ░▒░
░░▒█▓██▒ ▓▓█░░░▒░▓▒▒▒█▓▒░░░░▒
▓▒▒█ ░▒░█▒ ██░░░░▒ █▓█▒░█
▓█▒▓▒▒▒ ▓▓▓░▓█
▒█░░█▒▓█ ▒█▒ ▒▓█░
▓▒▓░ ░██▓██▒█▒█░██▓█
░▒▓▒▒▒▒▒▒▓▒█▒▒
░░░

View File

@@ -0,0 +1,17 @@
▒██▓▒███▒██▒
██▒█▓░███ ░█░▓ ░█▒▒
▒▓▓░▓██░▒█ ░ ░ █▒█▓ ░██
█▓▓█▓█▓█▒ ██▒▒░▒
▓▓░░▓▓▒ ▒██ ░▒█░█
▓▓▓▓█░ █░▒ ▓▓█▒ ░▒▒░
▒ ▓▓ ▒▒ ██▒▓ ░▒▒▒
░░░▓ ▓▒▒▓▓█ ▓ ▓
▓ █▒ █░░▓▓ ▓░▒▒▒▓▒▒█░░ ░░▒█
░█▒▓█ ▓▓▓ ██▓░▓ ▒█▒▒▒▒▓ ░▓█ ░█
▓░▒██▓▒▒░▓▒░ ░ ▒▒▒▒█▒▒█▓▓▒█░
▓▒▒▓░ ▒▓█ █▒
▒▓░▒▓█▓█ █▓▓▒███
▒▒ ░█░▓▓░░█░▓▓█ ▒▓▓
▒░▓▒▒▒▓▒▒███ ▒

View File

@@ -0,0 +1,17 @@
▒▒█▒████▒██▒
▒▒ ▒█▓▓▓█▒█▓██ ███▒
█▒█▒███▓█ ░░ ░ █░██░██░█
▒░ ██▒▒▒▒ ██░▒ ░
█▓▒▓▒█░▒░▒█▓ ▒▒▓█
▓ █▓░ █▒ ░▓█ ▒▒█
░ ▓ ░ ▒ ▒▒ ░▒░█
░░▒░ ▒▒ ▒▓▓ ▒░ ░
░█ ░ ▓▓ ██ ████▒█████▒ ░▒░░
▒█░▒ █░▒▒▓░▓ ░░▒▒▒▒▒▒▒░░ ▒▓█░
█ █░▒ █▒█▓▒ ██▒▒▒▒▒ ░█ ▓
██ ▒▓▓ █▓░ ▓
▒▓░░█░█ ███ ▓█░
██▒ ██▒▒▓░▒█░▓ ▓ █▓██
░██▓░▒██▒██████

View File

@@ -0,0 +1,17 @@
▒▓▒▓█▒▒█▒██▒
▒▓ ██░▓ ░▒▒▓█▓░ ▓██▒
██▒░░░██ ░ ░▒░▒▒█░▓▒▒▒
▓▓░█░ ▓██ ░██▒█▒
▓░▓▒░▒░▒▓▒░█ ▒ ▒░█▒
▓░░▒░ █▒░░▓█▒ █ ▒▒░█
▒░▓░ ███▒█ ░█ █ ▓░
░▓▒ █░▓█▒░░ ░░░
▒░ ░▒ ▓░▓ ▒▓▓█░███▒▒▒▒██ ░░█
░▒▓ ░ █▓▓▓█▒░░▒▒░█▓▒█▓▓▒▓░▓▓ ░
░░▓█▒█▒▒█▒▓ ████████▒▓░░░░
█░▒ ░▒░ █▒▓▓███
▒▒█▓▒ █▒ ▒▓▒██▓░▓
░░░▒▒██▒▓▓▒▓██▒██▒░█░
█▒▒░▓░▒▒▒▒▒▓▓█░

View File

@@ -0,0 +1,17 @@
▒▓▒▓▓█▒▒▒██▒
▒█ █▓█▓░░█░▒█▓▒░ ██
█▒▓▒█░█ ░ ▒▒░█▒ ███
█░▓░▓░▓▒█ ▓▒░░░░▒
█▒▓█▓▒▒█░▒▒█ ░ ▒░▒░▒
░░░░▓ ▒▒░▒▓▓░▒ █▓░░
░▓░ █ ░▒▒░▒ ░█ ██░█░█
░▓░▒ █▒▒░▓▒░ █░▒░
░█░▒█ ▓▒░ █░█▒▒░█░▒▒▒██▒ ░▓░
▒▒░▒██▓██ ░ ▓▓▒▒▒█▒▓█▓░▓█░░
▒█░░█░█▒▒▓█░ ██ █░▓░▒▓
▒▒█▓▒▒ ░ ▓▒▓██▒
▒▓█▒░▒█▒ ▒▒████▓█
▒░█░███▒▓░▒▒██▒█▒░▓█
▒▓█▒█ ▒▒▒▓▒███░

View File

@@ -0,0 +1,17 @@
▒▓▒▓▓█▒▒██▒▒
█▒▓▓█░▒██░██▓▒███▒
███░░░█ ░ ░▓▒███▓▒▒
▓█░█░█▒▒█ ▒█░░░░█
█▒░░░█▒▒██▒ ▓▒▒░▒█
▓▓▓░▓░▒█▓░▒▒░█ ▓▒▒▓░
▒ █░░ ▒▒░▓▒▒ ▒█░▒░
░ ░░░ ▒░▒░▓░░ ░█▒░░
▒▓░▓░ ▓█░░█▓▓█▒░█░▒▒██▒▓▒▓░
░░▒█▓▒▒▒▓█ ░▓▒██░░█▓▒▒▒░█░▒
▓ ░ ▓░░░▓▓ █ ██ ░▒▒▓░
█ ▓ ▓█░ █▓▒▓▓░░
▓░▒▒███ ▒█▒▒▓███
░ ░██ █ ▓░▒▒████ ▓▓█
▒▓▓███▒▒▒░▒███

View File

@@ -0,0 +1,17 @@
▒▓░▓██▒▒██▒
██░█▒░███▒▒▒▓ ░██
█ █░░░█░ ░▒░░ █▓▒██
▒▒░░░░▓█ ▒░▒█░▓█
░█░█░░▒░▓▒█ ▓ █░░▒
░ ▓░░ ░█▒▓░▒ █▓░░░
░▒ ░ ▒▒░▒░▒░ ██▒░░
▒ ▓░░ ▒█▓░█░░ █ ░░░
▓ ░█ █ ▒▓░▒▓░░▓▓▒░░▒▓█▒░░
░██░░▒▓░░▓█░▓▒░░▒▒█▒█▓▒░▒░
▒ ▒▒▓█░█▒▓ ██████ ▒▓░░
█▒ ▓▒▓▒░ █ ▓▓▓▓█
█▓██▒▒▒▒ █▒░██▓██
▒▒█▒░█▒▓░▒▒▒██░██▓
░█ ░▓░▒▒█▒▓██

View File

@@ -0,0 +1,17 @@
▒▒█▒▓██▒██▒
█ █▓░░░█▒▒ ░ █
▒░▒█░▓▓█ █ ░▓░█▒█▒█
▒█▒█▓░██░ █ ▒▒░░▒
█ ▓░▓█▒░▓▒ ▓█▒░░█
░██░▒▒▒▒▒░▒█ ▒█░░░
░█░░░ █▒▓▒░░░ ░▒░▓░█
▒█░░▓ ░█▒▓░██▓ ▓░▓░░
▒ ▒░░▒▒ ▓█▒░░▓█████▒░░░
▒█▓▒▒░ █░█░░▓░▒▒▒░░▒█
▓▓▒▒░▒░░░▓█▒█▒█ ▒█ ▓▒░
██ ░▒░░░ ▓█▓▓▓█
█▒▒█▒▒▒▒ ▒▓▒▒░█▓█
▓▓█░██ ▓▓██▓▓▒█░░
░░▒██▒░▒██▓▒░
░░

View File

@@ -0,0 +1,17 @@
▓▒▒█▓██▒█
▓█▒▓░░█ ▒ ▒▓▒▒
▓ █░░▓█▒▒▒▓ ▒▒░█
░░▓▓▒▒ ▒▒█░▒▒░██
▓█ ▓▒█ ░██ █▓██▓█░░
░ ░░░ ▒░▒▓▒▒ ░█░█░░░
░ ░█▒░██░▒▒█ ▓█▓ ░░░
░ ░▓▒█▒░░░▒▓▒▒▒░ ░░
█░ ▓░ ░░░░█░░█░░░
░▒░░░▒█░▒░▒░░░░▒▒░░░
░▒▓▒▒░▓ ████░░ ▓▒░
▒░░░▒█░ █▓ ▒▓░░
▒█▒░▒▒ ▓▓▒▓░▓█
▒▓ ▒▒░█▓█▒▓▓█░░
█▓▒ █▒▒░▓█▓

View File

@@ -1,27 +0,0 @@
eeeedcccccccccoee
oecxocccceeeedooeecceccce
eoxeccceoexccxxcdcxcccccoe cce
ocxoocecoocc oxxcccde co
ecxooceoo cccxoeco
oxxoocox oeoxxeee
ooxoc xc xceeec edexoex
ooxxccxe ceecxoxx eceexoex
ccxxexc xcdeeeeox cocxeeo
xecxeexx cexceoxxeo oxexx x
oxdx ex ceod cxd cxxxdx
c ee ox ceoxxc dc exxxx
xexx o oxooc oxe eddcdccccccccco xdox x
cxd xo eoxxcc oce oxxdeccccccccc xdexex
eoxeo xe cxoe exe cxcoooooo x xxcxe
eexxeexo ccccd ooeoocoo
eeex ede dcdcoeoo
exxxo cxee execxcexc
coccxo cocc deccxcccodc
cxcxxc eeoccexceeoooccccxcxccceoce
cccccdoxxcdxocccdxxdccc eccc
cccdxcxxdeeeeeeocdcce
eeccccccce

View File

@@ -1,27 +0,0 @@
eeeedcccccccccoee
oeoxoccccceoeooo ccceccce
eoxxcxcedexccxxcdxxcccccde cce
oxcoocecxocc cccede co
ecxoocedoc ccccxoeco
oxxooeox eoxxeee
oxxcc xc edceeec cdcxocx
ooxxcexe ceeeeoeo oecxxex
ccxxoxc exxxeeee ecocxxeo
xccxe x oexcceoxc xexx x
cxoxcex ceoxc oco dxxxcx
c eo cxo eoxxc odce xx x
xexxccce dxooo doee eddcdccdcccccco o ox x
cxo xe oxxcccoc c xecccccccccccc xdexex
ecxeecxo cxoc exc oocdccexxxdddcc oooxexe
eeexoexe cccc e ooeoxcoo
oeexecece ecdcocoo
eexxoocoee eodcxcexc
cocxxe cocc eeccxccxedc
cxcxxc ccoccxxceeooocccxxdxccceoce
eccccdeoxxdcxccccxcdcccceccc
cccxxccddeeeeeeocdcce
eeccccccce

View File

@@ -1,27 +0,0 @@
eeeedcccccccccoee
eeoxcccxceeoeooo eccxccce
eoxxccceodcccxcddxxcccecde ceo
oxxcxceecxcco cccede co
ocxooeoxc ccecxocco
oxxocexc ooeo ceexxeee
oxooooxc cecoecd ecxoee
oxox ox exccecoc eexdex
xxxe xxe cxxdxoee cecxoxx
xxex xxc ce xexeco exx x
xxex xc ccxxd dxo xxxex
oxod ec ecxxe exe e xxxxxe
ccxx xx oooce xo decdcdccdcccccco ecxx x
cxex cox exxoeoexc oexxcccccceccccc xeexex
xexe eee eccoo oc eecxccxxxxddddc ocxxeoc
exxd eee ecccc ocoocoo
oxxe cxxe eodcococ
ocxxe ecce eoxcocexc
e eece exccoedx eeccxccxedc
cxcxco ccxccxxceeoooccccxcxccceece
exxcoeexxcdooxxcxxxdccccexcc
cecxxxcdeeeeeeeocdcce
eeccccccce

View File

@@ -1,27 +0,0 @@
eeedcccccccdooe
eooxccccceeooe dxeccxccce
ecxoxceoedccxxcddccccceedoeccco
oocxceexcxcco cxcceo cce
exoxcddcccoo cxcxe ce
oxxecxoc oooe xcx eee
oxxoexxe deeococd cecxeeco
oxxoexco ecexeeeee coxoeee
xxcexxce eoexxeecee ecx ex
xxc cxc eecxxxoce cxxo oo
exx xx coeee dx xxx xx
xxx xxc oxxc ecoe xxxxxx
ocx xxo deoxocxcoc ocdddccccccccco xxx xx
xe xo xxoceoxc e xxocccccccccccccex excx x
xxx cox cxx ooeec eecdccccoxxxxdddceeoxxooc
xxe coe cccc eoooodx
exe coco oxcocox
exxo cocce eoxooceoc
cxxxe cccccee edcxeoxddoe
ecxxe ccxcxxccedoooccccxxxxceeece
ccceoe cccdxxxxxxcccce eccc
cccccodeeeeeeeeoddcce
eccccccce

View File

@@ -1,27 +0,0 @@
eeeoccccccccooe
oeocxccceeoxeooeccxcccoe
odocceedcccxxcddccxxxoeeeccceo
eocceodcccccx eccxecexocee
occoxxccc ccxxxdecd
exxodxc ooo cxxococo
exxxoxoc xxceexo exd eee
xxxox cxeococee cxx dxe
xooxxo cxxexeecee cxd oxo
xooxx eexxxeceeo cxx xxc
xex xcx cxx ocdxc xx oe
xee xco dcoocxocc cxc oe
xo xxx eoocoxxc occcddcccccdcco cxcexx
cxoxoeo oxcodecox coccccccocccccccx exxoox
ceo xexe coeoedc xeccccccxxoxxxxc oeceex
eeo eece cccce exexoccc
cec ecxc oxoxcoxc
cxdececxce eoxxcooxc
cxdeceoxxceeo edoxxcxeoo
cxxoeccxxxcxccddoooocccxxxcc ecc
ecceee eccccxxxxxxcccc eococ
cxcdxdoeeeeeeeeoddccc
eccccccce

View File

@@ -1,27 +0,0 @@
eeeecdccccccoeee
oeoxccceeedooeecccccccdoe
odcxcoeddcxdccddccocxccecxceox
oceeedococc oexeeccccce
oc ooccocec ccxeceeec
oe excoe oo edcoeoo
o oxex ocecx eee xcee
oe oxocx xodcoeee ee xexo
ecoexeo cxexoecxe xo dxc
x exxx cxdccoeee cx xcxx
xxcxxe eeexc dxxc xxexo
xocxxeo oco docoe xecxx
excxxxe oooedxeo edccccddddddddoe xeexe
coeexoeo ecccocc occcccccccccccxo exoxx
xeexecx ecdooe xccccccxxexxcxo eeexeo
xe xeoe ee ecoooo
xeoecocex ooexceo e
exocccoce eocoxxeoo
cxdecedxccoeo eoocexxcocc
ccxo ccxxxxcccooooocccxxcccedcoc
cccee ccccccccxoodcce ecccee
cccxxooeeeeeeeoocccc
eccccccee

View File

@@ -1,27 +0,0 @@
eoecdcccccdeee
eodxce c ccceccxcccodoe
eoc eocccdccddccedoeocxcccco
oc occeccec exceoecccexe
oc eoccoe dcce eceee
o doeo dceo coccdeox
x oooe ecex ceccxeex
x xode xxceco xxcxeox
oe ox e eecee deocoxcd
xoxxxo xoe cee ccx xecc
xoxxeo ex cec ooxxxe
xexcex cec ecc exxxxx
xocoxc xoeccoo edcccdddcdcddce ec xxxx
xoxcex exccexc xdeeccccccccccxoc xxcxoxo
xocoeee eeode ccccecceeecccxo exocec c
xecoeco oxeco o
cxecoece eceecc o
xe ecccce oocoeoeo
cxecxeedccee ecceccdceoe
cxeecexccxxeccdeeecccccocodoococ
ccxoe cccccxcccxcccxccccoxce
ecccdddeeeeeeoccxcccc
ecccccceee

View File

@@ -1,27 +0,0 @@
eoexdcccccdeee
odcce eccxcexo
edce eocccxxcdddcece eecceco
eoc ecccoo xeo ccdeco
occeoc oe exc deeeee
xoooo e oc cxoeeexoe
xcoxcd e coccc ceccexoe
oxxoc e oececd cooeoeox
xeexoo eeeee cc x xxe
xcdcx oeccoec ex cex o
xdxxexc x e oeco dcecxxdc
x xxxo xxcccc c cxcxxxo
xx cxxe coo ocoeodcccdddccdcd occdxxo
x xx x edxo oe cdddceeddddcexo xoxxo o
xeeodc ece cdddxeeeeeeecc doexd o
xeecec excx o o
cxeexxce oxooec oe
xoedco eoceoeo oe
cxecdxxc e eeccxxc cec
cxdcxcxxcxedcdeccccxxxceooexeoe
cxcoxxxxccccccxdccoeeooeoc
ccxccxxdxxxxeccxxccc
ecccccce e

View File

@@ -1,27 +0,0 @@
eocdcddcccddeo
eoocc ccxeecce
edcxeoccccxcddccco eeccccc
excdxcceccxo xeo eee co
ocexcc od xo xeo ce
ocoxcoe e dxe eeoexo
ocoxcxde xeoe coece xcxe
exeoc o oeeoe c o eee
xoex c eoeeeee eceoxoex
x xx x xeococo eoeoxceo
cexxcc eexocooeo xxxocxx
ocxxe cooccoxc o xxxxc
xxxxcx exeexe eocdxdcddddc xoxcxx
xoox e ceexo xecccddedddeo dcexxxxx
cxxox ccccdexdxexc oxox c o
xoeoecx ee dcxooc o
xeexxoe oceoxo oe
xxcceec excoo oooe
cdeeccocx oxceocoeeo
cxxcoccexcdxdoooccxxceooee cc
cxxxxcxccccccccceoecceeoe
eccxdeeeeeeoccdccocc
ecccccce e

View File

@@ -1,27 +0,0 @@
eocccdcccdeeo
eecccedoo cccccxccxeo
oxcddccccccdccxo coec cee
excxoceecc co oxeece
oooxcc xe eeoo ee
ooooo e e cexeoce
xooo deeec e eeeoee
xexx eecoceco oee oeex
xxee ocooece o eeceeoe
x xc ocxxoodeeo d xxooxx
x ec eeecooedo x xxx xx
xede cxo occ x exxxcx
xooc x ocoxc oeccccccdcd cxxxexxx
xxoxeo c oc xcccccccccee o xcexxx
xdox x ecccccxdeexc xcxcxxxee
cxccx x eeeee eo ooo x
exxxd e oxex o
exxeeecd oc oc oe
cxxoxeec excooeoc o
exxccdcxcecdeoocxxeecdxe oc
ccecccccxcxcccededcceoc
ccodeeeededccoceoce
ccccee ce

View File

@@ -1,27 +0,0 @@
eocccccdcceee
odcceoeeecccxxdcxcce
oxcecccccxdccdeccxce eee
ocoxcedcc ccce ce
xecxe ocoxo ex
xxcx eeoe ooec ex
deox ceeexe coceeeoe
xxxe xee xe oceoxx ee
ecxx eee eec exexxxxx
xxxx oxeoxeeeo ccxdxdoc
oxxx exeoexoo xoxxxexx
eoxx eeooc o e x xxxxxx
xcxc eoeo oeecccccccc cexxeoox
cooo co occcccccccxx x x xxxe
xxoo eccccexdedxc ocxcxxxxx
xooxoc e eeeee x eoxxxxc
xxee ce xexeoxxox
xeccece exeoooc x
xxoxeec ocecooe eo
ceccedoxcccooeoccoooce oc
cce cccxxxxcceodece oc
ccdeooeecccdcedcc
ecccee e

View File

@@ -1,27 +0,0 @@
eocdcceccceee
ooceooocccxcxd cce
exxdccccxccdececod cc
ococo xoee eeo
oecoe eexdc xo
oxc eeo eoexe oo
xoo cxeo oedecee
x x exeoeeo eo xx x
oeex cxcoeexe oexxx x
xxc eeexcxc xeoxd o
cxc eoxoccx cxxxcxxx
oxce oooxo xxxxexxx
xxox ex ox eecccccccocxxxxooxx
cxx x oxo cccccccceoox exoxxx
xoceeoo occddxdcxoeoexxxxx o
xxoec ex eeeeece xoxx cx
xoeeoo oexc oxee
eoxeeee ocoo o oc
eoxeccco ocoodo oo
eecxcceccoeeoceooee dc
ceecccddxcceecexe oc
ecoeeeooocddeeoce
eccce ce

View File

@@ -1,27 +0,0 @@
oedxcooccoo
ocxdoecedxcdcceo
ooxcccxcceeeee ce
oxoco exee oex
xxee xeee cex
eec eoe eoxodoee
xoxxxooc o ee ece
e x coe e xdcee x
oxxe oxxxeo x xeexxx
xxe cxood c oocxoodo
xxx xeexxx xoxxcoxx
xxxe e ccc xexxxxxx
xxoe xo eeccccc xexxxxxx
xxcxoeo ccccccxe xcxxxxxx
oxxcxe ccdxdcoc xcxxx ee
xoccx ecxxx xxxex
ccdx x ecoexxxxeo
xxooeee xoo oxxox
coxxecceo exoxoxc ooe
cocececcceooeo od xe
ceeccdxocedcoce eo
cceeeeccdce eoe
ecceeeece

View File

@@ -1,27 +0,0 @@
eodccodecoe
ocoocecoe cexo
xoocccecoeoec ee
dxoco cxoo ee
xxe o exee ece
xxceeee xdxcc xx
ccxxeccc e cx cxoo
xx xxxex o xx exe
oox ooexe xxxo cx
xxc xxxee xooxxex
xxo cxoxe xoxdoe xoo
xxo xdoecc eoexxxd xx
xo xx eeecccceexxxxxxx
xc ec cccccexxcexxxxxe
xxo x eddexcxxd xx x
oxx oo ecxxx xx cx
xxcx eeoc xx ex
xxxe xxooexxco
xxce oxeexxxx o
excdeceecoexc oox
eecxcoeoexc oo
cceeoceocoexc
ccceccc

View File

@@ -1,27 +0,0 @@
odccooecoe
ooeeeoodccxo
dxoceecxe cxo
cxcoxexco coce
xex eeoeee cod
xeoeco xxexc cx
xxxo x cxx edxxe
eoco xxo ex x
cxcxcc excexxo
eoecedx xxcxxoox
xoxddcx xxoccxox
xococcx xxce xxx
coexeeec xxoxxcxx
dexcccxe xxd xx x
cex d xxxxxxxex
xxceco e oxxxx ox
xxx xx e exxxx x
exc e dxxx odoee
xce x x eece xx
exeecoeoox xxec
ecoccodc co
ceeoec ec
ccecccc

View File

@@ -1,27 +0,0 @@
dccoocco
e exo ccoe
oxxxoeceee
oooxdx cxxx
xxxoxco cxx
xxexooxex ex
xxocxxox xx
xxccxxxxecc o
xxoxxcoexxxex
xxxxx xxx eex
xxecc xccxcxx
xxeex x cxoxx
xxeexeeoxxxxc
xxxxxxxxx xe
xxexcx xxx xo
xxexxxxxxxxcx
xeooox o x
xxoxxcdxxc
oxxxexceexe
xecxec xcxx
d x ee oeec
ccx ccodxx
cccecce

View File

@@ -1,27 +0,0 @@
cccoccco
xexc ccdx
xexc xeoe
xxxeeecxo
xooxocexx
cco eeoex
cocxocccx
exex xoee
exdeo ox
xxxeeexxe
xxxcoccxxx
xxxxoodox
xxxxxxexxe
xxxxxxeexe
xxxxxxxxxx
xxoxxxxxcx
xxxxxxxxex
xxxxxxxxox
xxcxeooxex
exdxxocxee
excx x x e
cocc oco
cccccce

Some files were not shown because too many files have changed in this diff Show More