@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.
@@ -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 lines = String(body || "").replace(/\r\n/g, "\n").split("\n");
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: String(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
- json(res, 200, { ok: true, snapshot });
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, { ok: true, snapshot: withDiagnostic });
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(scopedRoot, tapdId, payload, serverPublicBaseUrl(req, host, uiPort, payload));
11971
- const reviewUrl = review.url;
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: reviewUrl,
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: [{ label: "Markdown Review", url: reviewUrl, persistence: "runtime", durability, source: reviewSource, expiresAt: review.expiresAt || "" }],
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, { ok: true, review: { ...review, url: reviewUrl }, event, snapshot });
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 = prdWorkflowReviewPaths(scopedRoot, tapdId, reviewId);
12086
- if (!fs.existsSync(paths.markdownPath) || !fs.statSync(paths.markdownPath).isFile()) {
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
- const workspaceScheduleTimer = setInterval(() => {
16673
- try {
16674
- pollWorkspaceSchedules(root);
16675
- } catch (e) {
16676
- log.debug(`[workspace-scheduler] poll failed: ${(e && e.message) || String(e)}`);
16677
- }
16678
- }, WORKSPACE_SCHEDULE_POLL_MS);
16679
- try {
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
- pollWorkspaceSchedules(root);
16686
- } catch (e) {
16687
- log.debug(`[workspace-scheduler] initial poll failed: ${(e && e.message) || String(e)}`);
16688
- }
16689
- }, 1000).unref?.();
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);