feat: v2.7.8 — Artifacts hub, clone related repos as extra folders, quieter resume prompt

- Artifacts (/artifacts): claude.ai pages sessions publish (captured from the
  Artifact tool's hook), pinned workspace files, skills and plugins, each
  kept as a snapshot; shared ones appear in a Hub and a skill or plugin
  installs into the viewer's own account (server/lib/artifacts.js,
  /api/artifacts, docs/plans/artifacts/).
- Workspace extra folders: clone one or more related repos beside the
  workspace and add them in one step (POST /api/lanes/:id/sources/clone).
- A pane that ended by itself while tmux kept running is no longer offered
  for resume (tmux.serverStartedAt in reconcileOrphans).
- Changes panel: a folder that is not a git repo opens on Files, and its
  files open from there.
- Plus the parallel sessions' work in this tree: File Explorer button in the
  panel header, user delete, RSA SSH keys, chat/terminal and pet fixes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 16:04:03 +07:00
parent e94038481e
commit 22de5009ff
79 changed files with 4617 additions and 231 deletions
@@ -64,6 +64,8 @@ export function GitIdentitySettings() {
}
};
// ed25519 suits GitHub/GitLab/Gitea; AWS IAM (CodeCommit) accepts only RSA.
const [keyType, setKeyType] = useState<"ed25519" | "rsa">("ed25519");
const generate = async () => {
if (
id?.ssh &&
@@ -73,7 +75,10 @@ export function GitIdentitySettings() {
}))
)
return;
await run(() => api.gitIdentity.generateKey(Boolean(id?.ssh)), t("gitIdentity.keyMade"));
await run(
() => api.gitIdentity.generateKey(Boolean(id?.ssh), keyType),
t("gitIdentity.keyMade")
);
};
const removeKey = async () => {
@@ -203,6 +208,18 @@ export function GitIdentitySettings() {
<h3 className="flex-1 text-xs font-semibold text-fg-primary">
{t("gitIdentity.sshTitle")}
</h3>
{id.writable && (
<select
aria-label={t("gitIdentity.keyType")}
value={keyType}
disabled={busy}
onChange={(e) => setKeyType(e.target.value === "rsa" ? "rsa" : "ed25519")}
className="select !py-1 !text-xs"
>
<option value="ed25519">{t("gitIdentity.keyEd25519")}</option>
<option value="rsa">{t("gitIdentity.keyRsa")}</option>
</select>
)}
{id.writable && (
<button type="button" className={BTN} disabled={busy} onClick={() => void generate()}>
{id.ssh ? <RefreshCw className="h-3.5 w-3.5" /> : <Plus className="h-3.5 w-3.5" />}
@@ -222,6 +239,7 @@ export function GitIdentitySettings() {
/>
<div className="flex flex-wrap items-center gap-2">
<span className="min-w-0 flex-1 truncate font-mono text-[10px] text-fg-muted">
{id.ssh.type === "rsa" ? "RSA 4096 · " : id.ssh.type ? "ED25519 · " : ""}
{id.ssh.fingerprint}
</span>
<button