Skip to content
Merged
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
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@

Toutes les évolutions notables de WarpgateSH sont documentées ici.

## 0.1.11 — 2026-08-19

### Synchronisation

- L’agent suit désormais les changements d’adresse ou de port SSH annoncés par Warpgate, sans demander de recréer le profil lorsque la clé d’hôte épinglée reste identique.
- Les profils et les alias SSH enregistrent le nouvel endpoint après une synchronisation réussie.

### Interface

- La page des profils permet de régénérer immédiatement les alias SSH.
- La suppression d’un profil utilise maintenant une confirmation intégrée, plus claire que la boîte de dialogue système.

### Correctifs CLI

- `warpgatesh <cible> -- <commande>` conserve correctement les options OpenSSH avant la destination et place la commande distante après celle-ci.
- `warpgatesh agent install` retrouve correctement l’agent inclus dans l’application lorsque la CLI est appelée depuis son lien `/usr/local/bin/warpgatesh`.

### Documentation et diagnostics

- Les guides publics couvrent désormais l’installation, la première connexion, le dépannage, la désinstallation et la contribution.
- Cette publication inclut également les diagnostics locaux et l’export expurgé préparés pour v0.1.10, qui était restée en brouillon et n’avait pas été proposée aux utilisateurs.

## 0.1.10 — 2026-08-13

