@fieldwangai/agentflow 0.1.113 → 0.1.115
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/lib/admin-run-detail.mjs +263 -0
- package/bin/lib/ui-server.mjs +351 -37
- package/builtin/web-ui/dist/assets/index-CuRtjhm3.css +1 -0
- package/builtin/web-ui/dist/assets/{index-B86D6SK3.js → index-sFdIyhv_.js} +45 -44
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/builtin/web-ui/dist/assets/index-Brqy4uhJ.css +0 -1
package/bin/lib/ui-server.mjs
CHANGED
|
@@ -126,6 +126,7 @@ import {
|
|
|
126
126
|
readRunLedgerEvents,
|
|
127
127
|
runLedgerId,
|
|
128
128
|
} from "./run-ledger.mjs";
|
|
129
|
+
import { readAdminRunDetail } from "./admin-run-detail.mjs";
|
|
129
130
|
import {
|
|
130
131
|
appendWorkspaceRunLogEvent,
|
|
131
132
|
createWorkspaceRunLogSession,
|
|
@@ -7723,6 +7724,78 @@ function prdWorkflowReviewPaths(scopedRoot, tapdId, reviewId) {
|
|
|
7723
7724
|
};
|
|
7724
7725
|
}
|
|
7725
7726
|
|
|
7727
|
+
function prdWorkflowReviewIndexPath(tapdId, reviewId) {
|
|
7728
|
+
return path.join(
|
|
7729
|
+
getAgentflowDataRoot(),
|
|
7730
|
+
"prd-workflow-review-index",
|
|
7731
|
+
prdWorkflowSafeStateId(tapdId),
|
|
7732
|
+
`${prdWorkflowSafeStateId(reviewId)}.json`,
|
|
7733
|
+
);
|
|
7734
|
+
}
|
|
7735
|
+
|
|
7736
|
+
function prdWorkflowWriteReviewIndex(ownerId, tapdId, reviewId) {
|
|
7737
|
+
const indexPath = prdWorkflowReviewIndexPath(tapdId, reviewId);
|
|
7738
|
+
const tempPath = `${indexPath}.${process.pid}.${Date.now()}.tmp`;
|
|
7739
|
+
fs.mkdirSync(path.dirname(indexPath), { recursive: true });
|
|
7740
|
+
fs.writeFileSync(tempPath, JSON.stringify({
|
|
7741
|
+
version: 1,
|
|
7742
|
+
tapdId: String(tapdId || ""),
|
|
7743
|
+
reviewId: prdWorkflowSafeStateId(reviewId),
|
|
7744
|
+
ownerId: String(ownerId || "").trim(),
|
|
7745
|
+
updatedAt: new Date().toISOString(),
|
|
7746
|
+
}, null, 2) + "\n", "utf-8");
|
|
7747
|
+
fs.renameSync(tempPath, indexPath);
|
|
7748
|
+
}
|
|
7749
|
+
|
|
7750
|
+
function prdWorkflowReadReviewIndex(tapdId, reviewId) {
|
|
7751
|
+
try {
|
|
7752
|
+
const parsed = JSON.parse(fs.readFileSync(prdWorkflowReviewIndexPath(tapdId, reviewId), "utf-8"));
|
|
7753
|
+
if (
|
|
7754
|
+
String(parsed?.tapdId || "") !== String(tapdId || "")
|
|
7755
|
+
|| prdWorkflowSafeStateId(parsed?.reviewId) !== prdWorkflowSafeStateId(reviewId)
|
|
7756
|
+
) {
|
|
7757
|
+
return null;
|
|
7758
|
+
}
|
|
7759
|
+
return parsed;
|
|
7760
|
+
} catch {
|
|
7761
|
+
return null;
|
|
7762
|
+
}
|
|
7763
|
+
}
|
|
7764
|
+
|
|
7765
|
+
function prdWorkflowReviewFileExists(paths) {
|
|
7766
|
+
try {
|
|
7767
|
+
return fs.existsSync(paths.markdownPath) && fs.statSync(paths.markdownPath).isFile();
|
|
7768
|
+
} catch {
|
|
7769
|
+
return false;
|
|
7770
|
+
}
|
|
7771
|
+
}
|
|
7772
|
+
|
|
7773
|
+
function prdWorkflowResolveReviewPaths(scopedRoot, tapdId, reviewId) {
|
|
7774
|
+
const direct = prdWorkflowReviewPaths(scopedRoot, tapdId, reviewId);
|
|
7775
|
+
if (prdWorkflowReviewFileExists(direct)) return direct;
|
|
7776
|
+
|
|
7777
|
+
const indexed = prdWorkflowReadReviewIndex(tapdId, reviewId);
|
|
7778
|
+
if (indexed) {
|
|
7779
|
+
const indexedPaths = prdWorkflowReviewPaths(
|
|
7780
|
+
getAgentflowUserDataRoot(indexed.ownerId || ""),
|
|
7781
|
+
tapdId,
|
|
7782
|
+
reviewId,
|
|
7783
|
+
);
|
|
7784
|
+
if (prdWorkflowReviewFileExists(indexedPaths)) return indexedPaths;
|
|
7785
|
+
}
|
|
7786
|
+
|
|
7787
|
+
const candidateOwners = ["", ...listAgentflowUserIds()];
|
|
7788
|
+
for (const ownerId of candidateOwners) {
|
|
7789
|
+
const candidate = prdWorkflowReviewPaths(getAgentflowUserDataRoot(ownerId), tapdId, reviewId);
|
|
7790
|
+
if (!prdWorkflowReviewFileExists(candidate)) continue;
|
|
7791
|
+
try {
|
|
7792
|
+
prdWorkflowWriteReviewIndex(ownerId, tapdId, reviewId);
|
|
7793
|
+
} catch (_) {}
|
|
7794
|
+
return candidate;
|
|
7795
|
+
}
|
|
7796
|
+
return direct;
|
|
7797
|
+
}
|
|
7798
|
+
|
|
7726
7799
|
function prdWorkflowMigrateLegacyState(legacyRoot, stateRoot, tapdId) {
|
|
7727
7800
|
const sourceRoot = path.resolve(legacyRoot || "");
|
|
7728
7801
|
const destinationRoot = path.resolve(stateRoot || "");
|
|
@@ -7752,6 +7825,75 @@ function prdWorkflowMigrateLegacyState(legacyRoot, stateRoot, tapdId) {
|
|
|
7752
7825
|
} catch (_) {}
|
|
7753
7826
|
}
|
|
7754
7827
|
|
|
7828
|
+
function prdWorkflowReviewShortLinkDir(root) {
|
|
7829
|
+
return path.join(path.resolve(root || process.cwd()), ".workspace", "prd-flow", "review-short-links");
|
|
7830
|
+
}
|
|
7831
|
+
|
|
7832
|
+
function prdWorkflowReviewShortLinkPath(root, shortCode) {
|
|
7833
|
+
return path.join(
|
|
7834
|
+
prdWorkflowReviewShortLinkDir(root),
|
|
7835
|
+
`${String(shortCode || "").trim()}.json`,
|
|
7836
|
+
);
|
|
7837
|
+
}
|
|
7838
|
+
|
|
7839
|
+
function prdWorkflowReadReviewShortLink(root, shortCode) {
|
|
7840
|
+
const code = String(shortCode || "").trim();
|
|
7841
|
+
if (!/^[A-Za-z0-9_-]{8,32}$/.test(code)) return null;
|
|
7842
|
+
try {
|
|
7843
|
+
const filePath = prdWorkflowReviewShortLinkPath(root, code);
|
|
7844
|
+
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) return null;
|
|
7845
|
+
const link = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
7846
|
+
const targetPath = String(link?.targetPath || "").trim();
|
|
7847
|
+
if (!targetPath.startsWith("/api/prd-workflow/review/") || /[\r\n]/.test(targetPath)) return null;
|
|
7848
|
+
return {
|
|
7849
|
+
...link,
|
|
7850
|
+
shortCode: code,
|
|
7851
|
+
targetPath,
|
|
7852
|
+
filePath,
|
|
7853
|
+
};
|
|
7854
|
+
} catch {
|
|
7855
|
+
return null;
|
|
7856
|
+
}
|
|
7857
|
+
}
|
|
7858
|
+
|
|
7859
|
+
function prdWorkflowCreateReviewShortLink(root, reviewUrl, review = {}) {
|
|
7860
|
+
let parsed;
|
|
7861
|
+
try {
|
|
7862
|
+
parsed = new URL(String(reviewUrl || ""));
|
|
7863
|
+
} catch {
|
|
7864
|
+
return null;
|
|
7865
|
+
}
|
|
7866
|
+
const targetPath = `${parsed.pathname}${parsed.search}`;
|
|
7867
|
+
if (!targetPath.startsWith("/api/prd-workflow/review/")) return null;
|
|
7868
|
+
const digest = crypto.createHash("sha256").update(targetPath).digest("base64url");
|
|
7869
|
+
const dir = prdWorkflowReviewShortLinkDir(root);
|
|
7870
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
7871
|
+
for (let length = 8; length <= 24; length += 2) {
|
|
7872
|
+
const shortCode = digest.slice(0, length);
|
|
7873
|
+
const existing = prdWorkflowReadReviewShortLink(root, shortCode);
|
|
7874
|
+
if (existing && existing.targetPath !== targetPath) continue;
|
|
7875
|
+
const link = {
|
|
7876
|
+
shortCode,
|
|
7877
|
+
targetPath,
|
|
7878
|
+
tapdId: String(review?.tapdId || ""),
|
|
7879
|
+
reviewId: String(review?.id || ""),
|
|
7880
|
+
durability: String(review?.durability || "temporary"),
|
|
7881
|
+
expiresAt: String(review?.expiresAt || ""),
|
|
7882
|
+
createdAt: String(review?.createdAt || new Date().toISOString()),
|
|
7883
|
+
};
|
|
7884
|
+
fs.writeFileSync(
|
|
7885
|
+
prdWorkflowReviewShortLinkPath(root, shortCode),
|
|
7886
|
+
JSON.stringify(link, null, 2) + "\n",
|
|
7887
|
+
"utf-8",
|
|
7888
|
+
);
|
|
7889
|
+
return {
|
|
7890
|
+
...link,
|
|
7891
|
+
shortUrl: `${parsed.origin}/r/${shortCode}`,
|
|
7892
|
+
};
|
|
7893
|
+
}
|
|
7894
|
+
throw new Error("Unable to allocate a unique review short code");
|
|
7895
|
+
}
|
|
7896
|
+
|
|
7755
7897
|
function prdWorkflowPruneReviews(scopedRoot, tapdId, maxReviews = 200) {
|
|
7756
7898
|
try {
|
|
7757
7899
|
const dir = prdWorkflowReviewDir(scopedRoot, tapdId);
|
|
@@ -7772,10 +7914,12 @@ function prdWorkflowPruneReviews(scopedRoot, tapdId, maxReviews = 200) {
|
|
|
7772
7914
|
for (const entry of entries.filter((item) => Number.isFinite(item.expiresMs) && item.expiresMs < now)) {
|
|
7773
7915
|
try { fs.unlinkSync(entry.abs); } catch (_) {}
|
|
7774
7916
|
try { fs.unlinkSync(path.join(dir, `${entry.id}.md`)); } catch (_) {}
|
|
7917
|
+
try { fs.unlinkSync(prdWorkflowReviewIndexPath(tapdId, entry.id)); } catch (_) {}
|
|
7775
7918
|
}
|
|
7776
7919
|
for (const entry of entries.filter((item) => !(Number.isFinite(item.expiresMs) && item.expiresMs < now)).slice(maxReviews)) {
|
|
7777
7920
|
try { fs.unlinkSync(entry.abs); } catch (_) {}
|
|
7778
7921
|
try { fs.unlinkSync(path.join(dir, `${entry.id}.md`)); } catch (_) {}
|
|
7922
|
+
try { fs.unlinkSync(prdWorkflowReviewIndexPath(tapdId, entry.id)); } catch (_) {}
|
|
7779
7923
|
}
|
|
7780
7924
|
} catch (_) {}
|
|
7781
7925
|
}
|
|
@@ -7804,6 +7948,30 @@ function prdWorkflowReviewSplitFrontmatter(markdown) {
|
|
|
7804
7948
|
return { frontmatter: match[1].trim(), body: text.slice(match[0].length) };
|
|
7805
7949
|
}
|
|
7806
7950
|
|
|
7951
|
+
function prdWorkflowReviewStripLegacyMetadata(body) {
|
|
7952
|
+
const lines = String(body || "").replace(/\r\n/g, "\n").split("\n");
|
|
7953
|
+
const visible = [];
|
|
7954
|
+
let fenced = false;
|
|
7955
|
+
let hidden = false;
|
|
7956
|
+
for (const line of lines) {
|
|
7957
|
+
if (!hidden && /^\s*```/.test(line)) {
|
|
7958
|
+
fenced = !fenced;
|
|
7959
|
+
visible.push(line);
|
|
7960
|
+
continue;
|
|
7961
|
+
}
|
|
7962
|
+
if (!fenced && !hidden && /<!--\s*prd-flow-start\b/.test(line)) {
|
|
7963
|
+
hidden = !/prd-flow-end\s*-->/.test(line);
|
|
7964
|
+
continue;
|
|
7965
|
+
}
|
|
7966
|
+
if (hidden) {
|
|
7967
|
+
if (/prd-flow-end\s*-->/.test(line)) hidden = false;
|
|
7968
|
+
continue;
|
|
7969
|
+
}
|
|
7970
|
+
visible.push(line);
|
|
7971
|
+
}
|
|
7972
|
+
return visible.join("\n");
|
|
7973
|
+
}
|
|
7974
|
+
|
|
7807
7975
|
function prdWorkflowReviewRenderFrontmatter(frontmatter) {
|
|
7808
7976
|
if (!String(frontmatter || "").trim()) return "";
|
|
7809
7977
|
const rows = [];
|
|
@@ -8050,20 +8218,24 @@ export function prdWorkflowReviewMarkdownToHtml(markdown) {
|
|
|
8050
8218
|
const { frontmatter, body } = prdWorkflowReviewSplitFrontmatter(markdown);
|
|
8051
8219
|
return [
|
|
8052
8220
|
prdWorkflowReviewRenderFrontmatter(frontmatter),
|
|
8053
|
-
prdWorkflowReviewBodyToHtml(body),
|
|
8221
|
+
prdWorkflowReviewBodyToHtml(prdWorkflowReviewStripLegacyMetadata(body)),
|
|
8054
8222
|
].filter(Boolean).join("\n");
|
|
8055
8223
|
}
|
|
8056
8224
|
|
|
8057
8225
|
function prdWorkflowReviewExtractPageTitle(markdown, fallbackTitle) {
|
|
8058
8226
|
const { frontmatter, body } = prdWorkflowReviewSplitFrontmatter(markdown);
|
|
8059
|
-
const
|
|
8227
|
+
const visibleBody = prdWorkflowReviewStripLegacyMetadata(body);
|
|
8228
|
+
const lines = String(visibleBody || "").replace(/\r\n/g, "\n").split("\n");
|
|
8060
8229
|
const firstContentIndex = lines.findIndex((line) => String(line || "").trim());
|
|
8061
8230
|
const heading = firstContentIndex >= 0
|
|
8062
8231
|
? String(lines[firstContentIndex] || "").trim().match(/^#\s+(.+)$/)
|
|
8063
8232
|
: null;
|
|
8064
8233
|
if (!heading) {
|
|
8065
8234
|
return {
|
|
8066
|
-
markdown:
|
|
8235
|
+
markdown: [
|
|
8236
|
+
frontmatter ? `---\n${frontmatter}\n---` : "",
|
|
8237
|
+
visibleBody,
|
|
8238
|
+
].filter(Boolean).join("\n\n"),
|
|
8067
8239
|
title: String(fallbackTitle || "PRD Workflow Review"),
|
|
8068
8240
|
};
|
|
8069
8241
|
}
|
|
@@ -8275,7 +8447,7 @@ export function prdWorkflowReviewHtml(title, markdown, meta = {}) {
|
|
|
8275
8447
|
</html>`;
|
|
8276
8448
|
}
|
|
8277
8449
|
|
|
8278
|
-
function prdWorkflowCreateReview(scopedRoot, tapdId, payload = {}, urlBase = "") {
|
|
8450
|
+
function prdWorkflowCreateReview(scopedRoot, tapdId, payload = {}, urlBase = "", ownerId = "") {
|
|
8279
8451
|
const content = String(payload.markdown || payload.content || payload.rawOutput || "").slice(0, 500000);
|
|
8280
8452
|
if (!content.trim()) throw new Error("Missing review markdown");
|
|
8281
8453
|
const title = String(payload.title || payload.label || "PRD Workflow Review").trim().slice(0, 160) || "PRD Workflow Review";
|
|
@@ -8311,6 +8483,7 @@ function prdWorkflowCreateReview(scopedRoot, tapdId, payload = {}, urlBase = "")
|
|
|
8311
8483
|
fs.mkdirSync(paths.dir, { recursive: true });
|
|
8312
8484
|
fs.writeFileSync(paths.markdownPath, content.trimEnd() + "\n", "utf-8");
|
|
8313
8485
|
fs.writeFileSync(paths.metaPath, JSON.stringify(meta, null, 2) + "\n", "utf-8");
|
|
8486
|
+
prdWorkflowWriteReviewIndex(ownerId, tapdId, paths.id);
|
|
8314
8487
|
prdWorkflowPruneReviews(scopedRoot, tapdId);
|
|
8315
8488
|
prdWorkflowAppendAudit(scopedRoot, tapdId, {
|
|
8316
8489
|
type: "review-created",
|
|
@@ -10698,6 +10871,7 @@ function normalizeContextInstanceIds(raw) {
|
|
|
10698
10871
|
* @param {string} opts.workspaceRoot
|
|
10699
10872
|
* @param {number} opts.port
|
|
10700
10873
|
* @param {boolean} [opts.hideCommunityLinks]
|
|
10874
|
+
* @param {boolean} [opts.enableWorkspaceScheduler]
|
|
10701
10875
|
* @param {string} [opts.staticDir] 默认 PACKAGE_ROOT/builtin/web-ui/dist(npm run build 产出)
|
|
10702
10876
|
* @returns {Promise<import('http').Server>}
|
|
10703
10877
|
*/
|
|
@@ -10706,6 +10880,7 @@ export function startUiServer({
|
|
|
10706
10880
|
port,
|
|
10707
10881
|
host = "127.0.0.1",
|
|
10708
10882
|
hideCommunityLinks = false,
|
|
10883
|
+
enableWorkspaceScheduler = true,
|
|
10709
10884
|
staticDir = path.join(PACKAGE_ROOT, "builtin", "web-ui", "dist"),
|
|
10710
10885
|
}) {
|
|
10711
10886
|
const root = path.resolve(workspaceRoot);
|
|
@@ -11095,7 +11270,19 @@ export function startUiServer({
|
|
|
11095
11270
|
baseSnapshot,
|
|
11096
11271
|
getSessionTokenFromRequest(req) || "",
|
|
11097
11272
|
);
|
|
11098
|
-
|
|
11273
|
+
const workflowShare = workflowScope.collaboration?.shareToken
|
|
11274
|
+
? prdWorkflowShareLinkSummary(
|
|
11275
|
+
workflowScope.collaboration,
|
|
11276
|
+
workflowScope.collaboration.shareToken,
|
|
11277
|
+
serverPublicBaseUrl(req, host, uiPort),
|
|
11278
|
+
userCtx.userId,
|
|
11279
|
+
)
|
|
11280
|
+
: null;
|
|
11281
|
+
json(res, 200, {
|
|
11282
|
+
ok: true,
|
|
11283
|
+
snapshot,
|
|
11284
|
+
...(workflowShare ? { workflowShare, shareUrl: workflowShare.url } : {}),
|
|
11285
|
+
});
|
|
11099
11286
|
} catch (e) {
|
|
11100
11287
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
11101
11288
|
}
|
|
@@ -11120,6 +11307,22 @@ export function startUiServer({
|
|
|
11120
11307
|
json(res, 400, { error: "Missing tapdId" });
|
|
11121
11308
|
return;
|
|
11122
11309
|
}
|
|
11310
|
+
const rawSnapshot = payload.snapshot && typeof payload.snapshot === "object" && !Array.isArray(payload.snapshot)
|
|
11311
|
+
? payload.snapshot
|
|
11312
|
+
: payload.prd || payload.next ? payload : null;
|
|
11313
|
+
if (!rawSnapshot) {
|
|
11314
|
+
json(res, 400, { error: "Missing snapshot" });
|
|
11315
|
+
return;
|
|
11316
|
+
}
|
|
11317
|
+
const existingCollaboration = getPrdWorkflowCollaborationForUser(tapdId, userCtx.userId);
|
|
11318
|
+
const existingAccess = prdWorkflowCollaborationAccess(existingCollaboration, userCtx.userId);
|
|
11319
|
+
const shareResult = existingCollaboration && existingAccess.role !== "owner"
|
|
11320
|
+
? { record: existingCollaboration, created: false }
|
|
11321
|
+
: ensurePrdWorkflowShareLink({ tapdId, userId: userCtx.userId });
|
|
11322
|
+
if (shareResult.error) {
|
|
11323
|
+
json(res, shareResult.status || 400, { error: shareResult.error });
|
|
11324
|
+
return;
|
|
11325
|
+
}
|
|
11123
11326
|
const flowId = String(payload.flowId || "").trim();
|
|
11124
11327
|
const flowSource = String(payload.flowSource || "user").trim() || "user";
|
|
11125
11328
|
const archived = payload.archived === true || payload.flowArchived === true;
|
|
@@ -11136,13 +11339,6 @@ export function startUiServer({
|
|
|
11136
11339
|
}
|
|
11137
11340
|
const scopedRoot = workflowScope.stateRoot;
|
|
11138
11341
|
prdWorkflowMigrateLegacyState(workflowScope.executionRoot, scopedRoot, tapdId);
|
|
11139
|
-
const rawSnapshot = payload.snapshot && typeof payload.snapshot === "object" && !Array.isArray(payload.snapshot)
|
|
11140
|
-
? payload.snapshot
|
|
11141
|
-
: payload.prd || payload.next ? payload : null;
|
|
11142
|
-
if (!rawSnapshot) {
|
|
11143
|
-
json(res, 400, { error: "Missing snapshot" });
|
|
11144
|
-
return;
|
|
11145
|
-
}
|
|
11146
11342
|
const normalizedSnapshot = {
|
|
11147
11343
|
...prdWorkflowSnapshotFromParsed(scopedRoot, tapdId, rawSnapshot, userCtx, { flowSource, flowId }),
|
|
11148
11344
|
clientReportedAt: new Date().toISOString(),
|
|
@@ -11262,8 +11458,20 @@ export function startUiServer({
|
|
|
11262
11458
|
}
|
|
11263
11459
|
const materialized = prdWorkflowMaterializeSnapshot(workflowScope.executionRoot, scopedRoot, tapdId, userCtx, { flowSource, flowId });
|
|
11264
11460
|
const withDiagnostic = prdWorkflowWithAgentflowTokenDiagnostic(materialized, getSessionTokenFromRequest(req) || "");
|
|
11461
|
+
const workflowShare = shareResult.record?.shareToken
|
|
11462
|
+
? prdWorkflowShareLinkSummary(
|
|
11463
|
+
shareResult.record,
|
|
11464
|
+
shareResult.record.shareToken,
|
|
11465
|
+
serverPublicBaseUrl(req, host, uiPort, payload),
|
|
11466
|
+
userCtx.userId,
|
|
11467
|
+
)
|
|
11468
|
+
: null;
|
|
11265
11469
|
prdWorkflowBroadcast(prdWorkflowKey(userCtx, flowSource, flowId, tapdId), { type: "snapshot-report", tapdId, snapshot: withDiagnostic });
|
|
11266
|
-
json(res, 200, {
|
|
11470
|
+
json(res, 200, {
|
|
11471
|
+
ok: true,
|
|
11472
|
+
snapshot: withDiagnostic,
|
|
11473
|
+
...(workflowShare ? { workflowShare, shareUrl: workflowShare.url } : {}),
|
|
11474
|
+
});
|
|
11267
11475
|
} catch (e) {
|
|
11268
11476
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
11269
11477
|
}
|
|
@@ -11966,8 +12174,26 @@ export function startUiServer({
|
|
|
11966
12174
|
}
|
|
11967
12175
|
const scopedRoot = workflowScope.stateRoot;
|
|
11968
12176
|
prdWorkflowMigrateLegacyState(workflowScope.executionRoot, scopedRoot, tapdId);
|
|
11969
|
-
const review = prdWorkflowCreateReview(
|
|
11970
|
-
|
|
12177
|
+
const review = prdWorkflowCreateReview(
|
|
12178
|
+
scopedRoot,
|
|
12179
|
+
tapdId,
|
|
12180
|
+
payload,
|
|
12181
|
+
serverPublicBaseUrl(req, host, uiPort, payload),
|
|
12182
|
+
workflowScope.ownerId,
|
|
12183
|
+
);
|
|
12184
|
+
const query = new URLSearchParams();
|
|
12185
|
+
if (flowId) query.set("flowId", flowId);
|
|
12186
|
+
if (flowId && flowSource && flowSource !== "user") query.set("flowSource", flowSource);
|
|
12187
|
+
if (archived) query.set("archived", "1");
|
|
12188
|
+
const reviewUrl = query.toString() ? `${review.url}?${query.toString()}` : review.url;
|
|
12189
|
+
let shortLink = null;
|
|
12190
|
+
try {
|
|
12191
|
+
shortLink = prdWorkflowCreateReviewShortLink(root, reviewUrl, review);
|
|
12192
|
+
} catch (e) {
|
|
12193
|
+
log.debug(`[prd-workflow] review short link failed: ${(e && e.message) || String(e)}`);
|
|
12194
|
+
}
|
|
12195
|
+
const shortUrl = shortLink?.shortUrl || "";
|
|
12196
|
+
const displayUrl = shortUrl || reviewUrl;
|
|
11971
12197
|
const durability = review.durability || "temporary";
|
|
11972
12198
|
const reviewSource = review.source && typeof review.source === "object" && !Array.isArray(review.source)
|
|
11973
12199
|
? review.source
|
|
@@ -11979,7 +12205,9 @@ export function startUiServer({
|
|
|
11979
12205
|
durability,
|
|
11980
12206
|
source: reviewSource,
|
|
11981
12207
|
confirmed: payload.confirmed === true || payload.confirmed === "1",
|
|
11982
|
-
url:
|
|
12208
|
+
url: displayUrl,
|
|
12209
|
+
canonicalUrl: reviewUrl,
|
|
12210
|
+
shortUrl,
|
|
11983
12211
|
expiresAt: review.expiresAt || "",
|
|
11984
12212
|
};
|
|
11985
12213
|
const event = prdWorkflowAppendRuntimeEvent(scopedRoot, tapdId, {
|
|
@@ -11998,15 +12226,35 @@ export function startUiServer({
|
|
|
11998
12226
|
sourceArtifact: reviewSource,
|
|
11999
12227
|
expiresAt: review.expiresAt || "",
|
|
12000
12228
|
artifacts: [artifact],
|
|
12001
|
-
links: [{
|
|
12229
|
+
links: [{
|
|
12230
|
+
label: "Markdown Review",
|
|
12231
|
+
url: displayUrl,
|
|
12232
|
+
canonicalUrl: reviewUrl,
|
|
12233
|
+
shortUrl,
|
|
12234
|
+
persistence: "runtime",
|
|
12235
|
+
durability,
|
|
12236
|
+
source: reviewSource,
|
|
12237
|
+
expiresAt: review.expiresAt || "",
|
|
12238
|
+
}],
|
|
12002
12239
|
reviewId: review.id,
|
|
12240
|
+
reviewShortCode: shortLink?.shortCode || "",
|
|
12003
12241
|
});
|
|
12004
12242
|
const snapshot = prdWorkflowWithAgentflowTokenDiagnostic(
|
|
12005
12243
|
await prdWorkflowSnapshot(workflowScope.executionRoot, scopedRoot, tapdId, userCtx, { flowSource, flowId }),
|
|
12006
12244
|
getSessionTokenFromRequest(req) || "",
|
|
12007
12245
|
);
|
|
12008
12246
|
prdWorkflowBroadcast(prdWorkflowKey(userCtx, flowSource, flowId, tapdId), { type: "review-link", tapdId, event, snapshot });
|
|
12009
|
-
json(res, 200, {
|
|
12247
|
+
json(res, 200, {
|
|
12248
|
+
ok: true,
|
|
12249
|
+
review: {
|
|
12250
|
+
...review,
|
|
12251
|
+
url: reviewUrl,
|
|
12252
|
+
shortUrl,
|
|
12253
|
+
shortCode: shortLink?.shortCode || "",
|
|
12254
|
+
},
|
|
12255
|
+
event,
|
|
12256
|
+
snapshot,
|
|
12257
|
+
});
|
|
12010
12258
|
} catch (e) {
|
|
12011
12259
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
12012
12260
|
}
|
|
@@ -12053,6 +12301,41 @@ export function startUiServer({
|
|
|
12053
12301
|
return;
|
|
12054
12302
|
}
|
|
12055
12303
|
|
|
12304
|
+
if (req.method === "GET" && url.pathname.startsWith("/r/")) {
|
|
12305
|
+
try {
|
|
12306
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
12307
|
+
const shortCode = decodeURIComponent(parts[1] || "");
|
|
12308
|
+
if (parts.length !== 2) {
|
|
12309
|
+
res.writeHead(404);
|
|
12310
|
+
res.end("Not found");
|
|
12311
|
+
return;
|
|
12312
|
+
}
|
|
12313
|
+
const link = prdWorkflowReadReviewShortLink(root, shortCode);
|
|
12314
|
+
if (!link) {
|
|
12315
|
+
res.writeHead(404);
|
|
12316
|
+
res.end("Not found");
|
|
12317
|
+
return;
|
|
12318
|
+
}
|
|
12319
|
+
const expiresMs = Date.parse(link.expiresAt || "");
|
|
12320
|
+
if (Number.isFinite(expiresMs) && expiresMs < Date.now()) {
|
|
12321
|
+
try { fs.unlinkSync(link.filePath); } catch (_) {}
|
|
12322
|
+
res.writeHead(410, { "Content-Type": "text/plain; charset=utf-8" });
|
|
12323
|
+
res.end("Review link expired");
|
|
12324
|
+
return;
|
|
12325
|
+
}
|
|
12326
|
+
res.writeHead(302, {
|
|
12327
|
+
Location: link.targetPath,
|
|
12328
|
+
"Cache-Control": "no-store",
|
|
12329
|
+
"Referrer-Policy": "no-referrer",
|
|
12330
|
+
});
|
|
12331
|
+
res.end();
|
|
12332
|
+
} catch {
|
|
12333
|
+
res.writeHead(404);
|
|
12334
|
+
res.end("Not found");
|
|
12335
|
+
}
|
|
12336
|
+
return;
|
|
12337
|
+
}
|
|
12338
|
+
|
|
12056
12339
|
if (req.method === "GET" && url.pathname.startsWith("/api/prd-workflow/review/")) {
|
|
12057
12340
|
try {
|
|
12058
12341
|
const parts = url.pathname.split("/").filter(Boolean);
|
|
@@ -12081,8 +12364,8 @@ export function startUiServer({
|
|
|
12081
12364
|
}
|
|
12082
12365
|
const scopedRoot = workflowScope.stateRoot;
|
|
12083
12366
|
prdWorkflowMigrateLegacyState(workflowScope.executionRoot, scopedRoot, tapdId);
|
|
12084
|
-
const paths =
|
|
12085
|
-
if (!
|
|
12367
|
+
const paths = prdWorkflowResolveReviewPaths(scopedRoot, tapdId, reviewId);
|
|
12368
|
+
if (!prdWorkflowReviewFileExists(paths)) {
|
|
12086
12369
|
res.writeHead(404);
|
|
12087
12370
|
res.end("Not found");
|
|
12088
12371
|
return;
|
|
@@ -12364,6 +12647,35 @@ export function startUiServer({
|
|
|
12364
12647
|
return;
|
|
12365
12648
|
}
|
|
12366
12649
|
|
|
12650
|
+
if (req.method === "GET" && url.pathname === "/api/admin/run-detail") {
|
|
12651
|
+
if (!authUser?.isAdmin) {
|
|
12652
|
+
json(res, 403, { error: "Admin permission required" });
|
|
12653
|
+
return;
|
|
12654
|
+
}
|
|
12655
|
+
const input = {
|
|
12656
|
+
runType: url.searchParams.get("runType") || "pipeline",
|
|
12657
|
+
userId: url.searchParams.get("userId") || "",
|
|
12658
|
+
flowId: url.searchParams.get("flowId") || "",
|
|
12659
|
+
flowSource: url.searchParams.get("flowSource") || "user",
|
|
12660
|
+
runId: url.searchParams.get("runId") || "",
|
|
12661
|
+
};
|
|
12662
|
+
if (!input.userId || !input.flowId || !input.runId) {
|
|
12663
|
+
json(res, 400, { error: "Missing userId, flowId or runId" });
|
|
12664
|
+
return;
|
|
12665
|
+
}
|
|
12666
|
+
try {
|
|
12667
|
+
const detail = readAdminRunDetail(root, input);
|
|
12668
|
+
if (!detail) {
|
|
12669
|
+
json(res, 404, { error: "Run detail not found" });
|
|
12670
|
+
return;
|
|
12671
|
+
}
|
|
12672
|
+
json(res, 200, detail);
|
|
12673
|
+
} catch (e) {
|
|
12674
|
+
json(res, 500, { error: (e && e.message) || String(e) });
|
|
12675
|
+
}
|
|
12676
|
+
return;
|
|
12677
|
+
}
|
|
12678
|
+
|
|
12367
12679
|
if (url.pathname === "/api/admin/user-allowlist") {
|
|
12368
12680
|
if (!authUser?.isAdmin) {
|
|
12369
12681
|
json(res, 403, { error: "Admin permission required" });
|
|
@@ -16639,24 +16951,26 @@ finishedAt: "${new Date().toISOString()}"
|
|
|
16639
16951
|
res.end(data);
|
|
16640
16952
|
});
|
|
16641
16953
|
|
|
16642
|
-
|
|
16643
|
-
|
|
16644
|
-
|
|
16645
|
-
|
|
16646
|
-
|
|
16647
|
-
|
|
16648
|
-
|
|
16649
|
-
|
|
16650
|
-
workspaceScheduleTimer.unref?.();
|
|
16651
|
-
} catch (_) {}
|
|
16652
|
-
server.on("close", () => clearInterval(workspaceScheduleTimer));
|
|
16653
|
-
setTimeout(() => {
|
|
16954
|
+
if (enableWorkspaceScheduler) {
|
|
16955
|
+
const workspaceScheduleTimer = setInterval(() => {
|
|
16956
|
+
try {
|
|
16957
|
+
pollWorkspaceSchedules(root);
|
|
16958
|
+
} catch (e) {
|
|
16959
|
+
log.debug(`[workspace-scheduler] poll failed: ${(e && e.message) || String(e)}`);
|
|
16960
|
+
}
|
|
16961
|
+
}, WORKSPACE_SCHEDULE_POLL_MS);
|
|
16654
16962
|
try {
|
|
16655
|
-
|
|
16656
|
-
} catch (
|
|
16657
|
-
|
|
16658
|
-
|
|
16659
|
-
|
|
16963
|
+
workspaceScheduleTimer.unref?.();
|
|
16964
|
+
} catch (_) {}
|
|
16965
|
+
server.on("close", () => clearInterval(workspaceScheduleTimer));
|
|
16966
|
+
setTimeout(() => {
|
|
16967
|
+
try {
|
|
16968
|
+
pollWorkspaceSchedules(root);
|
|
16969
|
+
} catch (e) {
|
|
16970
|
+
log.debug(`[workspace-scheduler] initial poll failed: ${(e && e.message) || String(e)}`);
|
|
16971
|
+
}
|
|
16972
|
+
}, 1000).unref?.();
|
|
16973
|
+
}
|
|
16660
16974
|
|
|
16661
16975
|
return new Promise((resolve, reject) => {
|
|
16662
16976
|
server.once("error", reject);
|