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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Netclaw.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ static async Task RunAsync(string[] args)
// On an existing install, `netclaw init` opens an explicit action menu instead
// of silently re-walking setup (simplify-netclaw-init). First run starts the
// bootstrap wizard directly.
var initStartRoute = File.Exists(initPaths.NetclawConfigPath)
var initStartRoute = initPaths.HasCompletedSetup()
? InitExistingInstallViewModel.MenuRoute
: "/init";

Expand Down
75 changes: 75 additions & 0 deletions src/Netclaw.Configuration.Tests/NetclawPathsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,81 @@ public void EnsureDirectoriesExist_reports_actionable_initialization_failures()
File.Delete(basePath);
}
}

[Fact]
public void HasCompletedSetup_false_for_channel_seed_config_only()
{
var basePath = Path.Combine(Path.GetTempPath(), "netclaw-seed-" + Guid.NewGuid().ToString("N"));
try
{
var paths = new NetclawPaths(basePath);
Directory.CreateDirectory(paths.ConfigDirectory);
File.WriteAllText(
paths.NetclawConfigPath,
"""{"configVersion":1,"Daemon":{"UpdateChannel":"beta"}}""");

Assert.False(paths.HasCompletedSetup());
}
finally
{
if (Directory.Exists(basePath))
Directory.Delete(basePath, recursive: true);
}
}

[Fact]
public void HasCompletedSetup_true_when_identity_or_secrets_exist()
{
var basePath = Path.Combine(Path.GetTempPath(), "netclaw-setup-" + Guid.NewGuid().ToString("N"));
try
{
var paths = new NetclawPaths(basePath);
paths.EnsureDirectoriesExist();
File.WriteAllText(
paths.NetclawConfigPath,
"""{"configVersion":1,"Daemon":{"UpdateChannel":"beta"}}""");

Assert.False(paths.HasCompletedSetup());

Directory.CreateDirectory(paths.IdentityDirectory);
File.WriteAllText(paths.SoulPath, "# soul");
Assert.True(paths.HasCompletedSetup());

File.Delete(paths.SoulPath);
File.WriteAllText(paths.SecretsPath, """{"DeviceToken":"test"}""");
Assert.True(paths.HasCompletedSetup());
}
finally
{
if (Directory.Exists(basePath))
Directory.Delete(basePath, recursive: true);
}
}

[Fact]
public void HasCompletedSetup_true_for_legacy_personality_file_only()
{
var basePath = Path.Combine(Path.GetTempPath(), "netclaw-legacy-" + Guid.NewGuid().ToString("N"));
try
{
var paths = new NetclawPaths(basePath);
Directory.CreateDirectory(paths.ConfigDirectory);
Directory.CreateDirectory(paths.SoulDirectory);
File.WriteAllText(
paths.NetclawConfigPath,
"""{"configVersion":1,"Daemon":{"ExposureMode":"local"}}""");

Assert.False(paths.HasCompletedSetup());

File.WriteAllText(paths.PersonalityPath, "# personality");
Assert.True(paths.HasCompletedSetup());
}
finally
{
if (Directory.Exists(basePath))
Directory.Delete(basePath, recursive: true);
}
}
}

/// <summary>
Expand Down
11 changes: 11 additions & 0 deletions src/Netclaw.Configuration/NetclawPaths.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,17 @@ private IEnumerable<string> StandardDirectories()
yield return WorkspacesDirectory;
yield return ModelsDirectory;
}

/// <summary>
/// Whether <c>netclaw init</c> has completed at least once. Wizard-owned identity
/// files, legacy personality files, and <see cref="SecretsPath"/> indicate a prior
/// setup, whereas a channel-seed <c>netclaw.json</c> from <c>install.sh --channel</c>
/// is not.
/// </summary>
public bool HasCompletedSetup() =>
File.Exists(SoulPath)
|| File.Exists(SecretsPath)
|| File.Exists(PersonalityPath);
}

public sealed class NetclawDirectoryInitializationException : IOException
Expand Down
2 changes: 1 addition & 1 deletion tests/smoke/tapes/init-existing.tape
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
Output "/tmp/tape-init-existing.gif"

# ─── Seed an existing install ───────────────────────────────────────
Type "mkdir -p $NETCLAW_HOME/config && echo {} > $NETCLAW_HOME/config/netclaw.json"
Type "mkdir -p $NETCLAW_HOME/config $NETCLAW_HOME/identity && echo {} > $NETCLAW_HOME/config/netclaw.json && echo '# soul' > $NETCLAW_HOME/identity/SOUL.md"
Enter
Wait+Screen@5s /TAPE\$/

Expand Down