### Nouveauté
Expand Down
9 changes: 5 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ members = [
resolver = "3"

[workspace.package]
version = "0.1.10"
version = "0.1.11"
edition = "2024"
rust-version = "1.85"
license = "Apache-2.0"
Expand Down
4 changes: 2 additions & 2 deletions apps/warpgatesh-companion/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/warpgatesh-companion/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "warpgatesh-companion",
"private": true,
"version": "0.1.10",
"version": "0.1.11",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
6 changes: 3 additions & 3 deletions apps/warpgatesh-companion/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/warpgatesh-companion/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[package]
name = "warpgatesh-companion"
description = "Optional desktop companion for WarpgateSH"
version = "0.1.10"
version = "0.1.11"
edition = "2024"
rust-version = "1.85"
license = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion apps/warpgatesh-companion/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "WarpgateSH",
"version": "0.1.10",
"version": "0.1.11",
"identifier": "dev.warpgatesh.companion",
"build": {
"beforeDevCommand": "npm run prepare:sidecars && npm run dev",
Expand Down
34 changes: 27 additions & 7 deletions apps/warpgatesh-companion/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ function ProfileCard({
onRemove: (name: string) => Promise<void>;
}) {
const [renewing, setRenewing] = useState(false);
const [confirmingRemoval, setConfirmingRemoval] = useState(false);
const [token, setToken] = useState("");

return (
Expand Down Expand Up @@ -245,10 +246,21 @@ function ProfileCard({
</div>
</form>
) : (
<div className="card-actions">
<button type="button" onClick={() => setRenewing(true)}>Renouveler le jeton</button>
<button className="text-danger" type="button" onClick={() => void onRemove(profile.name)}>Supprimer</button>
</div>
<>
<div className="card-actions">
<button type="button" onClick={() => setRenewing(true)}>Renouveler le jeton</button>
<button className="text-danger" type="button" onClick={() => setConfirmingRemoval(true)}>Supprimer</button>
</div>
{confirmingRemoval ? (
<div className="inline-confirm" role="group" aria-label={`Confirmer la suppression du profil ${profile.name}`}>
<p>Supprimer ce profil et régénérer les alias SSH sans lui ?</p>
<div className="button-row">
<button className="button-secondary" type="button" onClick={() => setConfirmingRemoval(false)}>Annuler</button>
<button className="button-danger" type="button" disabled={busy} onClick={() => void onRemove(profile.name)}>Confirmer</button>
</div>
</div>
) : null}
</>
)}
</article>
);
Expand All @@ -257,12 +269,16 @@ function ProfileCard({
function ProfilesView({
profiles,
busy,
synchronizing,
onChanged,
onSynchronize,
runAction,
}: {
profiles: CompanionProfile[];
busy: boolean;
synchronizing: boolean;
onChanged: () => Promise<void>;
onSynchronize: () => void;
runAction: (action: () => Promise<void>, success: string) => Promise<boolean>;
}) {
const [adding, setAdding] = useState(false);
Expand Down Expand Up @@ -297,7 +313,6 @@ function ProfilesView({
}

async function remove(name: string) {
if (!window.confirm(`Supprimer le profil « ${name} » et ses alias SSH ?`)) return;
await runAction(async () => {
await removeProfile(name);
await onChanged();
Expand All @@ -308,7 +323,12 @@ function ProfilesView({
<section className="page-panel" aria-labelledby="profiles-title">
<div className="page-heading">
<div><p className="section-kicker">Instances</p><h2 id="profiles-title">Profils Warpgate</h2></div>
<button className="button-primary button-compact" type="button" onClick={() => setAdding((value) => !value)}>{adding ? "Fermer" : "+ Ajouter"}</button>
<div className="page-heading__actions">
<button className="button-secondary button-compact" type="button" disabled={synchronizing} onClick={onSynchronize}>
{synchronizing ? "Régénération…" : "Régénérer les alias SSH"}
</button>
<button className="button-primary button-compact" type="button" onClick={() => setAdding((value) => !value)}>{adding ? "Fermer" : "+ Ajouter"}</button>
</div>
</div>

{adding ? (
Expand Down Expand Up @@ -853,7 +873,7 @@ export default function App() {
<main>
{state === null ? <p className="loading-state">Lecture de l’état local…</p> : null}
{state && view === "access" ? <AccessView state={state} busy={busy} onSync={() => void handleSync()} onOpen={(alias) => void handleOpen(alias)} onNavigate={setView} /> : null}
{state && view === "profiles" ? <ProfilesView profiles={state.profiles} busy={busy} onChanged={refresh} runAction={runAction} /> : null}
{state && view === "profiles" ? <ProfilesView profiles={state.profiles} busy={busy} synchronizing={busy || state.agentSynchronizing} onChanged={refresh} onSynchronize={() => void handleSync()} runAction={runAction} /> : null}
{state && view === "preferences" ? <PreferencesView state={state} busy={busy} onSave={handlePreferences} onInstallCli={handleInstallCli} onCheckForUpdates={handleCheckForUpdates} onInstallUpdate={handleInstallUpdate} onUninstall={handleUninstall} /> : null}
</main>

Expand Down
29 changes: 29 additions & 0 deletions apps/warpgatesh-companion/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,13 @@ main {
margin-bottom: 18px;
}

.page-heading__actions {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 8px;
}

.button-primary,
.button-secondary {
min-height: 40px;
Expand Down Expand Up @@ -830,6 +837,28 @@ main {
gap: 10px;
}

.inline-confirm {
display: grid;
gap: 10px;
padding: 12px;
border: 1px solid rgb(211 93 71 / 35%);
border-radius: 8px;
background: var(--danger-soft);
}

.inline-confirm p {
margin: 0;
color: var(--ink);
font-size: 0.76rem;
line-height: 1.45;
}

.button-danger {
border-color: rgb(211 93 71 / 55%);
color: var(--danger);
background: var(--danger-soft);
}

.button-row {
justify-content: flex-end;
gap: 8px;
Expand Down
19 changes: 13 additions & 6 deletions crates/warpgatesh-agent/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ use std::process::ExitCode;
use serde_json::json;
use warpgatesh_core::schedule::SyncSchedule;
use warpgatesh_runtime::RuntimeError;
use warpgatesh_runtime::api::ApiClient;
use warpgatesh_runtime::configuration::{ConfigurationMutation, LocalConfiguration};
use warpgatesh_runtime::diagnostics::DiagnosticLogger;
use warpgatesh_runtime::ipc::MUTATION_PREFIX;
use warpgatesh_runtime::keychain::SystemKeychain;
use warpgatesh_runtime::ssh::verify_host_keys;
use warpgatesh_runtime::keychain::{SystemKeychain, TokenStore};
use warpgatesh_runtime::ssh::{save_host_keys, verify_host_keys};
use warpgatesh_runtime::storage::{
AGENT_STATUS_SCHEMA_VERSION, AgentErrorKind, AgentStatus, LocalStore,
};
Expand Down Expand Up @@ -111,15 +112,21 @@ fn synchronize_and_record(store: &LocalStore) -> Result<SyncReport, RuntimeError
}

fn verify_and_synchronize(store: &LocalStore) -> Result<SyncReport, RuntimeError> {
let tokens = SystemKeychain;
for profile in store.load_profiles()?.profiles {
verify_host_keys(
let token = tokens.get(&profile.name)?;
let metadata = ApiClient::new(&profile.base_url)?.validate(&token)?;
let presented = verify_host_keys(
store.paths(),
&profile.name,
&profile.ssh_host,
profile.ssh_port,
&metadata.ssh_host,
metadata.ssh_port,
)?;
if profile.ssh_host != metadata.ssh_host || profile.ssh_port != metadata.ssh_port {
save_host_keys(store.paths(), &profile.name, &presented.known_hosts)?;
}
}
synchronize_all(store, &SystemKeychain)
synchronize_all(store, &tokens)
}

fn error_kind(error: &RuntimeError) -> AgentErrorKind {
Expand Down
3 changes: 3 additions & 0 deletions crates/warpgatesh-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,8 @@ rpassword.workspace = true
warpgatesh-core = { path = "../warpgatesh-core" }
warpgatesh-runtime = { path = "../warpgatesh-runtime" }

[dev-dependencies]
tempfile = "3.27.0"

[lints]
workspace = true
Loading