@tempo-ai/visual-review 0.0.1 → 0.0.2
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/dist/bin.js +2 -2
- package/dist/{chunk-HMPQK5ZG.js → chunk-4KKBAMNP.js} +14 -7
- package/dist/chunk-4KKBAMNP.js.map +1 -0
- package/dist/{chunk-4QAZFBC3.js → chunk-HTDJZ32L.js} +43 -9
- package/dist/chunk-HTDJZ32L.js.map +1 -0
- package/dist/{cli-3HH2BY7Y.js → cli-X7OOU6H4.js} +2 -2
- package/dist/index.js +2 -2
- package/dist/{report-RCKB2CVS.js → report-RD2OQ5TP.js} +6 -4
- package/package.json +2 -2
- package/dist/chunk-4QAZFBC3.js.map +0 -1
- package/dist/chunk-HMPQK5ZG.js.map +0 -1
- /package/dist/{cli-3HH2BY7Y.js.map → cli-X7OOU6H4.js.map} +0 -0
- /package/dist/{report-RCKB2CVS.js.map → report-RD2OQ5TP.js.map} +0 -0
package/dist/bin.js
CHANGED
|
@@ -6,11 +6,11 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
6
6
|
const [command, ...commandArgs] = argv;
|
|
7
7
|
switch (command) {
|
|
8
8
|
case "capture":
|
|
9
|
-
return await (await import("./cli-
|
|
9
|
+
return await (await import("./cli-X7OOU6H4.js")).main(commandArgs);
|
|
10
10
|
case "claim":
|
|
11
11
|
return await (await import("./claim-WI2ZVIDS.js")).main(commandArgs);
|
|
12
12
|
case "report":
|
|
13
|
-
return await (await import("./report-
|
|
13
|
+
return await (await import("./report-RD2OQ5TP.js")).main(commandArgs);
|
|
14
14
|
case "install-browsers":
|
|
15
15
|
return await (await import("./install-browsers-5RCJN3AK.js")).main(commandArgs);
|
|
16
16
|
default:
|
|
@@ -7,6 +7,13 @@ import {
|
|
|
7
7
|
import { execFileSync } from "child_process";
|
|
8
8
|
var STICKY_MARKER = "<!-- tempo-visual-review -->";
|
|
9
9
|
var STATUS_CONTEXT = "visual-review";
|
|
10
|
+
function publisherDeliveryFailure(status) {
|
|
11
|
+
const publishError = status.lastPublishError;
|
|
12
|
+
if (publishError == null) return null;
|
|
13
|
+
if (publishError.revision < (status.lastPublish?.revision ?? 0)) return null;
|
|
14
|
+
if (status.statusPublishing !== false) return null;
|
|
15
|
+
return publishError;
|
|
16
|
+
}
|
|
10
17
|
function parseArgs(argv) {
|
|
11
18
|
const args = {
|
|
12
19
|
pollTimeoutS: 900,
|
|
@@ -34,7 +41,7 @@ function parseArgs(argv) {
|
|
|
34
41
|
return args;
|
|
35
42
|
}
|
|
36
43
|
function gh(...ghArgs) {
|
|
37
|
-
return execFileSync("gh", ghArgs, { encoding: "utf-8" });
|
|
44
|
+
return execFileSync("gh", ghArgs, { encoding: "utf-8", windowsHide: true });
|
|
38
45
|
}
|
|
39
46
|
function postCommitStatus(repo, sha, state, description, targetUrl) {
|
|
40
47
|
gh(
|
|
@@ -254,16 +261,15 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
254
261
|
await new Promise((resolve) => setTimeout(resolve, 5e3));
|
|
255
262
|
}
|
|
256
263
|
const verdict = describeGate(status.gate ?? {}, status.state);
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
if (status.publisherConfigured && !publishFailing) {
|
|
264
|
+
const publishFailure = publisherDeliveryFailure(status);
|
|
265
|
+
if (status.publisherConfigured && publishFailure === null) {
|
|
260
266
|
console.log(
|
|
261
267
|
`verdict (${verdict.state}) owned by the Convex publisher \u2014 skipping CI status write`
|
|
262
268
|
);
|
|
263
269
|
} else {
|
|
264
|
-
if (
|
|
270
|
+
if (publishFailure !== null) {
|
|
265
271
|
console.log(
|
|
266
|
-
`Convex publisher reported a delivery failure (${
|
|
272
|
+
`Convex publisher reported a delivery failure (${publishFailure.error}) \u2014 writing the status from CI as a fallback`
|
|
267
273
|
);
|
|
268
274
|
}
|
|
269
275
|
postCommitStatus(
|
|
@@ -283,6 +289,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
283
289
|
}
|
|
284
290
|
|
|
285
291
|
export {
|
|
292
|
+
publisherDeliveryFailure,
|
|
286
293
|
main
|
|
287
294
|
};
|
|
288
|
-
//# sourceMappingURL=chunk-
|
|
295
|
+
//# sourceMappingURL=chunk-4KKBAMNP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/report.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\n\nimport { visualReviewPrDeclaration } from \"./github-actions\";\n\nconst STICKY_MARKER = \"<!-- tempo-visual-review -->\";\nconst STATUS_CONTEXT = \"visual-review\";\n\ntype ReportArgs = {\n pollTimeoutS: number;\n bypass: boolean;\n noVisualChange: boolean;\n fallbackFailure: boolean;\n statusUrl?: string;\n pr?: number;\n sha?: string;\n};\n\ntype Gate = {\n satisfied?: boolean;\n required?: number;\n approvalsNeeded?: number;\n rejected?: number;\n blockingErrors?: number;\n};\n\n/**\n * Shape of the `/visual-review/status` poll. Convex serialises absent\n * optional fields as `null`, not `undefined` — every optional here must be\n * read null-safely.\n */\nexport type ReportStatus = {\n state?: \"complete\" | \"error\" | string;\n reportUrl?: string;\n trackedTotal?: number;\n counts?: Record<string, number | undefined>;\n gate?: Gate;\n topChanged?: Array<{\n name?: string;\n storyboardId: string;\n diffRatio?: number | null;\n verdict: string;\n error?: string;\n baselineHash?: string;\n candidateHash?: string;\n }>;\n blobUrlTemplate?: string;\n approximateBase?: boolean;\n freshEpoch?: boolean;\n publisherConfigured?: boolean;\n lastPublishError?: { revision: number; error: string } | null;\n lastPublish?: { revision: number } | null;\n statusPublishing?: boolean | null;\n};\n\n/**\n * `publisherConfigured` only means the GitHub App env is set GLOBALLY — it\n * does NOT mean the app can post on THIS repo (it may not be installed\n * there, or lack the permission). When the publisher has a TERMINAL\n * outstanding failure for the current revision, CI writes the status itself\n * so the check can't hang pending forever. Terminal means the error is\n * current (revision-compare, mirroring the portal's \"delivery failing\"\n * banner) AND no publish worker holds the singleflight claim\n * (`statusPublishing === false`). A transient failure mid-retry-ladder keeps\n * the claim, so falling back then would race the publisher's own retry and\n * could land a stale snapshot after a newer portal flip.\n *\n * Returns the terminal failure, or `null` when Convex still owns the verdict.\n */\nexport function publisherDeliveryFailure(\n status: ReportStatus,\n): { revision: number; error: string } | null {\n const publishError = status.lastPublishError;\n // A healthy run reports `lastPublishError: null` (0.0.1 compared against\n // `undefined` only and crashed on `.revision`).\n if (publishError == null) return null;\n if (publishError.revision < (status.lastPublish?.revision ?? 0)) return null;\n if (status.statusPublishing !== false) return null;\n return publishError;\n}\n\nfunction parseArgs(argv: string[]): ReportArgs {\n const args: ReportArgs = {\n pollTimeoutS: 900,\n bypass: false,\n noVisualChange: false,\n fallbackFailure: false,\n };\n for (let index = 0; index < argv.length; index += 1) {\n const flag = argv[index];\n const next = () => {\n const value = argv[++index];\n if (value === undefined) throw new Error(`${flag} needs a value`);\n return value;\n };\n if (flag === \"--status-url\") args.statusUrl = next();\n else if (flag === \"--pr\") args.pr = Number(next());\n else if (flag === \"--sha\") args.sha = next();\n else if (flag === \"--poll-timeout-s\") args.pollTimeoutS = Number(next());\n else if (flag === \"--bypass\") args.bypass = true;\n else if (flag === \"--no-visual-change\") args.noVisualChange = true;\n else if (flag === \"--fallback-failure\") args.fallbackFailure = true;\n else throw new Error(`unknown flag ${flag}`);\n }\n if (!args.sha) throw new Error(\"--sha is required\");\n return args;\n}\n\nfunction gh(...ghArgs: string[]): string {\n return execFileSync(\"gh\", ghArgs, { encoding: \"utf-8\", windowsHide: true });\n}\n\nfunction postCommitStatus(\n repo: string,\n sha: string,\n state: \"success\" | \"failure\" | \"pending\",\n description: string,\n targetUrl?: string,\n): void {\n gh(\n \"api\",\n `repos/${repo}/statuses/${sha}`,\n \"-f\",\n `state=${state}`,\n \"-f\",\n `context=${STATUS_CONTEXT}`,\n \"-f\",\n `description=${description.slice(0, 140)}`,\n ...(targetUrl ? [\"-f\", `target_url=${targetUrl}`] : []),\n );\n}\n\nfunction upsertStickyComment(repo: string, pr: number, body: string): void {\n const comments = (\n JSON.parse(\n gh(\"api\", `repos/${repo}/issues/${pr}/comments`, \"--paginate\", \"--slurp\"),\n ) as Array<Array<{ id: number; body?: string }>>\n ).flat();\n const existing = comments.find(\n (comment) =>\n typeof comment.body === \"string\" && comment.body.includes(STICKY_MARKER),\n );\n if (existing) {\n gh(\n \"api\",\n \"--method\",\n \"PATCH\",\n `repos/${repo}/issues/comments/${existing.id}`,\n \"-f\",\n `body=${body}`,\n );\n } else {\n gh(\"api\", `repos/${repo}/issues/${pr}/comments`, \"-f\", `body=${body}`);\n }\n}\n\nfunction describeGate(\n gate: Gate,\n state: ReportStatus[\"state\"],\n): { state: \"success\" | \"failure\" | \"pending\"; description: string } {\n if (state === \"error\") {\n return {\n state: \"failure\",\n description: \"visual review run failed — see report\",\n };\n }\n if (state !== \"complete\") {\n return {\n state: \"pending\",\n description: \"capturing & comparing storyboards…\",\n };\n }\n if (gate.satisfied) {\n return {\n state: \"success\",\n description:\n gate.required === 0\n ? \"no visual changes\"\n : `${gate.required} visual change${gate.required === 1 ? \"\" : \"s\"} approved`,\n };\n }\n const parts: string[] = [];\n if ((gate.approvalsNeeded ?? 0) > 0) {\n parts.push(`${gate.approvalsNeeded} of ${gate.required} need approval`);\n }\n if ((gate.rejected ?? 0) > 0) parts.push(`${gate.rejected} rejected`);\n if ((gate.blockingErrors ?? 0) > 0) {\n parts.push(\n `${gate.blockingErrors} render error${gate.blockingErrors === 1 ? \"\" : \"s\"}`,\n );\n }\n return {\n state: \"failure\",\n description: `visual review: ${parts.join(\", \")} — see report`,\n };\n}\n\nfunction buildCommentBody(status: ReportStatus): string {\n const counts = status.counts ?? {};\n const gateLine = status.gate?.satisfied\n ? \"✅ **Check passing** — every visual change is approved.\"\n : `🔴 **Check failing** — ${status.gate?.approvalsNeeded ?? \"?\"} of ${status.gate?.required ?? \"?\"} storyboards need approval${status.gate?.blockingErrors ? `, ${status.gate.blockingErrors} render error(s)` : \"\"}${status.gate?.rejected ? `, ${status.gate.rejected} rejected` : \"\"}. Approving in the report flips the check instantly — no CI re-run.`;\n const summary = `| tracked | changed | new | removed | render errors | unchanged |\n| --- | --- | --- | --- | --- | --- |\n| ${status.trackedTotal ?? 0} | ${counts.changed ?? 0} | ${counts.added ?? 0} | ${counts.removed ?? 0} | ${counts.errors ?? 0} | ${counts.unchanged ?? 0} |`;\n\n const thumbs = (status.topChanged ?? [])\n .slice(0, 6)\n .map((board) => {\n const title = board.name ?? board.storyboardId;\n const meta =\n board.diffRatio !== undefined && board.diffRatio !== null\n ? ` · ${(board.diffRatio * 100).toFixed(2)}%`\n : \"\";\n if (board.verdict === \"render-error\") {\n return `**${title}** — render error${board.error ? `: \\`${board.error.slice(0, 120)}\\`` : \"\"}`;\n }\n const cell = (hash: string | undefined, label: string) =>\n hash && status.blobUrlTemplate\n ? `<img src=\"${status.blobUrlTemplate}${hash}\" alt=\"${label}\" width=\"360\" />`\n : \"—\";\n return `<details><summary><b>${title}</b> — ${board.verdict}${meta}</summary>\n\n| baseline | candidate |\n| --- | --- |\n| ${cell(board.baselineHash, \"baseline\")} | ${cell(board.candidateHash, \"candidate\")} |\n\n</details>`;\n })\n .join(\"\\n\");\n\n const notes: string[] = [];\n if (status.approximateBase) {\n notes.push(\n \"⚠ exact merge-base had no visual run — compared against the nearest captured ancestor.\",\n );\n }\n if (status.freshEpoch) {\n notes.push(\n \"ℹ fresh baseline epoch (capture environment changed) — everything reports as new.\",\n );\n }\n\n return `${STICKY_MARKER}\n## 👁 Visual review\n\n${gateLine}\n\n${summary}\n\n${notes.length > 0 ? `${notes.join(\"\\n\")}\\n` : \"\"}${thumbs}\n\n📷 **[Open the visual review report](${status.reportUrl})** — side-by-side, onion-skin and diff views; approve, reject, and comment there.`;\n}\n\nfunction buildNoVisualChangeComment(): string {\n return `${STICKY_MARKER}\n## 👁 Visual review\n\n✅ **Not applicable — no storyboard changes expected.** Storyboard capture was skipped from the PR declaration.`;\n}\n\nfunction latestContextState(repo: string, sha: string): string | null {\n const statuses = (\n JSON.parse(\n gh(\n \"api\",\n `repos/${repo}/commits/${sha}/statuses`,\n \"--paginate\",\n \"--slurp\",\n ),\n ) as Array<Array<{ context?: string; state?: string }>>\n ).flat();\n return (\n statuses.find((status) => status.context === STATUS_CONTEXT)?.state ?? null\n );\n}\n\nfunction isPermanentError(error: unknown): boolean {\n return (\n error instanceof Error && \"permanent\" in error && error.permanent === true\n );\n}\n\nexport async function main(argv = process.argv.slice(2)): Promise<number> {\n const args = parseArgs(argv);\n const repo = process.env.GITHUB_REPOSITORY;\n if (!repo) throw new Error(\"GITHUB_REPOSITORY is not set\");\n const sha = args.sha;\n if (!sha) throw new Error(\"--sha is required\");\n const declaration = visualReviewPrDeclaration();\n\n if (args.bypass || declaration.bypass) {\n postCommitStatus(\n repo,\n sha,\n \"success\",\n \"bypassed via visual-review:bypass label\",\n );\n console.log(\"bypass label present — posted success and skipped the report\");\n return 0;\n }\n\n if (args.noVisualChange || declaration.noVisualChange) {\n postCommitStatus(\n repo,\n sha,\n \"success\",\n \"not applicable — no storyboard changes expected\",\n );\n if (args.pr)\n upsertStickyComment(repo, args.pr, buildNoVisualChangeComment());\n console.log(\n \"no-visual-change declaration present — posted success and skipped capture\",\n );\n return 0;\n }\n\n if (args.fallbackFailure) {\n const current = latestContextState(repo, sha);\n if (current === null || current === \"pending\") {\n postCommitStatus(\n repo,\n sha,\n \"failure\",\n \"visual review reporting failed — see workflow logs\",\n );\n console.log(\n `reporting fallback: latest state was ${current ?? \"absent\"} — posted failure`,\n );\n } else {\n console.log(\n `reporting fallback: latest '${STATUS_CONTEXT}' state is '${current}' — leaving it`,\n );\n }\n return 0;\n }\n\n const statusUrl = args.statusUrl;\n if (!statusUrl) throw new Error(\"--status-url is required\");\n const deadline = Date.now() + args.pollTimeoutS * 1_000;\n let status: ReportStatus | undefined;\n for (;;) {\n try {\n const response = await fetch(statusUrl);\n if (response.status === 404) {\n const error = new Error(\"status poll 404 — wrong report key\");\n Object.assign(error, { permanent: true });\n throw error;\n }\n if (!response.ok)\n throw new Error(`status poll failed: ${response.status}`);\n status = (await response.json()) as ReportStatus;\n } catch (error) {\n if (isPermanentError(error)) throw error;\n console.warn(\n `transient status poll failure, retrying: ${error instanceof Error ? error.message : error}`,\n );\n }\n if (status && (status.state === \"complete\" || status.state === \"error\")) {\n break;\n }\n if (Date.now() > deadline) {\n postCommitStatus(\n repo,\n sha,\n \"failure\",\n \"visual review timed out waiting for compare — see report\",\n status?.reportUrl,\n );\n throw new Error(\"timed out waiting for the run to complete\");\n }\n await new Promise((resolve) => setTimeout(resolve, 5_000));\n }\n\n const verdict = describeGate(status.gate ?? {}, status.state);\n const publishFailure = publisherDeliveryFailure(status);\n if (status.publisherConfigured && publishFailure === null) {\n console.log(\n `verdict (${verdict.state}) owned by the Convex publisher — skipping CI status write`,\n );\n } else {\n if (publishFailure !== null) {\n console.log(\n `Convex publisher reported a delivery failure (${publishFailure.error}) — writing the status from CI as a fallback`,\n );\n }\n postCommitStatus(\n repo,\n sha,\n verdict.state,\n verdict.description,\n status.reportUrl,\n );\n console.log(`commit status: ${verdict.state} — ${verdict.description}`);\n }\n\n if (args.pr) {\n upsertStickyComment(repo, args.pr, buildCommentBody(status));\n console.log(`sticky comment upserted on #${args.pr}`);\n }\n return 0;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -8009,7 +8009,7 @@ ${output}` : "";
|
|
|
8009
8009
|
this.notifyExitListeners(code, signal, "launcher-finished", false);
|
|
8010
8010
|
return;
|
|
8011
8011
|
}
|
|
8012
|
-
this.lastExitCrashed = !this.intentionalStop && (code !== 0 || signal != null
|
|
8012
|
+
this.lastExitCrashed = !this.intentionalStop && !isWindowsTeardownExitCode(code) && (code !== 0 || signal != null || this.readinessConfirmed);
|
|
8013
8013
|
if (cleanLauncherExit) {
|
|
8014
8014
|
this.launcherStopRequired = true;
|
|
8015
8015
|
}
|
|
@@ -8051,11 +8051,17 @@ ${output}` : "";
|
|
|
8051
8051
|
onExit(listener) {
|
|
8052
8052
|
if (this.launcherFinished && !this.terminalExitNotified) {
|
|
8053
8053
|
this.exitListeners.push(listener);
|
|
8054
|
-
listener(0, null, false, "launcher-finished");
|
|
8054
|
+
listener(0, null, false, "launcher-finished", false);
|
|
8055
8055
|
return;
|
|
8056
8056
|
}
|
|
8057
8057
|
if (this.stopped) {
|
|
8058
|
-
listener(
|
|
8058
|
+
listener(
|
|
8059
|
+
null,
|
|
8060
|
+
null,
|
|
8061
|
+
this.lastExitCrashed,
|
|
8062
|
+
"process-exit",
|
|
8063
|
+
this.intentionalStop
|
|
8064
|
+
);
|
|
8059
8065
|
return;
|
|
8060
8066
|
}
|
|
8061
8067
|
this.exitListeners.push(listener);
|
|
@@ -8178,6 +8184,19 @@ Hint: ${current.hint}` : "";
|
|
|
8178
8184
|
this.lastExitCrashed = true;
|
|
8179
8185
|
this.notifyExitListeners(null, null, "probe-death");
|
|
8180
8186
|
}
|
|
8187
|
+
/**
|
|
8188
|
+
* The supervisor's liveness check found this child gone without an `exit`
|
|
8189
|
+
* event having reached us (a lost SIGCHLD). Deliver the one terminal exit
|
|
8190
|
+
* its consumers are owed, so the adapter and pool release the host instead
|
|
8191
|
+
* of keeping a dead incarnation `running`; an `exit` event that arrives
|
|
8192
|
+
* late is then a no-op (`terminalExitNotified`).
|
|
8193
|
+
*/
|
|
8194
|
+
reportObservedChildDeath() {
|
|
8195
|
+
if (this.stopped || this.terminalExitNotified) return;
|
|
8196
|
+
this.stopped = true;
|
|
8197
|
+
this.lastExitCrashed = !this.intentionalStop;
|
|
8198
|
+
this.notifyExitListeners(null, null, "process-exit");
|
|
8199
|
+
}
|
|
8181
8200
|
/** Whether the most recent exit looked like an unexpected crash. */
|
|
8182
8201
|
didLastExitCrash() {
|
|
8183
8202
|
return this.lastExitCrashed;
|
|
@@ -8434,7 +8453,13 @@ Hint: ${current.hint}` : "";
|
|
|
8434
8453
|
}
|
|
8435
8454
|
for (const listener of listeners) {
|
|
8436
8455
|
try {
|
|
8437
|
-
listener(
|
|
8456
|
+
listener(
|
|
8457
|
+
code,
|
|
8458
|
+
signal,
|
|
8459
|
+
this.lastExitCrashed,
|
|
8460
|
+
reason,
|
|
8461
|
+
this.intentionalStop
|
|
8462
|
+
);
|
|
8438
8463
|
} catch {
|
|
8439
8464
|
}
|
|
8440
8465
|
}
|
|
@@ -8811,13 +8836,13 @@ var TempoDevServer = class _TempoDevServer {
|
|
|
8811
8836
|
const hintListeners = [];
|
|
8812
8837
|
const exitListeners = [];
|
|
8813
8838
|
const attachProcessListeners = (activeProcess) => {
|
|
8814
|
-
activeProcess.onExit((code, signal, crashed, reason) => {
|
|
8839
|
+
activeProcess.onExit((code, signal, crashed, reason, intentional) => {
|
|
8815
8840
|
if (reason === "launcher-finished") {
|
|
8816
8841
|
devLog("supervisor:launcher_finished", {
|
|
8817
8842
|
pid: activeProcess.getPid()
|
|
8818
8843
|
});
|
|
8819
8844
|
for (const listener of exitListeners) {
|
|
8820
|
-
listener(code, signal, crashed, reason);
|
|
8845
|
+
listener(code, signal, crashed, reason, intentional);
|
|
8821
8846
|
}
|
|
8822
8847
|
return;
|
|
8823
8848
|
}
|
|
@@ -8850,7 +8875,7 @@ var TempoDevServer = class _TempoDevServer {
|
|
|
8850
8875
|
this.persistStatus().catch(() => {
|
|
8851
8876
|
});
|
|
8852
8877
|
for (const listener of exitListeners) {
|
|
8853
|
-
listener(code, signal, crashed, reason);
|
|
8878
|
+
listener(code, signal, crashed, reason, intentional);
|
|
8854
8879
|
}
|
|
8855
8880
|
});
|
|
8856
8881
|
for (const listener of outputListeners) {
|
|
@@ -8911,6 +8936,14 @@ var TempoDevServer = class _TempoDevServer {
|
|
|
8911
8936
|
this.devserverLaunchPid = activeProcess.getPid();
|
|
8912
8937
|
this.devserverLaunchProcessStartedAt = this.devserverLaunchPid ? await readProcessStartedAt(this.devserverLaunchPid) : null;
|
|
8913
8938
|
attachProcessListeners(activeProcess);
|
|
8939
|
+
try {
|
|
8940
|
+
options?.onProcessSpawned?.();
|
|
8941
|
+
} catch (error) {
|
|
8942
|
+
devLog("supervisor:on_process_spawned_threw", {
|
|
8943
|
+
pid: activeProcess.getPid(),
|
|
8944
|
+
error: error instanceof Error ? error.message : String(error)
|
|
8945
|
+
});
|
|
8946
|
+
}
|
|
8914
8947
|
await this.writeProcessMarker(activeProcess.getPid()).catch(() => {
|
|
8915
8948
|
});
|
|
8916
8949
|
if (options?.signal?.aborted) {
|
|
@@ -8987,7 +9020,7 @@ var TempoDevServer = class _TempoDevServer {
|
|
|
8987
9020
|
onExit(listener) {
|
|
8988
9021
|
exitListeners.push(listener);
|
|
8989
9022
|
if (process2.getLivenessSource() === "probe") {
|
|
8990
|
-
listener(0, null, false, "launcher-finished");
|
|
9023
|
+
listener(0, null, false, "launcher-finished", false);
|
|
8991
9024
|
}
|
|
8992
9025
|
},
|
|
8993
9026
|
onOutput(listener) {
|
|
@@ -9833,6 +9866,7 @@ var TempoDevServer = class _TempoDevServer {
|
|
|
9833
9866
|
this.stopUrlHealthProbe();
|
|
9834
9867
|
return;
|
|
9835
9868
|
}
|
|
9869
|
+
this.activeHostProcess?.reportObservedChildDeath();
|
|
9836
9870
|
this.state.devserverUrl = null;
|
|
9837
9871
|
this.state.urlUnreachableSince = null;
|
|
9838
9872
|
this.idleProcessStatus = "exited";
|
|
@@ -13583,4 +13617,4 @@ export {
|
|
|
13583
13617
|
main,
|
|
13584
13618
|
VISUAL_REVIEW_OIDC_AUDIENCE
|
|
13585
13619
|
};
|
|
13586
|
-
//# sourceMappingURL=chunk-
|
|
13620
|
+
//# sourceMappingURL=chunk-HTDJZ32L.js.map
|