@fieldwangai/agentflow 0.1.114 → 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/ui-server.mjs
CHANGED
|
@@ -7724,6 +7724,78 @@ function prdWorkflowReviewPaths(scopedRoot, tapdId, reviewId) {
|
|
|
7724
7724
|
};
|
|
7725
7725
|
}
|
|
7726
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
|
+
|
|
7727
7799
|
function prdWorkflowMigrateLegacyState(legacyRoot, stateRoot, tapdId) {
|
|
7728
7800
|
const sourceRoot = path.resolve(legacyRoot || "");
|
|
7729
7801
|
const destinationRoot = path.resolve(stateRoot || "");
|
|
@@ -7753,6 +7825,75 @@ function prdWorkflowMigrateLegacyState(legacyRoot, stateRoot, tapdId) {
|
|
|
7753
7825
|
} catch (_) {}
|
|
7754
7826
|
}
|
|
7755
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
|
+
|
|
7756
7897
|
function prdWorkflowPruneReviews(scopedRoot, tapdId, maxReviews = 200) {
|
|
7757
7898
|
try {
|
|
7758
7899
|
const dir = prdWorkflowReviewDir(scopedRoot, tapdId);
|
|
@@ -7773,10 +7914,12 @@ function prdWorkflowPruneReviews(scopedRoot, tapdId, maxReviews = 200) {
|
|
|
7773
7914
|
for (const entry of entries.filter((item) => Number.isFinite(item.expiresMs) && item.expiresMs < now)) {
|
|
7774
7915
|
try { fs.unlinkSync(entry.abs); } catch (_) {}
|
|
7775
7916
|
try { fs.unlinkSync(path.join(dir, `${entry.id}.md`)); } catch (_) {}
|
|
7917
|
+
try { fs.unlinkSync(prdWorkflowReviewIndexPath(tapdId, entry.id)); } catch (_) {}
|
|
7776
7918
|
}
|
|
7777
7919
|
for (const entry of entries.filter((item) => !(Number.isFinite(item.expiresMs) && item.expiresMs < now)).slice(maxReviews)) {
|
|
7778
7920
|
try { fs.unlinkSync(entry.abs); } catch (_) {}
|
|
7779
7921
|
try { fs.unlinkSync(path.join(dir, `${entry.id}.md`)); } catch (_) {}
|
|
7922
|
+
try { fs.unlinkSync(prdWorkflowReviewIndexPath(tapdId, entry.id)); } catch (_) {}
|
|
7780
7923
|
}
|
|
7781
7924
|
} catch (_) {}
|
|
7782
7925
|
}
|
|
@@ -7805,6 +7948,30 @@ function prdWorkflowReviewSplitFrontmatter(markdown) {
|
|
|
7805
7948
|
return { frontmatter: match[1].trim(), body: text.slice(match[0].length) };
|
|
7806
7949
|
}
|
|
7807
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
|
+
|
|
7808
7975
|
function prdWorkflowReviewRenderFrontmatter(frontmatter) {
|
|
7809
7976
|
if (!String(frontmatter || "").trim()) return "";
|
|
7810
7977
|
const rows = [];
|
|
@@ -8051,20 +8218,24 @@ export function prdWorkflowReviewMarkdownToHtml(markdown) {
|
|
|
8051
8218
|
const { frontmatter, body } = prdWorkflowReviewSplitFrontmatter(markdown);
|
|
8052
8219
|
return [
|
|
8053
8220
|
prdWorkflowReviewRenderFrontmatter(frontmatter),
|
|
8054
|
-
prdWorkflowReviewBodyToHtml(body),
|
|
8221
|
+
prdWorkflowReviewBodyToHtml(prdWorkflowReviewStripLegacyMetadata(body)),
|
|
8055
8222
|
].filter(Boolean).join("\n");
|
|
8056
8223
|
}
|
|
8057
8224
|
|
|
8058
8225
|
function prdWorkflowReviewExtractPageTitle(markdown, fallbackTitle) {
|
|
8059
8226
|
const { frontmatter, body } = prdWorkflowReviewSplitFrontmatter(markdown);
|
|
8060
|
-
const
|
|
8227
|
+
const visibleBody = prdWorkflowReviewStripLegacyMetadata(body);
|
|
8228
|
+
const lines = String(visibleBody || "").replace(/\r\n/g, "\n").split("\n");
|
|
8061
8229
|
const firstContentIndex = lines.findIndex((line) => String(line || "").trim());
|
|
8062
8230
|
const heading = firstContentIndex >= 0
|
|
8063
8231
|
? String(lines[firstContentIndex] || "").trim().match(/^#\s+(.+)$/)
|
|
8064
8232
|
: null;
|
|
8065
8233
|
if (!heading) {
|
|
8066
8234
|
return {
|
|
8067
|
-
markdown:
|
|
8235
|
+
markdown: [
|
|
8236
|
+
frontmatter ? `---\n${frontmatter}\n---` : "",
|
|
8237
|
+
visibleBody,
|
|
8238
|
+
].filter(Boolean).join("\n\n"),
|
|
8068
8239
|
title: String(fallbackTitle || "PRD Workflow Review"),
|
|
8069
8240
|
};
|
|
8070
8241
|
}
|
|
@@ -8276,7 +8447,7 @@ export function prdWorkflowReviewHtml(title, markdown, meta = {}) {
|
|
|
8276
8447
|
</html>`;
|
|
8277
8448
|
}
|
|
8278
8449
|
|
|
8279
|
-
function prdWorkflowCreateReview(scopedRoot, tapdId, payload = {}, urlBase = "") {
|
|
8450
|
+
function prdWorkflowCreateReview(scopedRoot, tapdId, payload = {}, urlBase = "", ownerId = "") {
|
|
8280
8451
|
const content = String(payload.markdown || payload.content || payload.rawOutput || "").slice(0, 500000);
|
|
8281
8452
|
if (!content.trim()) throw new Error("Missing review markdown");
|
|
8282
8453
|
const title = String(payload.title || payload.label || "PRD Workflow Review").trim().slice(0, 160) || "PRD Workflow Review";
|
|
@@ -8312,6 +8483,7 @@ function prdWorkflowCreateReview(scopedRoot, tapdId, payload = {}, urlBase = "")
|
|
|
8312
8483
|
fs.mkdirSync(paths.dir, { recursive: true });
|
|
8313
8484
|
fs.writeFileSync(paths.markdownPath, content.trimEnd() + "\n", "utf-8");
|
|
8314
8485
|
fs.writeFileSync(paths.metaPath, JSON.stringify(meta, null, 2) + "\n", "utf-8");
|
|
8486
|
+
prdWorkflowWriteReviewIndex(ownerId, tapdId, paths.id);
|
|
8315
8487
|
prdWorkflowPruneReviews(scopedRoot, tapdId);
|
|
8316
8488
|
prdWorkflowAppendAudit(scopedRoot, tapdId, {
|
|
8317
8489
|
type: "review-created",
|
|
@@ -10699,6 +10871,7 @@ function normalizeContextInstanceIds(raw) {
|
|
|
10699
10871
|
* @param {string} opts.workspaceRoot
|
|
10700
10872
|
* @param {number} opts.port
|
|
10701
10873
|
* @param {boolean} [opts.hideCommunityLinks]
|
|
10874
|
+
* @param {boolean} [opts.enableWorkspaceScheduler]
|
|
10702
10875
|
* @param {string} [opts.staticDir] 默认 PACKAGE_ROOT/builtin/web-ui/dist(npm run build 产出)
|
|
10703
10876
|
* @returns {Promise<import('http').Server>}
|
|
10704
10877
|
*/
|
|
@@ -10707,6 +10880,7 @@ export function startUiServer({
|
|
|
10707
10880
|
port,
|
|
10708
10881
|
host = "127.0.0.1",
|
|
10709
10882
|
hideCommunityLinks = false,
|
|
10883
|
+
enableWorkspaceScheduler = true,
|
|
10710
10884
|
staticDir = path.join(PACKAGE_ROOT, "builtin", "web-ui", "dist"),
|
|
10711
10885
|
}) {
|
|
10712
10886
|
const root = path.resolve(workspaceRoot);
|
|
@@ -11096,7 +11270,19 @@ export function startUiServer({
|
|
|
11096
11270
|
baseSnapshot,
|
|
11097
11271
|
getSessionTokenFromRequest(req) || "",
|
|
11098
11272
|
);
|
|
11099
|
-
|
|
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
|
+
});
|
|
11100
11286
|
} catch (e) {
|
|
11101
11287
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
11102
11288
|
}
|
|
@@ -11121,6 +11307,22 @@ export function startUiServer({
|
|
|
11121
11307
|
json(res, 400, { error: "Missing tapdId" });
|
|
11122
11308
|
return;
|
|
11123
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
|
+
}
|
|
11124
11326
|
const flowId = String(payload.flowId || "").trim();
|
|
11125
11327
|
const flowSource = String(payload.flowSource || "user").trim() || "user";
|
|
11126
11328
|
const archived = payload.archived === true || payload.flowArchived === true;
|
|
@@ -11137,13 +11339,6 @@ export function startUiServer({
|
|
|
11137
11339
|
}
|
|
11138
11340
|
const scopedRoot = workflowScope.stateRoot;
|
|
11139
11341
|
prdWorkflowMigrateLegacyState(workflowScope.executionRoot, scopedRoot, tapdId);
|
|
11140
|
-
const rawSnapshot = payload.snapshot && typeof payload.snapshot === "object" && !Array.isArray(payload.snapshot)
|
|
11141
|
-
? payload.snapshot
|
|
11142
|
-
: payload.prd || payload.next ? payload : null;
|
|
11143
|
-
if (!rawSnapshot) {
|
|
11144
|
-
json(res, 400, { error: "Missing snapshot" });
|
|
11145
|
-
return;
|
|
11146
|
-
}
|
|
11147
11342
|
const normalizedSnapshot = {
|
|
11148
11343
|
...prdWorkflowSnapshotFromParsed(scopedRoot, tapdId, rawSnapshot, userCtx, { flowSource, flowId }),
|
|
11149
11344
|
clientReportedAt: new Date().toISOString(),
|
|
@@ -11263,8 +11458,20 @@ export function startUiServer({
|
|
|
11263
11458
|
}
|
|
11264
11459
|
const materialized = prdWorkflowMaterializeSnapshot(workflowScope.executionRoot, scopedRoot, tapdId, userCtx, { flowSource, flowId });
|
|
11265
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;
|
|
11266
11469
|
prdWorkflowBroadcast(prdWorkflowKey(userCtx, flowSource, flowId, tapdId), { type: "snapshot-report", tapdId, snapshot: withDiagnostic });
|
|
11267
|
-
json(res, 200, {
|
|
11470
|
+
json(res, 200, {
|
|
11471
|
+
ok: true,
|
|
11472
|
+
snapshot: withDiagnostic,
|
|
11473
|
+
...(workflowShare ? { workflowShare, shareUrl: workflowShare.url } : {}),
|
|
11474
|
+
});
|
|
11268
11475
|
} catch (e) {
|
|
11269
11476
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
11270
11477
|
}
|
|
@@ -11967,8 +12174,26 @@ export function startUiServer({
|
|
|
11967
12174
|
}
|
|
11968
12175
|
const scopedRoot = workflowScope.stateRoot;
|
|
11969
12176
|
prdWorkflowMigrateLegacyState(workflowScope.executionRoot, scopedRoot, tapdId);
|
|
11970
|
-
const review = prdWorkflowCreateReview(
|
|
11971
|
-
|
|
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;
|
|
11972
12197
|
const durability = review.durability || "temporary";
|
|
11973
12198
|
const reviewSource = review.source && typeof review.source === "object" && !Array.isArray(review.source)
|
|
11974
12199
|
? review.source
|
|
@@ -11980,7 +12205,9 @@ export function startUiServer({
|
|
|
11980
12205
|
durability,
|
|
11981
12206
|
source: reviewSource,
|
|
11982
12207
|
confirmed: payload.confirmed === true || payload.confirmed === "1",
|
|
11983
|
-
url:
|
|
12208
|
+
url: displayUrl,
|
|
12209
|
+
canonicalUrl: reviewUrl,
|
|
12210
|
+
shortUrl,
|
|
11984
12211
|
expiresAt: review.expiresAt || "",
|
|
11985
12212
|
};
|
|
11986
12213
|
const event = prdWorkflowAppendRuntimeEvent(scopedRoot, tapdId, {
|
|
@@ -11999,15 +12226,35 @@ export function startUiServer({
|
|
|
11999
12226
|
sourceArtifact: reviewSource,
|
|
12000
12227
|
expiresAt: review.expiresAt || "",
|
|
12001
12228
|
artifacts: [artifact],
|
|
12002
|
-
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
|
+
}],
|
|
12003
12239
|
reviewId: review.id,
|
|
12240
|
+
reviewShortCode: shortLink?.shortCode || "",
|
|
12004
12241
|
});
|
|
12005
12242
|
const snapshot = prdWorkflowWithAgentflowTokenDiagnostic(
|
|
12006
12243
|
await prdWorkflowSnapshot(workflowScope.executionRoot, scopedRoot, tapdId, userCtx, { flowSource, flowId }),
|
|
12007
12244
|
getSessionTokenFromRequest(req) || "",
|
|
12008
12245
|
);
|
|
12009
12246
|
prdWorkflowBroadcast(prdWorkflowKey(userCtx, flowSource, flowId, tapdId), { type: "review-link", tapdId, event, snapshot });
|
|
12010
|
-
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
|
+
});
|
|
12011
12258
|
} catch (e) {
|
|
12012
12259
|
json(res, 500, { error: (e && e.message) || String(e) });
|
|
12013
12260
|
}
|
|
@@ -12054,6 +12301,41 @@ export function startUiServer({
|
|
|
12054
12301
|
return;
|
|
12055
12302
|
}
|
|
12056
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
|
+
|
|
12057
12339
|
if (req.method === "GET" && url.pathname.startsWith("/api/prd-workflow/review/")) {
|
|
12058
12340
|
try {
|
|
12059
12341
|
const parts = url.pathname.split("/").filter(Boolean);
|
|
@@ -12082,8 +12364,8 @@ export function startUiServer({
|
|
|
12082
12364
|
}
|
|
12083
12365
|
const scopedRoot = workflowScope.stateRoot;
|
|
12084
12366
|
prdWorkflowMigrateLegacyState(workflowScope.executionRoot, scopedRoot, tapdId);
|
|
12085
|
-
const paths =
|
|
12086
|
-
if (!
|
|
12367
|
+
const paths = prdWorkflowResolveReviewPaths(scopedRoot, tapdId, reviewId);
|
|
12368
|
+
if (!prdWorkflowReviewFileExists(paths)) {
|
|
12087
12369
|
res.writeHead(404);
|
|
12088
12370
|
res.end("Not found");
|
|
12089
12371
|
return;
|
|
@@ -16669,24 +16951,26 @@ finishedAt: "${new Date().toISOString()}"
|
|
|
16669
16951
|
res.end(data);
|
|
16670
16952
|
});
|
|
16671
16953
|
|
|
16672
|
-
|
|
16673
|
-
|
|
16674
|
-
|
|
16675
|
-
|
|
16676
|
-
|
|
16677
|
-
|
|
16678
|
-
|
|
16679
|
-
|
|
16680
|
-
workspaceScheduleTimer.unref?.();
|
|
16681
|
-
} catch (_) {}
|
|
16682
|
-
server.on("close", () => clearInterval(workspaceScheduleTimer));
|
|
16683
|
-
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);
|
|
16684
16962
|
try {
|
|
16685
|
-
|
|
16686
|
-
} catch (
|
|
16687
|
-
|
|
16688
|
-
|
|
16689
|
-
|
|
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
|
+
}
|
|
16690
16974
|
|
|
16691
16975
|
return new Promise((resolve, reject) => {
|
|
16692
16976
|
server.once("error", reject);
|