feat(workflows): stamp runs with revision and improve history preview

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-20 22:31:18 +07:00
co-authored by Cursor
parent f68376587e
commit 84f29057df
10 changed files with 521 additions and 63 deletions
+46 -1
View File
@@ -1,6 +1,7 @@
import { randomUUID } from "node:crypto";
import { db } from "./db.js";
import { workflowContentSha } from "./workflow-normalize.js";
import * as fsStore from "./fs-store.js";
import { workflowContentSha, workflowIdFromFile } from "./workflow-normalize.js";
const MAX_REVISIONS = 50;
@@ -70,6 +71,50 @@ export async function getRevision(workflowId, revision) {
};
}
/**
* Ensure a workflow has at least revision #1 (seed from disk when history is empty).
* @param {{ owner: string, file: string }} opts
* @returns {Promise<{ revision: number, id: string, content_sha: string, created_at: string, seeded: boolean } | null>}
*/
export async function ensureInitialRevision(opts) {
const workflowId = workflowIdFromFile(opts.file);
const latest = await getLatestRevision(workflowId);
if (latest) {
return {
revision: latest.revision,
id: latest.id,
content_sha: latest.content_sha,
created_at: latest.created_at,
seeded: false,
};
}
const content = fsStore.readWorkflowYaml(opts.owner, opts.file);
if (content == null) return null;
const recorded = await recordRevision({
workflowId,
owner: opts.owner,
file: opts.file,
content,
reason: "seed",
force: true,
});
if (recorded.revision == null || recorded.id == null) return null;
const row = await getLatestRevision(workflowId);
if (!row) return null;
return {
revision: row.revision,
id: row.id,
content_sha: row.content_sha,
created_at: row.created_at,
seeded: true,
};
}
/**
* Insert a revision when content changed (SHA dedup skips identical saves).
* @param {{