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
+6 -4
View File
@@ -210,6 +210,7 @@ function createApp() {
app.use("/api/locks", locksRouter);
app.use("/api/pets", petsRouter);
app.use("/api/snapshots", snapshotsRouter);
app.use("/api/artifacts", require("./routes/artifacts"));
app.use("/api/alerts", alertsRouter);
app.use("/api/budgets", budgetsRouter);
app.use("/api/query", queryRouter);
@@ -762,7 +763,7 @@ function startBackgroundServices() {
if (Number.isFinite(HEAL_MS) && HEAL_MS > 0) {
const { healAllRunIds } = require("./routes/lanes");
const { reconcileOrphans } = require("./lib/dashboard-runs");
const { liveSessions } = require("./lib/tmux");
const { liveSessions, serverStartedAt } = require("./lib/tmux");
const anyRun = require("./db").db.prepare(
`SELECT EXISTS (SELECT 1 FROM lanes WHERE run_id IS NOT NULL)
OR EXISTS (SELECT 1 FROM dashboard_runs WHERE status IN ('running', 'spawning')) AS any`
@@ -782,7 +783,8 @@ function startBackgroundServices() {
healAllRunIds(live);
// A pane that quit (Ctrl+C, `exit`) ends its run record too; one that
// died under its agent (killed tmux, crash) is offered for resume.
if (reconcileOrphans((id) => live.has(id)) > 0) broadcast("runs_interrupted", {});
if (reconcileOrphans((id) => live.has(id), serverStartedAt) > 0)
broadcast("runs_interrupted", {});
} catch (err) {
console.warn("run heal failed:", err.message);
}
@@ -841,8 +843,8 @@ function startBackgroundServices() {
// this process, owns it.
try {
const { reconcileOrphans } = require("./lib/dashboard-runs");
const { hasSession } = require("./lib/tmux");
const reconciled = reconcileOrphans(hasSession);
const { hasSession, serverStartedAt } = require("./lib/tmux");
const reconciled = reconcileOrphans(hasSession, serverStartedAt);
if (reconciled > 0) {
console.log(`[runs] reconciled ${reconciled} orphan run(s) → interrupted/abandoned`);
}