@probelabs/visor 0.1.127 → 0.1.129
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/README.md +31 -1
- package/defaults/.visor.yaml +420 -0
- package/dist/ai-review-service.d.ts +1 -0
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/daemon.js +19 -0
- package/dist/defaults/.visor.yaml +420 -0
- package/dist/docs/commands.md +1 -1
- package/dist/docs/debugging.md +133 -0
- package/dist/docs/dev-playbook.md +10 -0
- package/dist/docs/index.md +1 -0
- package/dist/docs/scheduler.md +503 -0
- package/dist/docs/slack-integration.md +21 -0
- package/dist/docs/timeouts.md +1 -1
- package/dist/docs/workflow-creation-guide.md +39 -0
- package/dist/examples/README.md +30 -0
- package/dist/examples/calculator-config.yaml +4 -4
- package/dist/examples/sandbox-basic.yaml +18 -0
- package/dist/examples/sandbox-cache.yaml +32 -0
- package/dist/examples/sandbox-dockerfile-inline.yaml +22 -0
- package/dist/examples/sandbox-env-passthrough.yaml +26 -0
- package/dist/examples/sandbox-multi-env.yaml +27 -0
- package/dist/examples/sandbox-read-only.yaml +33 -0
- package/dist/examples/scheduler-example.yaml +118 -0
- package/dist/frontends/host.d.ts.map +1 -1
- package/dist/frontends/slack-frontend.d.ts.map +1 -1
- package/dist/generated/config-schema.d.ts +230 -9
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/index.js +13676 -1604
- package/dist/mcp-server.d.ts +8 -8
- package/dist/{traces/run-2026-02-01T09-59-08-165Z.ndjson → output/traces/run-2026-02-08T18-16-04-160Z.ndjson} +84 -84
- package/dist/{traces/run-2026-02-01T09-59-52-595Z.ndjson → output/traces/run-2026-02-08T18-16-51-253Z.ndjson} +1029 -1029
- package/dist/providers/ai-check-provider.d.ts +16 -0
- package/dist/providers/ai-check-provider.d.ts.map +1 -1
- package/dist/providers/check-provider.interface.d.ts +5 -0
- package/dist/providers/check-provider.interface.d.ts.map +1 -1
- package/dist/providers/command-check-provider.d.ts.map +1 -1
- package/dist/providers/log-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-check-provider.d.ts +3 -0
- package/dist/providers/mcp-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-custom-sse-server.d.ts +22 -2
- package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -1
- package/dist/providers/workflow-check-provider.d.ts.map +1 -1
- package/dist/providers/workflow-tool-executor.d.ts +2 -0
- package/dist/providers/workflow-tool-executor.d.ts.map +1 -1
- package/dist/sandbox/cache-volume-manager.d.ts +48 -0
- package/dist/sandbox/cache-volume-manager.d.ts.map +1 -0
- package/dist/sandbox/check-runner.d.ts +25 -0
- package/dist/sandbox/check-runner.d.ts.map +1 -0
- package/dist/sandbox/docker-compose-sandbox.d.ts +25 -0
- package/dist/sandbox/docker-compose-sandbox.d.ts.map +1 -0
- package/dist/sandbox/docker-image-sandbox.d.ts +32 -0
- package/dist/sandbox/docker-image-sandbox.d.ts.map +1 -0
- package/dist/sandbox/env-filter.d.ts +19 -0
- package/dist/sandbox/env-filter.d.ts.map +1 -0
- package/dist/sandbox/index.d.ts +9 -0
- package/dist/sandbox/index.d.ts.map +1 -0
- package/dist/sandbox/sandbox-manager.d.ts +39 -0
- package/dist/sandbox/sandbox-manager.d.ts.map +1 -0
- package/dist/sandbox/sandbox-telemetry.d.ts +9 -0
- package/dist/sandbox/sandbox-telemetry.d.ts.map +1 -0
- package/dist/sandbox/trace-ingester.d.ts +19 -0
- package/dist/sandbox/trace-ingester.d.ts.map +1 -0
- package/dist/sandbox/types.d.ts +149 -0
- package/dist/sandbox/types.d.ts.map +1 -0
- package/dist/scheduler/cli-handler.d.ts +6 -0
- package/dist/scheduler/cli-handler.d.ts.map +1 -0
- package/dist/scheduler/index.d.ts +14 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/scheduler/schedule-parser.d.ts +34 -0
- package/dist/scheduler/schedule-parser.d.ts.map +1 -0
- package/dist/scheduler/schedule-store.d.ts +182 -0
- package/dist/scheduler/schedule-store.d.ts.map +1 -0
- package/dist/scheduler/schedule-tool.d.ts +137 -0
- package/dist/scheduler/schedule-tool.d.ts.map +1 -0
- package/dist/scheduler/scheduler.d.ts +195 -0
- package/dist/scheduler/scheduler.d.ts.map +1 -0
- package/dist/sdk/check-provider-registry-ACRGIYOB.mjs +28 -0
- package/dist/sdk/check-provider-registry-VYHKFHK2.mjs +28 -0
- package/dist/sdk/{chunk-CNX7V5JK.mjs → chunk-25IC7KXZ.mjs} +2 -2
- package/dist/sdk/{chunk-IHZOSIF4.mjs → chunk-2KB35MB7.mjs} +3 -3
- package/dist/sdk/{chunk-HQL734ZI.mjs → chunk-6W75IMDC.mjs} +3 -3
- package/dist/sdk/{chunk-XWJPT5KQ.mjs → chunk-7YSOINAQ.mjs} +392 -18
- package/dist/sdk/chunk-7YSOINAQ.mjs.map +1 -0
- package/dist/sdk/{chunk-3OMWVM6J.mjs → chunk-B7BVQM5K.mjs} +2 -2
- package/dist/sdk/chunk-BDGUM6BA.mjs +38825 -0
- package/dist/sdk/chunk-BDGUM6BA.mjs.map +1 -0
- package/dist/sdk/{chunk-VW2GBXQT.mjs → chunk-D5KI4YQ4.mjs} +3 -3
- package/dist/sdk/chunk-DGZPPGJJ.mjs +38825 -0
- package/dist/sdk/chunk-DGZPPGJJ.mjs.map +1 -0
- package/dist/sdk/chunk-H5BOW5CR.mjs +91 -0
- package/dist/sdk/chunk-H5BOW5CR.mjs.map +1 -0
- package/dist/sdk/{chunk-YSN4G6CI.mjs → chunk-HEX3RL32.mjs} +81 -3
- package/dist/sdk/{chunk-YSN4G6CI.mjs.map → chunk-HEX3RL32.mjs.map} +1 -1
- package/dist/sdk/{chunk-ZYAUYXSW.mjs → chunk-J5RGJQ53.mjs} +14 -3
- package/dist/sdk/{chunk-ZYAUYXSW.mjs.map → chunk-J5RGJQ53.mjs.map} +1 -1
- package/dist/sdk/{chunk-WMJKH4XE.mjs → chunk-J7LXIPZS.mjs} +16 -1
- package/dist/sdk/{chunk-EXFGO4FX.mjs → chunk-KFKHU6CM.mjs} +2 -2
- package/dist/sdk/{chunk-MPS4HVQI.mjs → chunk-N7HO6KKC.mjs} +8 -8
- package/dist/sdk/{chunk-O5EZDNYL.mjs → chunk-NCWIZVOT.mjs} +2 -2
- package/dist/sdk/{chunk-3NMLT3YS.mjs → chunk-PO7X5XI7.mjs} +3 -3
- package/dist/sdk/{chunk-BHOKBQPB.mjs → chunk-R5Z7YWPB.mjs} +5 -5
- package/dist/sdk/{chunk-EORMDOZU.mjs → chunk-SGS2VMEL.mjs} +7 -7
- package/dist/sdk/{chunk-BOVFH3LI.mjs → chunk-VF6XIUE4.mjs} +21 -10
- package/dist/sdk/chunk-VF6XIUE4.mjs.map +1 -0
- package/dist/sdk/{chunk-J2QWVDXK.mjs → chunk-XDLQ3UNF.mjs} +3 -3
- package/dist/sdk/{chunk-S2RUE2RG.mjs → chunk-XR7XXGL7.mjs} +3 -3
- package/dist/sdk/{chunk-NAW3DB3I.mjs → chunk-XXAEN5KU.mjs} +3 -3
- package/dist/sdk/command-executor-DVVXERLR.mjs +14 -0
- package/dist/sdk/config-7VTT64SQ.mjs +16 -0
- package/dist/sdk/config-merger-RKCZJQ44.mjs +10 -0
- package/dist/sdk/event-bus-5K3Y2FCS.mjs +43 -0
- package/dist/sdk/{event-bus-5BEVPQ6T.mjs.map → event-bus-5K3Y2FCS.mjs.map} +1 -1
- package/dist/sdk/failure-condition-evaluator-4WMDF4Q3.mjs +17 -0
- package/dist/sdk/git-repository-analyzer-QFMW6WIS.mjs +471 -0
- package/dist/sdk/git-repository-analyzer-QFMW6WIS.mjs.map +1 -0
- package/dist/sdk/{github-frontend-5PCKKHVC.mjs → github-frontend-3N2NLO66.mjs} +520 -588
- package/dist/sdk/github-frontend-3N2NLO66.mjs.map +1 -0
- package/dist/sdk/host-ONVMEHAA.mjs +63 -0
- package/dist/sdk/host-ONVMEHAA.mjs.map +1 -0
- package/dist/sdk/{liquid-extensions-I7O7KMHF.mjs → liquid-extensions-5IZLTFSZ.mjs} +8 -8
- package/dist/sdk/memory-store-3N4AZCYB.mjs +12 -0
- package/dist/sdk/{metrics-7PP3EJUH.mjs → metrics-GXQ2EDXA.mjs} +4 -4
- package/dist/sdk/ndjson-sink-FD2PSXGD.mjs +52 -0
- package/dist/sdk/{ndjson-sink-B4V4NTAQ.mjs.map → ndjson-sink-FD2PSXGD.mjs.map} +1 -1
- package/dist/sdk/{prompt-state-EZYOUG75.mjs → prompt-state-YHGXB2OA.mjs} +5 -5
- package/dist/sdk/{renderer-schema-CKFB5NDB.mjs → renderer-schema-CMXOLNIG.mjs} +4 -4
- package/dist/sdk/routing-S3Y7T2X3.mjs +24 -0
- package/dist/sdk/sdk.d.mts +212 -4
- package/dist/sdk/sdk.d.ts +212 -4
- package/dist/sdk/sdk.js +26927 -6264
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +23 -1278
- package/dist/sdk/sdk.mjs.map +1 -1
- package/dist/sdk/session-registry-6PV6SGEJ.mjs +10 -0
- package/dist/sdk/slack-frontend-R3M2CACB.mjs +899 -0
- package/dist/sdk/slack-frontend-R3M2CACB.mjs.map +1 -0
- package/dist/sdk/{trace-helpers-VP6QYVBX.mjs → trace-helpers-YHNPC7MR.mjs} +4 -4
- package/dist/sdk/tracer-init-XPRWKMZT.mjs +10 -0
- package/dist/sdk/tui-frontend-S546M7A7.mjs +281 -0
- package/dist/sdk/tui-frontend-S546M7A7.mjs.map +1 -0
- package/dist/sdk/workflow-check-provider-4F3432ZP.mjs +28 -0
- package/dist/sdk/workflow-check-provider-A44PBPG2.mjs +28 -0
- package/dist/sdk/workflow-check-provider-A44PBPG2.mjs.map +1 -0
- package/dist/sdk/workflow-registry-ZAYYXLEP.mjs +12 -0
- package/dist/sdk/workflow-registry-ZAYYXLEP.mjs.map +1 -0
- package/dist/slack/client.d.ts +28 -0
- package/dist/slack/client.d.ts.map +1 -1
- package/dist/slack/schedule-tool-handler.d.ts +46 -0
- package/dist/slack/schedule-tool-handler.d.ts.map +1 -0
- package/dist/slack/slack-output-adapter.d.ts +44 -0
- package/dist/slack/slack-output-adapter.d.ts.map +1 -0
- package/dist/slack/socket-runner.d.ts +22 -0
- package/dist/slack/socket-runner.d.ts.map +1 -1
- package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -1
- package/dist/state-machine/dispatch/foreach-processor.d.ts.map +1 -1
- package/dist/state-machine/dispatch/sandbox-routing.d.ts +21 -0
- package/dist/state-machine/dispatch/sandbox-routing.d.ts.map +1 -0
- package/dist/state-machine/states/level-dispatch.d.ts.map +1 -1
- package/dist/state-machine-execution-engine.d.ts.map +1 -1
- package/dist/test-runner/index.d.ts +5 -0
- package/dist/test-runner/index.d.ts.map +1 -1
- package/dist/{output/traces/run-2026-02-01T09-59-08-165Z.ndjson → traces/run-2026-02-08T18-16-04-160Z.ndjson} +84 -84
- package/dist/{output/traces/run-2026-02-01T09-59-52-595Z.ndjson → traces/run-2026-02-08T18-16-51-253Z.ndjson} +1029 -1029
- package/dist/tui/chat-runner.d.ts +39 -0
- package/dist/tui/chat-runner.d.ts.map +1 -0
- package/dist/tui/chat-state.d.ts +56 -0
- package/dist/tui/chat-state.d.ts.map +1 -0
- package/dist/tui/chat-tui.d.ts +69 -0
- package/dist/tui/chat-tui.d.ts.map +1 -0
- package/dist/tui/components/chat-box.d.ts +33 -0
- package/dist/tui/components/chat-box.d.ts.map +1 -0
- package/dist/tui/components/input-bar.d.ts +50 -0
- package/dist/tui/components/input-bar.d.ts.map +1 -0
- package/dist/tui/components/status-bar.d.ts +31 -0
- package/dist/tui/components/status-bar.d.ts.map +1 -0
- package/dist/tui/components/trace-viewer.d.ts +73 -0
- package/dist/tui/components/trace-viewer.d.ts.map +1 -0
- package/dist/tui/index.d.ts +14 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/tui-frontend.d.ts +29 -0
- package/dist/tui/tui-frontend.d.ts.map +1 -0
- package/dist/types/bot.d.ts +35 -0
- package/dist/types/bot.d.ts.map +1 -1
- package/dist/types/config.d.ts +152 -3
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/engine.d.ts +3 -0
- package/dist/types/engine.d.ts.map +1 -1
- package/dist/utils/sandbox.d.ts.map +1 -1
- package/dist/utils/workspace-manager.d.ts +22 -2
- package/dist/utils/workspace-manager.d.ts.map +1 -1
- package/dist/utils/worktree-manager.d.ts +2 -1
- package/dist/utils/worktree-manager.d.ts.map +1 -1
- package/package.json +4 -2
- package/dist/docs/NPM_USAGE.md +0 -281
- package/dist/generated/config-schema.json +0 -2161
- package/dist/sdk/check-provider-registry-CVUONJ5A.mjs +0 -28
- package/dist/sdk/chunk-BOVFH3LI.mjs.map +0 -1
- package/dist/sdk/chunk-TS6BUNAI.mjs +0 -17722
- package/dist/sdk/chunk-TS6BUNAI.mjs.map +0 -1
- package/dist/sdk/chunk-XWJPT5KQ.mjs.map +0 -1
- package/dist/sdk/command-executor-Q7MHJKZJ.mjs +0 -14
- package/dist/sdk/config-DXX64GD3.mjs +0 -16
- package/dist/sdk/config-merger-PX3WIT57.mjs +0 -10
- package/dist/sdk/event-bus-5BEVPQ6T.mjs +0 -35
- package/dist/sdk/failure-condition-evaluator-G4HMJPXF.mjs +0 -17
- package/dist/sdk/git-repository-analyzer-HJC4MYW4.mjs +0 -458
- package/dist/sdk/git-repository-analyzer-HJC4MYW4.mjs.map +0 -1
- package/dist/sdk/github-frontend-5PCKKHVC.mjs.map +0 -1
- package/dist/sdk/host-H3AWNZ2F.mjs +0 -52
- package/dist/sdk/host-H3AWNZ2F.mjs.map +0 -1
- package/dist/sdk/memory-store-RW5N2NGJ.mjs +0 -12
- package/dist/sdk/ndjson-sink-B4V4NTAQ.mjs +0 -44
- package/dist/sdk/routing-QHTGDIXF.mjs +0 -24
- package/dist/sdk/session-registry-4E6YRQ77.mjs +0 -10
- package/dist/sdk/slack-frontend-JUT3TYVC.mjs +0 -821
- package/dist/sdk/slack-frontend-JUT3TYVC.mjs.map +0 -1
- package/dist/sdk/tracer-init-GSLPPLCD.mjs +0 -10
- package/dist/sdk/workflow-check-provider-3IWBAZP7.mjs +0 -28
- package/dist/sdk/workflow-registry-KFWSDSLM.mjs +0 -12
- package/dist/tui.d.ts +0 -51
- package/dist/tui.d.ts.map +0 -1
- /package/dist/sdk/{check-provider-registry-CVUONJ5A.mjs.map → check-provider-registry-ACRGIYOB.mjs.map} +0 -0
- /package/dist/sdk/{chunk-WMJKH4XE.mjs.map → check-provider-registry-VYHKFHK2.mjs.map} +0 -0
- /package/dist/sdk/{chunk-CNX7V5JK.mjs.map → chunk-25IC7KXZ.mjs.map} +0 -0
- /package/dist/sdk/{chunk-IHZOSIF4.mjs.map → chunk-2KB35MB7.mjs.map} +0 -0
- /package/dist/sdk/{chunk-HQL734ZI.mjs.map → chunk-6W75IMDC.mjs.map} +0 -0
- /package/dist/sdk/{chunk-3OMWVM6J.mjs.map → chunk-B7BVQM5K.mjs.map} +0 -0
- /package/dist/sdk/{chunk-VW2GBXQT.mjs.map → chunk-D5KI4YQ4.mjs.map} +0 -0
- /package/dist/sdk/{command-executor-Q7MHJKZJ.mjs.map → chunk-J7LXIPZS.mjs.map} +0 -0
- /package/dist/sdk/{chunk-EXFGO4FX.mjs.map → chunk-KFKHU6CM.mjs.map} +0 -0
- /package/dist/sdk/{chunk-MPS4HVQI.mjs.map → chunk-N7HO6KKC.mjs.map} +0 -0
- /package/dist/sdk/{chunk-O5EZDNYL.mjs.map → chunk-NCWIZVOT.mjs.map} +0 -0
- /package/dist/sdk/{chunk-3NMLT3YS.mjs.map → chunk-PO7X5XI7.mjs.map} +0 -0
- /package/dist/sdk/{chunk-BHOKBQPB.mjs.map → chunk-R5Z7YWPB.mjs.map} +0 -0
- /package/dist/sdk/{chunk-EORMDOZU.mjs.map → chunk-SGS2VMEL.mjs.map} +0 -0
- /package/dist/sdk/{chunk-J2QWVDXK.mjs.map → chunk-XDLQ3UNF.mjs.map} +0 -0
- /package/dist/sdk/{chunk-S2RUE2RG.mjs.map → chunk-XR7XXGL7.mjs.map} +0 -0
- /package/dist/sdk/{chunk-NAW3DB3I.mjs.map → chunk-XXAEN5KU.mjs.map} +0 -0
- /package/dist/sdk/{config-DXX64GD3.mjs.map → command-executor-DVVXERLR.mjs.map} +0 -0
- /package/dist/sdk/{config-merger-PX3WIT57.mjs.map → config-7VTT64SQ.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-G4HMJPXF.mjs.map → config-merger-RKCZJQ44.mjs.map} +0 -0
- /package/dist/sdk/{liquid-extensions-I7O7KMHF.mjs.map → failure-condition-evaluator-4WMDF4Q3.mjs.map} +0 -0
- /package/dist/sdk/{memory-store-RW5N2NGJ.mjs.map → liquid-extensions-5IZLTFSZ.mjs.map} +0 -0
- /package/dist/sdk/{metrics-7PP3EJUH.mjs.map → memory-store-3N4AZCYB.mjs.map} +0 -0
- /package/dist/sdk/{prompt-state-EZYOUG75.mjs.map → metrics-GXQ2EDXA.mjs.map} +0 -0
- /package/dist/sdk/{routing-QHTGDIXF.mjs.map → prompt-state-YHGXB2OA.mjs.map} +0 -0
- /package/dist/sdk/{renderer-schema-CKFB5NDB.mjs.map → renderer-schema-CMXOLNIG.mjs.map} +0 -0
- /package/dist/sdk/{session-registry-4E6YRQ77.mjs.map → routing-S3Y7T2X3.mjs.map} +0 -0
- /package/dist/sdk/{trace-helpers-VP6QYVBX.mjs.map → session-registry-6PV6SGEJ.mjs.map} +0 -0
- /package/dist/sdk/{tracer-init-GSLPPLCD.mjs.map → trace-helpers-YHNPC7MR.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-3IWBAZP7.mjs.map → tracer-init-XPRWKMZT.mjs.map} +0 -0
- /package/dist/sdk/{workflow-registry-KFWSDSLM.mjs.map → workflow-check-provider-4F3432ZP.mjs.map} +0 -0
|
@@ -1,26 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractTextFromJson,
|
|
3
|
+
init_json_text_extractor
|
|
4
|
+
} from "./chunk-H5BOW5CR.mjs";
|
|
1
5
|
import {
|
|
2
6
|
generateShortHumanId,
|
|
3
7
|
init_human_id
|
|
4
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-KFKHU6CM.mjs";
|
|
5
9
|
import {
|
|
6
10
|
failure_condition_evaluator_exports,
|
|
7
11
|
init_failure_condition_evaluator
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-N7HO6KKC.mjs";
|
|
13
|
+
import "./chunk-J5RGJQ53.mjs";
|
|
14
|
+
import "./chunk-XR7XXGL7.mjs";
|
|
15
|
+
import "./chunk-25IC7KXZ.mjs";
|
|
16
|
+
import "./chunk-VF6XIUE4.mjs";
|
|
17
|
+
import "./chunk-2KB35MB7.mjs";
|
|
14
18
|
import {
|
|
15
19
|
init_logger,
|
|
16
20
|
logger
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-
|
|
21
|
+
} from "./chunk-PO7X5XI7.mjs";
|
|
22
|
+
import "./chunk-HEX3RL32.mjs";
|
|
19
23
|
import {
|
|
20
24
|
__esm,
|
|
21
25
|
__export,
|
|
22
26
|
__toCommonJS
|
|
23
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-J7LXIPZS.mjs";
|
|
24
28
|
|
|
25
29
|
// src/footer.ts
|
|
26
30
|
function generateFooter(options = {}) {
|
|
@@ -804,621 +808,549 @@ ${content}
|
|
|
804
808
|
});
|
|
805
809
|
|
|
806
810
|
// src/frontends/github-frontend.ts
|
|
807
|
-
|
|
811
|
+
var GitHubFrontend;
|
|
812
|
+
var init_github_frontend = __esm({
|
|
813
|
+
"src/frontends/github-frontend.ts"() {
|
|
814
|
+
init_logger();
|
|
815
|
+
init_json_text_extractor();
|
|
816
|
+
GitHubFrontend = class {
|
|
817
|
+
name = "github";
|
|
818
|
+
subs = [];
|
|
819
|
+
checkRunIds = /* @__PURE__ */ new Map();
|
|
820
|
+
revision = 0;
|
|
821
|
+
cachedCommentId;
|
|
822
|
+
// legacy single-thread id (kept for compatibility)
|
|
823
|
+
// Group → (checkId → SectionState)
|
|
824
|
+
stepStatusByGroup = /* @__PURE__ */ new Map();
|
|
825
|
+
// Debounce/coalescing state
|
|
826
|
+
debounceMs = 400;
|
|
827
|
+
maxWaitMs = 2e3;
|
|
828
|
+
_timer = null;
|
|
829
|
+
_lastFlush = 0;
|
|
830
|
+
_pendingIds = /* @__PURE__ */ new Set();
|
|
831
|
+
// Mutex for serializing comment updates per group
|
|
832
|
+
updateLocks = /* @__PURE__ */ new Map();
|
|
833
|
+
minUpdateDelayMs = 1e3;
|
|
834
|
+
// Minimum delay between updates (public for testing)
|
|
835
|
+
// Cache of created GitHub comment IDs per group to handle API eventual consistency
|
|
836
|
+
createdCommentGithubIds = /* @__PURE__ */ new Map();
|
|
837
|
+
start(ctx) {
|
|
838
|
+
const log = ctx.logger;
|
|
839
|
+
const bus = ctx.eventBus;
|
|
840
|
+
const octokit = ctx.octokit;
|
|
841
|
+
const repo = ctx.run.repo;
|
|
842
|
+
const pr = ctx.run.pr;
|
|
843
|
+
const headSha = ctx.run.headSha;
|
|
844
|
+
const canPostComments = !!(octokit && repo && pr);
|
|
845
|
+
const canPostChecks = !!(octokit && repo && pr && headSha);
|
|
846
|
+
const svc = canPostChecks ? new (init_github_check_service(), __toCommonJS(github_check_service_exports)).GitHubCheckService(octokit) : null;
|
|
847
|
+
const CommentManager2 = (init_github_comments(), __toCommonJS(github_comments_exports)).CommentManager;
|
|
848
|
+
const comments = canPostComments ? new CommentManager2(octokit) : null;
|
|
849
|
+
const threadKey = repo && pr && headSha ? `${repo.owner}/${repo.name}#${pr}@${(headSha || "").substring(0, 7)}` : ctx.run.runId;
|
|
850
|
+
this.cachedCommentId = `visor-thread-${threadKey}`;
|
|
851
|
+
this.subs.push(
|
|
852
|
+
bus.on("CheckScheduled", async (env) => {
|
|
853
|
+
const ev = env && env.payload || env;
|
|
854
|
+
try {
|
|
855
|
+
if (!canPostChecks || !svc) return;
|
|
856
|
+
if (this.checkRunIds.has(ev.checkId)) return;
|
|
857
|
+
const group = this.getGroupForCheck(ctx, ev.checkId);
|
|
858
|
+
this.upsertSectionState(group, ev.checkId, {
|
|
859
|
+
status: "queued",
|
|
860
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
861
|
+
});
|
|
862
|
+
const res = await svc.createCheckRun(
|
|
863
|
+
{
|
|
864
|
+
owner: repo.owner,
|
|
865
|
+
repo: repo.name,
|
|
866
|
+
head_sha: headSha,
|
|
867
|
+
name: `Visor: ${ev.checkId}`,
|
|
868
|
+
external_id: `visor:${ctx.run.runId}:${ev.checkId}`,
|
|
869
|
+
engine_mode: "state-machine"
|
|
870
|
+
},
|
|
871
|
+
{ title: `${ev.checkId}`, summary: "Queued" }
|
|
872
|
+
);
|
|
873
|
+
this.checkRunIds.set(ev.checkId, res.id);
|
|
874
|
+
} catch (e) {
|
|
875
|
+
log.warn(
|
|
876
|
+
`[github-frontend] createCheckRun failed for ${ev.checkId}: ${e instanceof Error ? e.message : e}`
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
})
|
|
880
|
+
);
|
|
881
|
+
this.subs.push(
|
|
882
|
+
bus.on("CheckCompleted", async (env) => {
|
|
883
|
+
const ev = env && env.payload || env;
|
|
884
|
+
try {
|
|
885
|
+
if (canPostChecks && svc && this.checkRunIds.has(ev.checkId)) {
|
|
886
|
+
const id = this.checkRunIds.get(ev.checkId);
|
|
887
|
+
const issues = Array.isArray(ev.result?.issues) ? ev.result.issues : [];
|
|
888
|
+
const failureResults = await this.evaluateFailureResults(ctx, ev.checkId, ev.result);
|
|
889
|
+
await svc.completeCheckRun(
|
|
890
|
+
repo.owner,
|
|
891
|
+
repo.name,
|
|
892
|
+
id,
|
|
893
|
+
ev.checkId,
|
|
894
|
+
failureResults,
|
|
895
|
+
issues,
|
|
896
|
+
void 0,
|
|
897
|
+
void 0,
|
|
898
|
+
pr,
|
|
899
|
+
headSha
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
if (canPostComments && comments) {
|
|
903
|
+
const count = Array.isArray(ev.result?.issues) ? ev.result.issues.length : 0;
|
|
904
|
+
const failureResults = await this.evaluateFailureResults(ctx, ev.checkId, ev.result);
|
|
905
|
+
const failed = Array.isArray(failureResults) ? failureResults.some((r) => r && r.failed) : false;
|
|
906
|
+
const group = this.getGroupForCheck(ctx, ev.checkId);
|
|
907
|
+
const rawContent = ev?.result?.content;
|
|
908
|
+
const extractedContent = extractTextFromJson(rawContent);
|
|
909
|
+
this.upsertSectionState(group, ev.checkId, {
|
|
910
|
+
status: "completed",
|
|
911
|
+
conclusion: failed ? "failure" : "success",
|
|
912
|
+
issues: count,
|
|
913
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
914
|
+
content: extractedContent
|
|
915
|
+
});
|
|
916
|
+
await this.updateGroupedComment(ctx, comments, group, ev.checkId);
|
|
917
|
+
}
|
|
918
|
+
} catch (e) {
|
|
919
|
+
log.warn(
|
|
920
|
+
`[github-frontend] handle CheckCompleted failed: ${e instanceof Error ? e.message : e}`
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
})
|
|
924
|
+
);
|
|
925
|
+
this.subs.push(
|
|
926
|
+
bus.on("CheckErrored", async (env) => {
|
|
927
|
+
const ev = env && env.payload || env;
|
|
928
|
+
try {
|
|
929
|
+
if (canPostChecks && svc && this.checkRunIds.has(ev.checkId)) {
|
|
930
|
+
const id = this.checkRunIds.get(ev.checkId);
|
|
931
|
+
await svc.completeCheckRun(
|
|
932
|
+
repo.owner,
|
|
933
|
+
repo.name,
|
|
934
|
+
id,
|
|
935
|
+
ev.checkId,
|
|
936
|
+
[],
|
|
937
|
+
[],
|
|
938
|
+
ev.error?.message || "Execution error",
|
|
939
|
+
void 0,
|
|
940
|
+
pr,
|
|
941
|
+
headSha
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
if (canPostComments && comments) {
|
|
945
|
+
const group = this.getGroupForCheck(ctx, ev.checkId);
|
|
946
|
+
this.upsertSectionState(group, ev.checkId, {
|
|
947
|
+
status: "errored",
|
|
948
|
+
conclusion: "failure",
|
|
949
|
+
issues: 0,
|
|
950
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
951
|
+
error: ev.error?.message || "Execution error"
|
|
952
|
+
});
|
|
953
|
+
await this.updateGroupedComment(ctx, comments, group, ev.checkId);
|
|
954
|
+
}
|
|
955
|
+
} catch (e) {
|
|
956
|
+
log.warn(
|
|
957
|
+
`[github-frontend] handle CheckErrored failed: ${e instanceof Error ? e.message : e}`
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
})
|
|
961
|
+
);
|
|
962
|
+
this.subs.push(
|
|
963
|
+
bus.on("StateTransition", async (env) => {
|
|
964
|
+
const ev = env && env.payload || env;
|
|
965
|
+
try {
|
|
966
|
+
if (ev.to === "Completed" || ev.to === "Error") {
|
|
967
|
+
if (canPostComments && comments) {
|
|
968
|
+
for (const group of this.stepStatusByGroup.keys()) {
|
|
969
|
+
await this.updateGroupedComment(ctx, comments, group);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
} catch (e) {
|
|
974
|
+
log.warn(
|
|
975
|
+
`[github-frontend] handle StateTransition failed: ${e instanceof Error ? e.message : e}`
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
})
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
stop() {
|
|
982
|
+
for (const s of this.subs) s.unsubscribe();
|
|
983
|
+
this.subs = [];
|
|
984
|
+
}
|
|
985
|
+
async buildFullBody(ctx, group) {
|
|
986
|
+
const header = this.renderThreadHeader(ctx, group);
|
|
987
|
+
const sections = this.renderSections(ctx, group);
|
|
988
|
+
return `${header}
|
|
808
989
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
/^\s*\{\s*"text"\s*:\s*"/i,
|
|
813
|
-
/^\s*\{\s*"response"\s*:\s*"/i,
|
|
814
|
-
/^\s*\{\s*"message"\s*:\s*"/i
|
|
815
|
-
];
|
|
816
|
-
for (const pattern of fieldPatterns) {
|
|
817
|
-
const match = pattern.exec(content);
|
|
818
|
-
if (match) {
|
|
819
|
-
const valueStart = match[0].length;
|
|
820
|
-
const remaining = content.substring(valueStart);
|
|
821
|
-
let value = "";
|
|
822
|
-
let i = 0;
|
|
823
|
-
while (i < remaining.length) {
|
|
824
|
-
const char = remaining[i];
|
|
825
|
-
if (char === "\\" && i + 1 < remaining.length) {
|
|
826
|
-
const nextChar = remaining[i + 1];
|
|
827
|
-
if (nextChar === "n") {
|
|
828
|
-
value += "\n";
|
|
829
|
-
} else if (nextChar === "r") {
|
|
830
|
-
value += "\r";
|
|
831
|
-
} else if (nextChar === "t") {
|
|
832
|
-
value += " ";
|
|
833
|
-
} else if (nextChar === '"') {
|
|
834
|
-
value += '"';
|
|
835
|
-
} else if (nextChar === "\\") {
|
|
836
|
-
value += "\\";
|
|
837
|
-
} else {
|
|
838
|
-
value += char + nextChar;
|
|
839
|
-
}
|
|
840
|
-
i += 2;
|
|
841
|
-
} else if (char === '"') {
|
|
842
|
-
break;
|
|
843
|
-
} else {
|
|
844
|
-
value += char;
|
|
845
|
-
i++;
|
|
846
|
-
}
|
|
990
|
+
${sections}
|
|
991
|
+
|
|
992
|
+
<!-- visor:thread-end key="${this.threadKeyFor(ctx)}" -->`;
|
|
847
993
|
}
|
|
848
|
-
|
|
849
|
-
|
|
994
|
+
threadKeyFor(ctx) {
|
|
995
|
+
const r = ctx.run;
|
|
996
|
+
return r.repo && r.pr && r.headSha ? `${r.repo.owner}/${r.repo.name}#${r.pr}@${(r.headSha || "").substring(0, 7)}` : r.runId;
|
|
850
997
|
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
862
|
-
}
|
|
863
|
-
try {
|
|
864
|
-
parsed = JSON.parse(trimmed);
|
|
865
|
-
} catch {
|
|
866
|
-
const extracted = extractTextFieldFromMalformedJson(trimmed);
|
|
867
|
-
if (extracted) {
|
|
868
|
-
return extracted;
|
|
998
|
+
renderThreadHeader(ctx, group) {
|
|
999
|
+
const header = {
|
|
1000
|
+
key: this.threadKeyFor(ctx),
|
|
1001
|
+
runId: ctx.run.runId,
|
|
1002
|
+
workflowId: ctx.run.workflowId,
|
|
1003
|
+
revision: this.revision,
|
|
1004
|
+
group,
|
|
1005
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1006
|
+
};
|
|
1007
|
+
return `<!-- visor:thread=${JSON.stringify(header)} -->`;
|
|
869
1008
|
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
const trimmed = content.trim();
|
|
881
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
882
|
-
}
|
|
883
|
-
return void 0;
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
// src/frontends/github-frontend.ts
|
|
887
|
-
var GitHubFrontend = class {
|
|
888
|
-
name = "github";
|
|
889
|
-
subs = [];
|
|
890
|
-
checkRunIds = /* @__PURE__ */ new Map();
|
|
891
|
-
revision = 0;
|
|
892
|
-
cachedCommentId;
|
|
893
|
-
// legacy single-thread id (kept for compatibility)
|
|
894
|
-
// Group → (checkId → SectionState)
|
|
895
|
-
stepStatusByGroup = /* @__PURE__ */ new Map();
|
|
896
|
-
// Debounce/coalescing state
|
|
897
|
-
debounceMs = 400;
|
|
898
|
-
maxWaitMs = 2e3;
|
|
899
|
-
_timer = null;
|
|
900
|
-
_lastFlush = 0;
|
|
901
|
-
_pendingIds = /* @__PURE__ */ new Set();
|
|
902
|
-
// Mutex for serializing comment updates per group
|
|
903
|
-
updateLocks = /* @__PURE__ */ new Map();
|
|
904
|
-
minUpdateDelayMs = 1e3;
|
|
905
|
-
// Minimum delay between updates (public for testing)
|
|
906
|
-
// Cache of created GitHub comment IDs per group to handle API eventual consistency
|
|
907
|
-
createdCommentGithubIds = /* @__PURE__ */ new Map();
|
|
908
|
-
start(ctx) {
|
|
909
|
-
const log = ctx.logger;
|
|
910
|
-
const bus = ctx.eventBus;
|
|
911
|
-
const octokit = ctx.octokit;
|
|
912
|
-
const repo = ctx.run.repo;
|
|
913
|
-
const pr = ctx.run.pr;
|
|
914
|
-
const headSha = ctx.run.headSha;
|
|
915
|
-
const canPostComments = !!(octokit && repo && pr);
|
|
916
|
-
const canPostChecks = !!(octokit && repo && pr && headSha);
|
|
917
|
-
const svc = canPostChecks ? new (init_github_check_service(), __toCommonJS(github_check_service_exports)).GitHubCheckService(octokit) : null;
|
|
918
|
-
const CommentManager2 = (init_github_comments(), __toCommonJS(github_comments_exports)).CommentManager;
|
|
919
|
-
const comments = canPostComments ? new CommentManager2(octokit) : null;
|
|
920
|
-
const threadKey = repo && pr && headSha ? `${repo.owner}/${repo.name}#${pr}@${(headSha || "").substring(0, 7)}` : ctx.run.runId;
|
|
921
|
-
this.cachedCommentId = `visor-thread-${threadKey}`;
|
|
922
|
-
this.subs.push(
|
|
923
|
-
bus.on("CheckScheduled", async (env) => {
|
|
924
|
-
const ev = env && env.payload || env;
|
|
925
|
-
try {
|
|
926
|
-
if (!canPostChecks || !svc) return;
|
|
927
|
-
if (this.checkRunIds.has(ev.checkId)) return;
|
|
928
|
-
const group = this.getGroupForCheck(ctx, ev.checkId);
|
|
929
|
-
this.upsertSectionState(group, ev.checkId, {
|
|
930
|
-
status: "queued",
|
|
931
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
932
|
-
});
|
|
933
|
-
const res = await svc.createCheckRun(
|
|
934
|
-
{
|
|
935
|
-
owner: repo.owner,
|
|
936
|
-
repo: repo.name,
|
|
937
|
-
head_sha: headSha,
|
|
938
|
-
name: `Visor: ${ev.checkId}`,
|
|
939
|
-
external_id: `visor:${ctx.run.runId}:${ev.checkId}`,
|
|
940
|
-
engine_mode: "state-machine"
|
|
941
|
-
},
|
|
942
|
-
{ title: `${ev.checkId}`, summary: "Queued" }
|
|
943
|
-
);
|
|
944
|
-
this.checkRunIds.set(ev.checkId, res.id);
|
|
945
|
-
} catch (e) {
|
|
946
|
-
log.warn(
|
|
947
|
-
`[github-frontend] createCheckRun failed for ${ev.checkId}: ${e instanceof Error ? e.message : e}`
|
|
948
|
-
);
|
|
1009
|
+
renderSections(ctx, group) {
|
|
1010
|
+
const lines = [];
|
|
1011
|
+
const groupMap = this.stepStatusByGroup.get(group) || /* @__PURE__ */ new Map();
|
|
1012
|
+
for (const [checkId, st] of groupMap.entries()) {
|
|
1013
|
+
const start = `<!-- visor:section=${JSON.stringify({ id: checkId, revision: this.revision })} -->`;
|
|
1014
|
+
const end = `<!-- visor:section-end id="${checkId}" -->`;
|
|
1015
|
+
const body = st.content && st.content.toString().trim().length > 0 ? st.content.toString().trim() : "";
|
|
1016
|
+
lines.push(`${start}
|
|
1017
|
+
${body}
|
|
1018
|
+
${end}`);
|
|
949
1019
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1020
|
+
return lines.join("\\n\\n");
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Acquires a mutex lock for the given group and executes the update.
|
|
1024
|
+
* This ensures only one comment update happens at a time per group,
|
|
1025
|
+
* preventing race conditions where updates overwrite each other.
|
|
1026
|
+
*
|
|
1027
|
+
* Uses a proper queue-based mutex: each new caller chains onto the previous
|
|
1028
|
+
* lock, ensuring strict serialization even when multiple callers wait
|
|
1029
|
+
* simultaneously.
|
|
1030
|
+
*/
|
|
1031
|
+
async updateGroupedComment(ctx, comments, group, changedIds) {
|
|
1032
|
+
const existingLock = this.updateLocks.get(group);
|
|
1033
|
+
let resolveLock;
|
|
1034
|
+
const ourLock = new Promise((resolve) => {
|
|
1035
|
+
resolveLock = resolve;
|
|
1036
|
+
});
|
|
1037
|
+
this.updateLocks.set(group, ourLock);
|
|
955
1038
|
try {
|
|
956
|
-
if (
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
ev.checkId,
|
|
965
|
-
failureResults,
|
|
966
|
-
issues,
|
|
967
|
-
void 0,
|
|
968
|
-
void 0,
|
|
969
|
-
pr,
|
|
970
|
-
headSha
|
|
971
|
-
);
|
|
1039
|
+
if (existingLock) {
|
|
1040
|
+
try {
|
|
1041
|
+
await existingLock;
|
|
1042
|
+
} catch (error) {
|
|
1043
|
+
logger.warn(
|
|
1044
|
+
`[github-frontend] Previous update for group ${group} failed: ${error instanceof Error ? error.message : error}`
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
972
1047
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
const group = this.getGroupForCheck(ctx, ev.checkId);
|
|
978
|
-
const rawContent = ev?.result?.content;
|
|
979
|
-
const extractedContent = extractTextFromJson(rawContent);
|
|
980
|
-
this.upsertSectionState(group, ev.checkId, {
|
|
981
|
-
status: "completed",
|
|
982
|
-
conclusion: failed ? "failure" : "success",
|
|
983
|
-
issues: count,
|
|
984
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
985
|
-
content: extractedContent
|
|
986
|
-
});
|
|
987
|
-
await this.updateGroupedComment(ctx, comments, group, ev.checkId);
|
|
1048
|
+
await this.performGroupedCommentUpdate(ctx, comments, group, changedIds);
|
|
1049
|
+
} finally {
|
|
1050
|
+
if (this.updateLocks.get(group) === ourLock) {
|
|
1051
|
+
this.updateLocks.delete(group);
|
|
988
1052
|
}
|
|
989
|
-
|
|
990
|
-
log.warn(
|
|
991
|
-
`[github-frontend] handle CheckCompleted failed: ${e instanceof Error ? e.message : e}`
|
|
992
|
-
);
|
|
1053
|
+
resolveLock();
|
|
993
1054
|
}
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Performs the actual comment update with delay enforcement.
|
|
1058
|
+
*/
|
|
1059
|
+
async performGroupedCommentUpdate(ctx, comments, group, changedIds) {
|
|
999
1060
|
try {
|
|
1000
|
-
if (
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
ev.checkId,
|
|
1007
|
-
[],
|
|
1008
|
-
[],
|
|
1009
|
-
ev.error?.message || "Execution error",
|
|
1010
|
-
void 0,
|
|
1011
|
-
pr,
|
|
1012
|
-
headSha
|
|
1061
|
+
if (!ctx.run.repo || !ctx.run.pr) return;
|
|
1062
|
+
const config = ctx.config;
|
|
1063
|
+
const prCommentEnabled = config?.output?.pr_comment?.enabled !== false;
|
|
1064
|
+
if (!prCommentEnabled) {
|
|
1065
|
+
logger.debug(
|
|
1066
|
+
`[github-frontend] PR comments disabled in config, skipping comment for group: ${group}`
|
|
1013
1067
|
);
|
|
1068
|
+
return;
|
|
1014
1069
|
}
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
this.
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
error: ev.error?.message || "Execution error"
|
|
1023
|
-
});
|
|
1024
|
-
await this.updateGroupedComment(ctx, comments, group, ev.checkId);
|
|
1070
|
+
const timeSinceLastFlush = Date.now() - this._lastFlush;
|
|
1071
|
+
if (this._lastFlush > 0 && timeSinceLastFlush < this.minUpdateDelayMs) {
|
|
1072
|
+
const delay = this.minUpdateDelayMs - timeSinceLastFlush;
|
|
1073
|
+
logger.debug(
|
|
1074
|
+
`[github-frontend] Waiting ${delay}ms before next update to prevent rate limiting`
|
|
1075
|
+
);
|
|
1076
|
+
await this.sleep(delay);
|
|
1025
1077
|
}
|
|
1078
|
+
this.revision++;
|
|
1079
|
+
const commentId = this.commentIdForGroup(ctx, group);
|
|
1080
|
+
const mergedBody = await this.mergeIntoExistingBody(ctx, comments, group, changedIds);
|
|
1081
|
+
const cachedGithubId = this.createdCommentGithubIds.get(commentId);
|
|
1082
|
+
const result = await comments.updateOrCreateComment(
|
|
1083
|
+
ctx.run.repo.owner,
|
|
1084
|
+
ctx.run.repo.name,
|
|
1085
|
+
ctx.run.pr,
|
|
1086
|
+
mergedBody,
|
|
1087
|
+
{
|
|
1088
|
+
commentId,
|
|
1089
|
+
triggeredBy: this.deriveTriggeredBy(ctx),
|
|
1090
|
+
commitSha: ctx.run.headSha,
|
|
1091
|
+
// Pass the cached GitHub comment ID if available
|
|
1092
|
+
cachedGithubCommentId: cachedGithubId
|
|
1093
|
+
}
|
|
1094
|
+
);
|
|
1095
|
+
if (result && result.id) {
|
|
1096
|
+
this.createdCommentGithubIds.set(commentId, result.id);
|
|
1097
|
+
}
|
|
1098
|
+
this._lastFlush = Date.now();
|
|
1026
1099
|
} catch (e) {
|
|
1027
|
-
|
|
1028
|
-
`[github-frontend]
|
|
1100
|
+
logger.debug(
|
|
1101
|
+
`[github-frontend] updateGroupedComment failed: ${e instanceof Error ? e.message : e}`
|
|
1029
1102
|
);
|
|
1030
1103
|
}
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1104
|
+
}
|
|
1105
|
+
deriveTriggeredBy(ctx) {
|
|
1106
|
+
const ev = ctx.run.event || "";
|
|
1107
|
+
const actor = ctx.run.actor;
|
|
1108
|
+
const commentEvents = /* @__PURE__ */ new Set([
|
|
1109
|
+
"issue_comment",
|
|
1110
|
+
"issue_comment_created",
|
|
1111
|
+
"pr_comment",
|
|
1112
|
+
"comment",
|
|
1113
|
+
"pull_request_review_comment"
|
|
1114
|
+
]);
|
|
1115
|
+
if (commentEvents.has(ev) && actor) return actor;
|
|
1116
|
+
if (ev) return ev;
|
|
1117
|
+
return actor || "unknown";
|
|
1118
|
+
}
|
|
1119
|
+
async mergeIntoExistingBody(ctx, comments, group, changedIds) {
|
|
1120
|
+
const repo = ctx.run.repo;
|
|
1121
|
+
const pr = ctx.run.pr;
|
|
1122
|
+
const existing = await comments.findVisorComment(
|
|
1123
|
+
repo.owner,
|
|
1124
|
+
repo.name,
|
|
1125
|
+
pr,
|
|
1126
|
+
this.commentIdForGroup(ctx, group)
|
|
1127
|
+
);
|
|
1128
|
+
if (!existing || !existing.body) return this.buildFullBody(ctx, group);
|
|
1129
|
+
const body = String(existing.body);
|
|
1130
|
+
const doc = this.parseSections(body);
|
|
1131
|
+
doc.header = {
|
|
1132
|
+
...doc.header || {},
|
|
1133
|
+
key: this.threadKeyFor(ctx),
|
|
1134
|
+
revision: this.revision,
|
|
1135
|
+
group
|
|
1136
|
+
};
|
|
1137
|
+
if (changedIds) {
|
|
1138
|
+
const ids = Array.isArray(changedIds) ? changedIds : [changedIds];
|
|
1139
|
+
const fresh = this.renderSections(ctx, group);
|
|
1140
|
+
for (const id of ids) {
|
|
1141
|
+
const block = this.extractSectionById(fresh, id);
|
|
1142
|
+
if (block) doc.sections.set(id, block);
|
|
1143
|
+
}
|
|
1144
|
+
} else {
|
|
1145
|
+
const fresh = this.renderSections(ctx, group);
|
|
1146
|
+
const map = this.stepStatusByGroup.get(group) || /* @__PURE__ */ new Map();
|
|
1147
|
+
for (const [checkId] of map.entries()) {
|
|
1148
|
+
if (!doc.sections.has(checkId)) {
|
|
1149
|
+
const block = this.extractSectionById(fresh, checkId);
|
|
1150
|
+
if (block) doc.sections.set(checkId, block);
|
|
1042
1151
|
}
|
|
1043
1152
|
}
|
|
1044
|
-
} catch (e) {
|
|
1045
|
-
log.warn(
|
|
1046
|
-
`[github-frontend] handle StateTransition failed: ${e instanceof Error ? e.message : e}`
|
|
1047
|
-
);
|
|
1048
1153
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1077
|
-
};
|
|
1078
|
-
return `<!-- visor:thread=${JSON.stringify(header)} -->`;
|
|
1079
|
-
}
|
|
1080
|
-
renderSections(ctx, group) {
|
|
1081
|
-
const lines = [];
|
|
1082
|
-
const groupMap = this.stepStatusByGroup.get(group) || /* @__PURE__ */ new Map();
|
|
1083
|
-
for (const [checkId, st] of groupMap.entries()) {
|
|
1084
|
-
const start = `<!-- visor:section=${JSON.stringify({ id: checkId, revision: this.revision })} -->`;
|
|
1085
|
-
const end = `<!-- visor:section-end id="${checkId}" -->`;
|
|
1086
|
-
const body = st.content && st.content.toString().trim().length > 0 ? st.content.toString().trim() : "";
|
|
1087
|
-
lines.push(`${start}
|
|
1088
|
-
${body}
|
|
1089
|
-
${end}`);
|
|
1090
|
-
}
|
|
1091
|
-
return lines.join("\\n\\n");
|
|
1092
|
-
}
|
|
1093
|
-
/**
|
|
1094
|
-
* Acquires a mutex lock for the given group and executes the update.
|
|
1095
|
-
* This ensures only one comment update happens at a time per group,
|
|
1096
|
-
* preventing race conditions where updates overwrite each other.
|
|
1097
|
-
*
|
|
1098
|
-
* Uses a proper queue-based mutex: each new caller chains onto the previous
|
|
1099
|
-
* lock, ensuring strict serialization even when multiple callers wait
|
|
1100
|
-
* simultaneously.
|
|
1101
|
-
*/
|
|
1102
|
-
async updateGroupedComment(ctx, comments, group, changedIds) {
|
|
1103
|
-
const existingLock = this.updateLocks.get(group);
|
|
1104
|
-
let resolveLock;
|
|
1105
|
-
const ourLock = new Promise((resolve) => {
|
|
1106
|
-
resolveLock = resolve;
|
|
1107
|
-
});
|
|
1108
|
-
this.updateLocks.set(group, ourLock);
|
|
1109
|
-
try {
|
|
1110
|
-
if (existingLock) {
|
|
1154
|
+
return this.serializeSections(doc);
|
|
1155
|
+
}
|
|
1156
|
+
parseSections(body) {
|
|
1157
|
+
const sections = /* @__PURE__ */ new Map();
|
|
1158
|
+
const headerRe = /<!--\s*visor:thread=(\{[\s\S]*?\})\s*-->/m;
|
|
1159
|
+
const startRe = /<!--\s*visor:section=(\{[\s\S]*?\})\s*-->/g;
|
|
1160
|
+
const endRe = /<!--\s*visor:section-end\s+id=\"([^\"]+)\"\s*-->/g;
|
|
1161
|
+
const safePick = (obj, allowed) => {
|
|
1162
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj)) return void 0;
|
|
1163
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
1164
|
+
for (const [k, t] of Object.entries(allowed)) {
|
|
1165
|
+
if (Object.prototype.hasOwnProperty.call(obj, k)) {
|
|
1166
|
+
const v = obj[k];
|
|
1167
|
+
if (t === "string" && typeof v === "string") out[k] = v;
|
|
1168
|
+
else if (t === "number" && typeof v === "number" && Number.isFinite(v)) out[k] = v;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
return out;
|
|
1172
|
+
};
|
|
1173
|
+
const safeParse = (text) => {
|
|
1174
|
+
try {
|
|
1175
|
+
return JSON.parse(text);
|
|
1176
|
+
} catch {
|
|
1177
|
+
return void 0;
|
|
1178
|
+
}
|
|
1179
|
+
};
|
|
1180
|
+
let header;
|
|
1111
1181
|
try {
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1182
|
+
const h = headerRe.exec(body);
|
|
1183
|
+
if (h) {
|
|
1184
|
+
const parsed = safeParse(h[1]);
|
|
1185
|
+
const picked = safePick(parsed, {
|
|
1186
|
+
key: "string",
|
|
1187
|
+
runId: "string",
|
|
1188
|
+
workflowId: "string",
|
|
1189
|
+
revision: "number",
|
|
1190
|
+
group: "string",
|
|
1191
|
+
generatedAt: "string"
|
|
1192
|
+
});
|
|
1193
|
+
header = picked;
|
|
1194
|
+
}
|
|
1195
|
+
} catch {
|
|
1117
1196
|
}
|
|
1197
|
+
let cursor = 0;
|
|
1198
|
+
while (true) {
|
|
1199
|
+
const s = startRe.exec(body);
|
|
1200
|
+
if (!s) break;
|
|
1201
|
+
const metaRaw = safeParse(s[1]);
|
|
1202
|
+
const meta = safePick(metaRaw, { id: "string", revision: "number" }) || { id: "" };
|
|
1203
|
+
const startIdx = startRe.lastIndex;
|
|
1204
|
+
endRe.lastIndex = startIdx;
|
|
1205
|
+
const e = endRe.exec(body);
|
|
1206
|
+
if (!e) break;
|
|
1207
|
+
const id = typeof meta.id === "string" && meta.id ? String(meta.id) : String(e[1]);
|
|
1208
|
+
const content = body.substring(startIdx, e.index).trim();
|
|
1209
|
+
const block = `<!-- visor:section=${JSON.stringify(meta)} -->
|
|
1210
|
+
${content}
|
|
1211
|
+
<!-- visor:section-end id="${id}" -->`;
|
|
1212
|
+
sections.set(id, block);
|
|
1213
|
+
cursor = endRe.lastIndex;
|
|
1214
|
+
startRe.lastIndex = cursor;
|
|
1215
|
+
}
|
|
1216
|
+
return { header, sections };
|
|
1118
1217
|
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1218
|
+
serializeSections(doc) {
|
|
1219
|
+
const header = `<!-- visor:thread=${JSON.stringify({ ...doc.header || {}, generatedAt: (/* @__PURE__ */ new Date()).toISOString() })} -->`;
|
|
1220
|
+
const blocks = Array.from(doc.sections.values()).join("\n\n");
|
|
1221
|
+
const key = doc.header && doc.header.key || "";
|
|
1222
|
+
return `${header}
|
|
1223
|
+
|
|
1224
|
+
${blocks}
|
|
1225
|
+
|
|
1226
|
+
<!-- visor:thread-end key="${key}" -->`;
|
|
1123
1227
|
}
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
* Performs the actual comment update with delay enforcement.
|
|
1129
|
-
*/
|
|
1130
|
-
async performGroupedCommentUpdate(ctx, comments, group, changedIds) {
|
|
1131
|
-
try {
|
|
1132
|
-
if (!ctx.run.repo || !ctx.run.pr) return;
|
|
1133
|
-
const config = ctx.config;
|
|
1134
|
-
const prCommentEnabled = config?.output?.pr_comment?.enabled !== false;
|
|
1135
|
-
if (!prCommentEnabled) {
|
|
1136
|
-
logger.debug(
|
|
1137
|
-
`[github-frontend] PR comments disabled in config, skipping comment for group: ${group}`
|
|
1228
|
+
extractSectionById(rendered, id) {
|
|
1229
|
+
const rx = new RegExp(
|
|
1230
|
+
`<!--\\s*visor:section=(\\{[\\s\\S]*?\\})\\s*-->[\\s\\S]*?<!--\\s*visor:section-end\\s+id=\\"${this.escapeRegExp(id)}\\"\\s*-->`,
|
|
1231
|
+
"m"
|
|
1138
1232
|
);
|
|
1139
|
-
|
|
1233
|
+
const m = rx.exec(rendered);
|
|
1234
|
+
return m ? m[0] : void 0;
|
|
1140
1235
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
const delay = this.minUpdateDelayMs - timeSinceLastFlush;
|
|
1144
|
-
logger.debug(
|
|
1145
|
-
`[github-frontend] Waiting ${delay}ms before next update to prevent rate limiting`
|
|
1146
|
-
);
|
|
1147
|
-
await this.sleep(delay);
|
|
1236
|
+
escapeRegExp(s) {
|
|
1237
|
+
return s.replace(/[.*+?^${}()|[\\]\\]/g, "\\$&");
|
|
1148
1238
|
}
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
ctx.run.repo.name,
|
|
1156
|
-
ctx.run.pr,
|
|
1157
|
-
mergedBody,
|
|
1158
|
-
{
|
|
1159
|
-
commentId,
|
|
1160
|
-
triggeredBy: this.deriveTriggeredBy(ctx),
|
|
1161
|
-
commitSha: ctx.run.headSha,
|
|
1162
|
-
// Pass the cached GitHub comment ID if available
|
|
1163
|
-
cachedGithubCommentId: cachedGithubId
|
|
1239
|
+
getGroupForCheck(ctx, checkId) {
|
|
1240
|
+
try {
|
|
1241
|
+
const cfg = ctx.config || {};
|
|
1242
|
+
const g = cfg?.checks?.[checkId]?.group || cfg?.steps?.[checkId]?.group;
|
|
1243
|
+
if (typeof g === "string" && g.trim().length > 0) return g;
|
|
1244
|
+
} catch {
|
|
1164
1245
|
}
|
|
1165
|
-
|
|
1166
|
-
if (result && result.id) {
|
|
1167
|
-
this.createdCommentGithubIds.set(commentId, result.id);
|
|
1168
|
-
}
|
|
1169
|
-
this._lastFlush = Date.now();
|
|
1170
|
-
} catch (e) {
|
|
1171
|
-
logger.debug(
|
|
1172
|
-
`[github-frontend] updateGroupedComment failed: ${e instanceof Error ? e.message : e}`
|
|
1173
|
-
);
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
deriveTriggeredBy(ctx) {
|
|
1177
|
-
const ev = ctx.run.event || "";
|
|
1178
|
-
const actor = ctx.run.actor;
|
|
1179
|
-
const commentEvents = /* @__PURE__ */ new Set([
|
|
1180
|
-
"issue_comment",
|
|
1181
|
-
"issue_comment_created",
|
|
1182
|
-
"pr_comment",
|
|
1183
|
-
"comment",
|
|
1184
|
-
"pull_request_review_comment"
|
|
1185
|
-
]);
|
|
1186
|
-
if (commentEvents.has(ev) && actor) return actor;
|
|
1187
|
-
if (ev) return ev;
|
|
1188
|
-
return actor || "unknown";
|
|
1189
|
-
}
|
|
1190
|
-
async mergeIntoExistingBody(ctx, comments, group, changedIds) {
|
|
1191
|
-
const repo = ctx.run.repo;
|
|
1192
|
-
const pr = ctx.run.pr;
|
|
1193
|
-
const existing = await comments.findVisorComment(
|
|
1194
|
-
repo.owner,
|
|
1195
|
-
repo.name,
|
|
1196
|
-
pr,
|
|
1197
|
-
this.commentIdForGroup(ctx, group)
|
|
1198
|
-
);
|
|
1199
|
-
if (!existing || !existing.body) return this.buildFullBody(ctx, group);
|
|
1200
|
-
const body = String(existing.body);
|
|
1201
|
-
const doc = this.parseSections(body);
|
|
1202
|
-
doc.header = {
|
|
1203
|
-
...doc.header || {},
|
|
1204
|
-
key: this.threadKeyFor(ctx),
|
|
1205
|
-
revision: this.revision,
|
|
1206
|
-
group
|
|
1207
|
-
};
|
|
1208
|
-
if (changedIds) {
|
|
1209
|
-
const ids = Array.isArray(changedIds) ? changedIds : [changedIds];
|
|
1210
|
-
const fresh = this.renderSections(ctx, group);
|
|
1211
|
-
for (const id of ids) {
|
|
1212
|
-
const block = this.extractSectionById(fresh, id);
|
|
1213
|
-
if (block) doc.sections.set(id, block);
|
|
1246
|
+
return "review";
|
|
1214
1247
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
const block = this.extractSectionById(fresh, checkId);
|
|
1221
|
-
if (block) doc.sections.set(checkId, block);
|
|
1248
|
+
upsertSectionState(group, checkId, patch) {
|
|
1249
|
+
let groupMap = this.stepStatusByGroup.get(group);
|
|
1250
|
+
if (!groupMap) {
|
|
1251
|
+
groupMap = /* @__PURE__ */ new Map();
|
|
1252
|
+
this.stepStatusByGroup.set(group, groupMap);
|
|
1222
1253
|
}
|
|
1254
|
+
const prev = groupMap.get(checkId) || { status: "queued", lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1255
|
+
groupMap.set(checkId, { ...prev, ...patch });
|
|
1223
1256
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
parseSections(body) {
|
|
1228
|
-
const sections = /* @__PURE__ */ new Map();
|
|
1229
|
-
const headerRe = /<!--\s*visor:thread=(\{[\s\S]*?\})\s*-->/m;
|
|
1230
|
-
const startRe = /<!--\s*visor:section=(\{[\s\S]*?\})\s*-->/g;
|
|
1231
|
-
const endRe = /<!--\s*visor:section-end\s+id=\"([^\"]+)\"\s*-->/g;
|
|
1232
|
-
const safePick = (obj, allowed) => {
|
|
1233
|
-
if (!obj || typeof obj !== "object" || Array.isArray(obj)) return void 0;
|
|
1234
|
-
const out = /* @__PURE__ */ Object.create(null);
|
|
1235
|
-
for (const [k, t] of Object.entries(allowed)) {
|
|
1236
|
-
if (Object.prototype.hasOwnProperty.call(obj, k)) {
|
|
1237
|
-
const v = obj[k];
|
|
1238
|
-
if (t === "string" && typeof v === "string") out[k] = v;
|
|
1239
|
-
else if (t === "number" && typeof v === "number" && Number.isFinite(v)) out[k] = v;
|
|
1257
|
+
commentIdForGroup(ctx, group) {
|
|
1258
|
+
if (group === "dynamic") {
|
|
1259
|
+
return `visor-thread-dynamic-${ctx.run.runId}`;
|
|
1240
1260
|
}
|
|
1261
|
+
const r = ctx.run;
|
|
1262
|
+
const base = r.repo && r.pr ? `${r.repo.owner}/${r.repo.name}#${r.pr}` : r.runId;
|
|
1263
|
+
return `visor-thread-${group}-${base}`;
|
|
1241
1264
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1265
|
+
/**
|
|
1266
|
+
* Compute failure condition results for a completed check so Check Runs map to the
|
|
1267
|
+
* correct GitHub conclusion. This mirrors the engine's evaluation for fail_if.
|
|
1268
|
+
*/
|
|
1269
|
+
async evaluateFailureResults(ctx, checkId, result) {
|
|
1270
|
+
try {
|
|
1271
|
+
const config = ctx.config || {};
|
|
1272
|
+
const checks = config && config.checks || {};
|
|
1273
|
+
const checkCfg = checks[checkId] || {};
|
|
1274
|
+
const checkSchema = typeof checkCfg.schema === "string" ? checkCfg.schema : "code-review";
|
|
1275
|
+
const checkGroup = checkCfg.group || "default";
|
|
1276
|
+
const { FailureConditionEvaluator } = (init_failure_condition_evaluator(), __toCommonJS(failure_condition_evaluator_exports));
|
|
1277
|
+
const evaluator = new FailureConditionEvaluator();
|
|
1278
|
+
const reviewSummary = { issues: Array.isArray(result?.issues) ? result.issues : [] };
|
|
1279
|
+
const failures = [];
|
|
1280
|
+
if (config.fail_if) {
|
|
1281
|
+
const failed = await evaluator.evaluateSimpleCondition(
|
|
1282
|
+
checkId,
|
|
1283
|
+
checkSchema,
|
|
1284
|
+
checkGroup,
|
|
1285
|
+
reviewSummary,
|
|
1286
|
+
config.fail_if
|
|
1287
|
+
);
|
|
1288
|
+
failures.push({
|
|
1289
|
+
conditionName: "global_fail_if",
|
|
1290
|
+
failed,
|
|
1291
|
+
expression: config.fail_if,
|
|
1292
|
+
severity: "error",
|
|
1293
|
+
haltExecution: false
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
if (checkCfg.fail_if) {
|
|
1297
|
+
const failed = await evaluator.evaluateSimpleCondition(
|
|
1298
|
+
checkId,
|
|
1299
|
+
checkSchema,
|
|
1300
|
+
checkGroup,
|
|
1301
|
+
reviewSummary,
|
|
1302
|
+
checkCfg.fail_if
|
|
1303
|
+
);
|
|
1304
|
+
failures.push({
|
|
1305
|
+
conditionName: `${checkId}_fail_if`,
|
|
1306
|
+
failed,
|
|
1307
|
+
expression: checkCfg.fail_if,
|
|
1308
|
+
severity: "error",
|
|
1309
|
+
haltExecution: false
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
return failures;
|
|
1313
|
+
} catch {
|
|
1314
|
+
return [];
|
|
1315
|
+
}
|
|
1249
1316
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
const
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1317
|
+
// Debounce helpers
|
|
1318
|
+
scheduleUpdate(ctx, comments, group, id) {
|
|
1319
|
+
if (id) this._pendingIds.add(id);
|
|
1320
|
+
const now = Date.now();
|
|
1321
|
+
const since = now - this._lastFlush;
|
|
1322
|
+
const remaining = this.maxWaitMs - since;
|
|
1323
|
+
if (this._timer) clearTimeout(this._timer);
|
|
1324
|
+
const wait = Math.max(0, Math.min(this.debounceMs, remaining));
|
|
1325
|
+
this._timer = setTimeout(async () => {
|
|
1326
|
+
const ids = Array.from(this._pendingIds);
|
|
1327
|
+
this._pendingIds.clear();
|
|
1328
|
+
this._timer = null;
|
|
1329
|
+
await this.updateGroupedComment(ctx, comments, group, ids.length > 0 ? ids : void 0);
|
|
1330
|
+
this._lastFlush = Date.now();
|
|
1331
|
+
}, wait);
|
|
1265
1332
|
}
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
endRe.lastIndex = startIdx;
|
|
1276
|
-
const e = endRe.exec(body);
|
|
1277
|
-
if (!e) break;
|
|
1278
|
-
const id = typeof meta.id === "string" && meta.id ? String(meta.id) : String(e[1]);
|
|
1279
|
-
const content = body.substring(startIdx, e.index).trim();
|
|
1280
|
-
const block = `<!-- visor:section=${JSON.stringify(meta)} -->
|
|
1281
|
-
${content}
|
|
1282
|
-
<!-- visor:section-end id="${id}" -->`;
|
|
1283
|
-
sections.set(id, block);
|
|
1284
|
-
cursor = endRe.lastIndex;
|
|
1285
|
-
startRe.lastIndex = cursor;
|
|
1286
|
-
}
|
|
1287
|
-
return { header, sections };
|
|
1288
|
-
}
|
|
1289
|
-
serializeSections(doc) {
|
|
1290
|
-
const header = `<!-- visor:thread=${JSON.stringify({ ...doc.header || {}, generatedAt: (/* @__PURE__ */ new Date()).toISOString() })} -->`;
|
|
1291
|
-
const blocks = Array.from(doc.sections.values()).join("\n\n");
|
|
1292
|
-
const key = doc.header && doc.header.key || "";
|
|
1293
|
-
return `${header}
|
|
1294
|
-
|
|
1295
|
-
${blocks}
|
|
1296
|
-
|
|
1297
|
-
<!-- visor:thread-end key="${key}" -->`;
|
|
1298
|
-
}
|
|
1299
|
-
extractSectionById(rendered, id) {
|
|
1300
|
-
const rx = new RegExp(
|
|
1301
|
-
`<!--\\s*visor:section=(\\{[\\s\\S]*?\\})\\s*-->[\\s\\S]*?<!--\\s*visor:section-end\\s+id=\\"${this.escapeRegExp(id)}\\"\\s*-->`,
|
|
1302
|
-
"m"
|
|
1303
|
-
);
|
|
1304
|
-
const m = rx.exec(rendered);
|
|
1305
|
-
return m ? m[0] : void 0;
|
|
1306
|
-
}
|
|
1307
|
-
escapeRegExp(s) {
|
|
1308
|
-
return s.replace(/[.*+?^${}()|[\\]\\]/g, "\\$&");
|
|
1309
|
-
}
|
|
1310
|
-
getGroupForCheck(ctx, checkId) {
|
|
1311
|
-
try {
|
|
1312
|
-
const cfg = ctx.config || {};
|
|
1313
|
-
const g = cfg?.checks?.[checkId]?.group || cfg?.steps?.[checkId]?.group;
|
|
1314
|
-
if (typeof g === "string" && g.trim().length > 0) return g;
|
|
1315
|
-
} catch {
|
|
1316
|
-
}
|
|
1317
|
-
return "review";
|
|
1318
|
-
}
|
|
1319
|
-
upsertSectionState(group, checkId, patch) {
|
|
1320
|
-
let groupMap = this.stepStatusByGroup.get(group);
|
|
1321
|
-
if (!groupMap) {
|
|
1322
|
-
groupMap = /* @__PURE__ */ new Map();
|
|
1323
|
-
this.stepStatusByGroup.set(group, groupMap);
|
|
1324
|
-
}
|
|
1325
|
-
const prev = groupMap.get(checkId) || { status: "queued", lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1326
|
-
groupMap.set(checkId, { ...prev, ...patch });
|
|
1327
|
-
}
|
|
1328
|
-
commentIdForGroup(ctx, group) {
|
|
1329
|
-
if (group === "dynamic") {
|
|
1330
|
-
return `visor-thread-dynamic-${ctx.run.runId}`;
|
|
1331
|
-
}
|
|
1332
|
-
const r = ctx.run;
|
|
1333
|
-
const base = r.repo && r.pr ? `${r.repo.owner}/${r.repo.name}#${r.pr}` : r.runId;
|
|
1334
|
-
return `visor-thread-${group}-${base}`;
|
|
1335
|
-
}
|
|
1336
|
-
/**
|
|
1337
|
-
* Compute failure condition results for a completed check so Check Runs map to the
|
|
1338
|
-
* correct GitHub conclusion. This mirrors the engine's evaluation for fail_if.
|
|
1339
|
-
*/
|
|
1340
|
-
async evaluateFailureResults(ctx, checkId, result) {
|
|
1341
|
-
try {
|
|
1342
|
-
const config = ctx.config || {};
|
|
1343
|
-
const checks = config && config.checks || {};
|
|
1344
|
-
const checkCfg = checks[checkId] || {};
|
|
1345
|
-
const checkSchema = typeof checkCfg.schema === "string" ? checkCfg.schema : "code-review";
|
|
1346
|
-
const checkGroup = checkCfg.group || "default";
|
|
1347
|
-
const { FailureConditionEvaluator } = (init_failure_condition_evaluator(), __toCommonJS(failure_condition_evaluator_exports));
|
|
1348
|
-
const evaluator = new FailureConditionEvaluator();
|
|
1349
|
-
const reviewSummary = { issues: Array.isArray(result?.issues) ? result.issues : [] };
|
|
1350
|
-
const failures = [];
|
|
1351
|
-
if (config.fail_if) {
|
|
1352
|
-
const failed = await evaluator.evaluateSimpleCondition(
|
|
1353
|
-
checkId,
|
|
1354
|
-
checkSchema,
|
|
1355
|
-
checkGroup,
|
|
1356
|
-
reviewSummary,
|
|
1357
|
-
config.fail_if
|
|
1358
|
-
);
|
|
1359
|
-
failures.push({
|
|
1360
|
-
conditionName: "global_fail_if",
|
|
1361
|
-
failed,
|
|
1362
|
-
expression: config.fail_if,
|
|
1363
|
-
severity: "error",
|
|
1364
|
-
haltExecution: false
|
|
1365
|
-
});
|
|
1333
|
+
async flushNow(ctx, comments, group) {
|
|
1334
|
+
if (this._timer) {
|
|
1335
|
+
clearTimeout(this._timer);
|
|
1336
|
+
this._timer = null;
|
|
1337
|
+
}
|
|
1338
|
+
const ids = Array.from(this._pendingIds);
|
|
1339
|
+
this._pendingIds.clear();
|
|
1340
|
+
await this.updateGroupedComment(ctx, comments, group, ids.length > 0 ? ids : void 0);
|
|
1341
|
+
this._lastFlush = Date.now();
|
|
1366
1342
|
}
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
reviewSummary,
|
|
1373
|
-
checkCfg.fail_if
|
|
1374
|
-
);
|
|
1375
|
-
failures.push({
|
|
1376
|
-
conditionName: `${checkId}_fail_if`,
|
|
1377
|
-
failed,
|
|
1378
|
-
expression: checkCfg.fail_if,
|
|
1379
|
-
severity: "error",
|
|
1380
|
-
haltExecution: false
|
|
1381
|
-
});
|
|
1343
|
+
/**
|
|
1344
|
+
* Sleep utility for enforcing delays
|
|
1345
|
+
*/
|
|
1346
|
+
sleep(ms) {
|
|
1347
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1382
1348
|
}
|
|
1383
|
-
|
|
1384
|
-
} catch {
|
|
1385
|
-
return [];
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
// Debounce helpers
|
|
1389
|
-
scheduleUpdate(ctx, comments, group, id) {
|
|
1390
|
-
if (id) this._pendingIds.add(id);
|
|
1391
|
-
const now = Date.now();
|
|
1392
|
-
const since = now - this._lastFlush;
|
|
1393
|
-
const remaining = this.maxWaitMs - since;
|
|
1394
|
-
if (this._timer) clearTimeout(this._timer);
|
|
1395
|
-
const wait = Math.max(0, Math.min(this.debounceMs, remaining));
|
|
1396
|
-
this._timer = setTimeout(async () => {
|
|
1397
|
-
const ids = Array.from(this._pendingIds);
|
|
1398
|
-
this._pendingIds.clear();
|
|
1399
|
-
this._timer = null;
|
|
1400
|
-
await this.updateGroupedComment(ctx, comments, group, ids.length > 0 ? ids : void 0);
|
|
1401
|
-
this._lastFlush = Date.now();
|
|
1402
|
-
}, wait);
|
|
1403
|
-
}
|
|
1404
|
-
async flushNow(ctx, comments, group) {
|
|
1405
|
-
if (this._timer) {
|
|
1406
|
-
clearTimeout(this._timer);
|
|
1407
|
-
this._timer = null;
|
|
1408
|
-
}
|
|
1409
|
-
const ids = Array.from(this._pendingIds);
|
|
1410
|
-
this._pendingIds.clear();
|
|
1411
|
-
await this.updateGroupedComment(ctx, comments, group, ids.length > 0 ? ids : void 0);
|
|
1412
|
-
this._lastFlush = Date.now();
|
|
1413
|
-
}
|
|
1414
|
-
/**
|
|
1415
|
-
* Sleep utility for enforcing delays
|
|
1416
|
-
*/
|
|
1417
|
-
sleep(ms) {
|
|
1418
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1349
|
+
};
|
|
1419
1350
|
}
|
|
1420
|
-
};
|
|
1351
|
+
});
|
|
1352
|
+
init_github_frontend();
|
|
1421
1353
|
export {
|
|
1422
1354
|
GitHubFrontend
|
|
1423
1355
|
};
|
|
1424
|
-
//# sourceMappingURL=github-frontend-
|
|
1356
|
+
//# sourceMappingURL=github-frontend-3N2NLO66.mjs.map
|