specrails-desktop 2.34.0 → 2.35.0
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/client/dist/assets/ActivityFeedPage-LCBJ5WiD.js +1 -0
- package/client/dist/assets/AgentBrowserCapture-B-xQpyZr.js +1 -0
- package/client/dist/assets/AgentModeAnalyticsPane-BI1Vcf3T.js +2 -0
- package/client/dist/assets/AgentModeCodePane-BTRwCl9h.js +2 -0
- package/client/dist/assets/AgentModeJobsPane-VzeBJLqP.js +1 -0
- package/client/dist/assets/AgentsPage-CJj1K24O.js +87 -0
- package/client/dist/assets/AnalyticsPage-CQpV5_fX.js +1 -0
- package/client/dist/assets/CodePage-9-tFh0jB.js +2 -0
- package/client/dist/assets/DesktopAnalyticsPage-BcZ0_O_C.js +1 -0
- package/client/dist/assets/DocsDialog-BPAzDICW.js +11 -0
- package/client/dist/assets/DocsPage-Dqo4Hn6G.js +11 -0
- package/client/dist/assets/IntegrationsPage-Bhy58lnQ.js +1 -0
- package/client/dist/assets/InteractiveJobComposer-D36fZWai.js +19 -0
- package/client/dist/assets/JobDetailModal-BxsktbkQ.js +1 -0
- package/client/dist/assets/JobDetailPage-WWyCy6Sp.js +1 -0
- package/client/dist/assets/JobsPage-CEbM7uxU.js +1 -0
- package/client/dist/assets/{LoopBuilderPage-WeiofeL6.js → LoopBuilderPage-CYsSN8io.js} +2 -2
- package/client/dist/assets/LoopPreviewModal-BJhgX5wY.js +1 -0
- package/client/dist/assets/LoopsPage-DB4OqMot.js +1 -0
- package/client/dist/assets/MinimizedChatsContext-CcjBHMHd.js +1 -0
- package/client/dist/assets/PluginsPage-CR8K4nM_.js +2 -0
- package/client/dist/assets/{ProjectSettingsDialog-BkCXBQWk.js → ProjectSettingsDialog-C45-qhF4.js} +1 -1
- package/client/dist/assets/ReviewPacketPage-Cz_QQeC3.js +1 -0
- package/client/dist/assets/TemplatePreviewModal-wFBd2vQP.js +1 -0
- package/client/dist/assets/TicketDetailModal-BvZoyDAL.js +14 -0
- package/client/dist/assets/{Trans-CTqw6rr9.js → Trans-DXZ-BLXb.js} +1 -1
- package/client/dist/assets/agent-BQgKv_2F.js +1 -0
- package/client/dist/assets/agent-C1NRDJqL.js +1 -0
- package/client/dist/assets/agent-CLNleiy6.js +1 -0
- package/client/dist/assets/agent-CmceArkQ.js +1 -0
- package/client/dist/assets/agent-D3UxQ3SR.js +1 -0
- package/client/dist/assets/agent-DB1JeE64.js +1 -0
- package/client/dist/assets/agent-DXkHcQ-D.js +1 -0
- package/client/dist/assets/agent-DjJJJ2M8.js +1 -0
- package/client/dist/assets/ban-E4EaZe3a.js +1 -0
- package/client/dist/assets/bot-DzuuuC5R.js +1 -0
- package/client/dist/assets/{browser-DxP-alBK.js → browser-BgqHDMXP.js} +1 -1
- package/client/dist/assets/{browser-CyR1Kp3m.js → browser-CsKJtrpv.js} +1 -1
- package/client/dist/assets/browser-Cu58EpZU.js +1 -0
- package/client/dist/assets/{browser-rRCwi7Y7.js → browser-CvXOJm-j.js} +1 -1
- package/client/dist/assets/{browser-CvgDySd9.js → browser-DOrFh1c7.js} +1 -1
- package/client/dist/assets/{browser-DwZljcRh.js → browser-DQv_Jo7x.js} +1 -1
- package/client/dist/assets/{browser-C25DkQiO.js → browser-LPgxvJu2.js} +1 -1
- package/client/dist/assets/{browser-CiJ7-L32.js → browser-eL-3MIEI.js} +1 -1
- package/client/dist/assets/button-CJqFnUOL.js +1 -0
- package/client/dist/assets/{chunk-KS7C4IRE-Cx5sqopX.js → chunk-KS7C4IRE-DEKu087k.js} +1 -1
- package/client/dist/assets/{commands-DyUZQ1FI.js → commands-6tYQih5m.js} +1 -1
- package/client/dist/assets/{commands-B9RZO4qS.js → commands-BK2QinXN.js} +1 -1
- package/client/dist/assets/{commands-Bbxu8kLU.js → commands-C2Nca76h.js} +1 -1
- package/client/dist/assets/{commands-CDU3qv68.js → commands-CKdLihNX.js} +1 -1
- package/client/dist/assets/{commands-DTQBF_q5.js → commands-CYIBTAlc.js} +1 -1
- package/client/dist/assets/{commands-hOtKuOXc.js → commands-CjUtYJIM.js} +1 -1
- package/client/dist/assets/{commands-BEwbAjtD.js → commands-Cq_u_8y9.js} +1 -1
- package/client/dist/assets/{commands-CzezgtQj.js → commands-D_ZHYQug.js} +1 -1
- package/client/dist/assets/core-B3mKBIh5.js +1 -0
- package/client/dist/assets/dashboard-B9GhC5ku.js +1 -0
- package/client/dist/assets/dashboard-C8WOEiPu.js +1 -0
- package/client/dist/assets/dashboard-CBnv6zOI.js +1 -0
- package/client/dist/assets/dashboard-CaP0fU87.js +1 -0
- package/client/dist/assets/dashboard-D4ZZ4x84.js +1 -0
- package/client/dist/assets/dashboard-DmuTQBX8.js +1 -0
- package/client/dist/assets/dashboard-DynLYj3d.js +1 -0
- package/client/dist/assets/dashboard-zj4gGFJ1.js +1 -0
- package/client/dist/assets/dialog-CdWCb02j.js +45 -0
- package/client/dist/assets/dist-js-CWeUdf5Z.js +1 -0
- package/client/dist/assets/dist-js-Rlh4FLH5.js +1 -0
- package/client/dist/assets/event-Bt7f3wXe.js +1 -0
- package/client/dist/assets/file-text-BcLCfcWZ.js +1 -0
- package/client/dist/assets/{formatDistanceToNow-BOuXgebD.js → formatDistanceToNow-C0ImhVKp.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-EjGOpn4b.js → getTimezoneOffsetInMilliseconds-owhDdTXs.js} +1 -1
- package/client/dist/assets/git-refresh-wTW0rO0Z.js +1 -0
- package/client/dist/assets/i18n-DwgvwTnx.js +2 -0
- package/client/dist/assets/index-DhjjFyU5.js +66 -0
- package/client/dist/assets/index-xBFsOEiJ.css +2 -0
- package/client/dist/assets/jira-api-CcZMcEN5.js +1 -0
- package/client/dist/assets/loops-B7YpGizO.js +1 -0
- package/client/dist/assets/{loops-CMMQOw2P.js → loops-BomD2J-h.js} +1 -1
- package/client/dist/assets/{loops-B2_97D26.js → loops-BrQHSQw3.js} +1 -1
- package/client/dist/assets/{loops-BwxCndZk.js → loops-BvBwvTPA.js} +1 -1
- package/client/dist/assets/{loops-BISOkuCu.js → loops-CODk6Slz.js} +1 -1
- package/client/dist/assets/{loops-CD19kEzq.js → loops-DC6k-Ym-.js} +1 -1
- package/client/dist/assets/{loops-aEb4RxN2.js → loops-DSEg8-uY.js} +1 -1
- package/client/dist/assets/{loops-Bo6w_peZ.js → loops-DhCoXD_v.js} +1 -1
- package/client/dist/assets/narration-4Z2VUhZw.js +1 -0
- package/client/dist/assets/narration-BGuiEDj3.js +1 -0
- package/client/dist/assets/narration-BuSoqRmv.js +1 -0
- package/client/dist/assets/narration-CB9t29fy.js +1 -0
- package/client/dist/assets/narration-CU3x8FHD.js +1 -0
- package/client/dist/assets/narration-CWeXhfP5.js +1 -0
- package/client/dist/assets/narration-DRaDnhT5.js +1 -0
- package/client/dist/assets/narration-DtEbQgCw.js +1 -0
- package/client/dist/assets/packet-B1IxaL5f.js +1 -0
- package/client/dist/assets/packet-Bh5kkbiB.js +1 -0
- package/client/dist/assets/packet-By8fqYxW.js +1 -0
- package/client/dist/assets/packet-CZdvA44E.js +1 -0
- package/client/dist/assets/packet-CrjHRQCP.js +1 -0
- package/client/dist/assets/packet-Dwi2pTtx.js +1 -0
- package/client/dist/assets/packet-_-ZIa0IR.js +1 -0
- package/client/dist/assets/packet-n0W6E1Hn.js +1 -0
- package/client/dist/assets/save-B6wGWwBR.js +1 -0
- package/client/dist/assets/settings-Bd0x4TBa.js +1 -0
- package/client/dist/assets/settings-C1-7cXzC.js +1 -0
- package/client/dist/assets/settings-CAFaT38b.js +1 -0
- package/client/dist/assets/settings-Ct3v1nW2.js +1 -0
- package/client/dist/assets/settings-DHh7_IF5.js +1 -0
- package/client/dist/assets/settings-DQXAmdcv.js +1 -0
- package/client/dist/assets/settings-guzlhpup.js +1 -0
- package/client/dist/assets/settings-sZfTPMEH.js +1 -0
- package/client/dist/assets/{spending-BpYPsevQ.js → spending-BQX3P7Cq.js} +1 -1
- package/client/dist/assets/useDesktop-BzX769yH.js +1 -0
- package/client/dist/index.html +29 -31
- package/docs/internals/embedded-browser-native-webview-evaluation.md +161 -0
- package/docs/internals/review-packet.md +212 -0
- package/package.json +1 -1
- package/server/dist/accept-ladder.js +61 -0
- package/server/dist/agent-chat-manager.js +22 -1
- package/server/dist/agent-defaults.js +359 -0
- package/server/dist/agent-operator-prompt.js +13 -5
- package/server/dist/browser-playwright.js +39 -2
- package/server/dist/db.js +38 -0
- package/server/dist/delivery-evidence.js +242 -0
- package/server/dist/desktop-router.js +23 -0
- package/server/dist/feature-flags.js +23 -0
- package/server/dist/loop-command-catalog.js +33 -0
- package/server/dist/loop-executors.js +12 -2
- package/server/dist/loop-factory.js +16 -1
- package/server/dist/loops-router.js +2 -0
- package/server/dist/mcp/tools/rails.js +16 -0
- package/server/dist/project-registry.js +33 -1
- package/server/dist/project-router-loop-runs.js +27 -2
- package/server/dist/providers/claude-adapter.js +60 -3
- package/server/dist/queue-manager.js +47 -4
- package/server/dist/rail-isolated-launch.js +119 -5
- package/server/dist/rail-pr-store.js +40 -3
- package/server/dist/rail-worktree-release.js +17 -1
- package/server/dist/rails-router.js +124 -3
- package/server/dist/review-packet.js +327 -0
- package/server/dist/revision-seed.js +73 -0
- package/server/dist/run-duration-stats.js +71 -0
- package/server/dist/stuck-run-detector.js +153 -0
- package/server/dist/telemetry-receiver.js +22 -0
- package/server/dist/worktree-node-modules.js +138 -16
- package/client/dist/assets/ActivityFeedPage-DIDxNsDj.js +0 -1
- package/client/dist/assets/AgentBrowserCapture-7pJ2G5VY.js +0 -1
- package/client/dist/assets/AgentModeAnalyticsPane-BSSyTnTM.js +0 -2
- package/client/dist/assets/AgentModeCodePane-DzW6gvDw.js +0 -2
- package/client/dist/assets/AgentModeJobsPane-9wJff15f.js +0 -1
- package/client/dist/assets/AgentsPage-CIA67psb.js +0 -87
- package/client/dist/assets/AnalyticsPage-C0LwYELz.js +0 -1
- package/client/dist/assets/CodePage-RtKvHmAO.js +0 -2
- package/client/dist/assets/DesktopAnalyticsPage-BU8Y_Ts1.js +0 -1
- package/client/dist/assets/DocsDialog-DIpoaGlQ.js +0 -11
- package/client/dist/assets/DocsPage-ME47KZpK.js +0 -11
- package/client/dist/assets/IntegrationsPage-Bs00_tl4.js +0 -1
- package/client/dist/assets/InteractiveJobComposer-BGjIe9R6.js +0 -19
- package/client/dist/assets/JobDetailModal-1UZkqH4I.js +0 -1
- package/client/dist/assets/JobDetailPage-CN65sAf1.js +0 -1
- package/client/dist/assets/JobsPage-f-4dqys7.js +0 -1
- package/client/dist/assets/LoopPreviewModal-DDTnTyH0.js +0 -1
- package/client/dist/assets/LoopsPage-BVNchIex.js +0 -1
- package/client/dist/assets/MinimizedChatsContext-CHsu3NCX.js +0 -1
- package/client/dist/assets/PluginsPage-CdFzGEfQ.js +0 -2
- package/client/dist/assets/TemplatePreviewModal-RDi0khFG.js +0 -1
- package/client/dist/assets/TicketDetailModal-MhJzrqbi.js +0 -14
- package/client/dist/assets/agent-Bd-q3Wqp.js +0 -1
- package/client/dist/assets/agent-BlvdW4Hv.js +0 -1
- package/client/dist/assets/agent-C-KQhxeM.js +0 -1
- package/client/dist/assets/agent-Ca15w5eu.js +0 -1
- package/client/dist/assets/agent-D6fKaoMd.js +0 -1
- package/client/dist/assets/agent-HFMYMTbB.js +0 -1
- package/client/dist/assets/agent-QIEbkBjg.js +0 -1
- package/client/dist/assets/agent-mTCsAbCl.js +0 -1
- package/client/dist/assets/api-B2M7CtDw.js +0 -1
- package/client/dist/assets/arrow-left-CpIR4j7G.js +0 -1
- package/client/dist/assets/arrow-up-right-9DoMn7fD.js +0 -1
- package/client/dist/assets/browser-CXUcnTMI.js +0 -1
- package/client/dist/assets/button-BUIIBQPT.js +0 -1
- package/client/dist/assets/chevron-right-CViMxucY.js +0 -1
- package/client/dist/assets/dashboard-BHRqsGCV.js +0 -1
- package/client/dist/assets/dashboard-Bfr90vOl.js +0 -1
- package/client/dist/assets/dashboard-CTsM8x3m.js +0 -1
- package/client/dist/assets/dashboard-CqC6wX0h.js +0 -1
- package/client/dist/assets/dashboard-CuZtAszc.js +0 -1
- package/client/dist/assets/dashboard-DAcqIc-n.js +0 -1
- package/client/dist/assets/dashboard-DekiV-86.js +0 -1
- package/client/dist/assets/dashboard-DoDczc5S.js +0 -1
- package/client/dist/assets/dialog-CzIekCw0.js +0 -45
- package/client/dist/assets/dist-Cez5CRkX.js +0 -1
- package/client/dist/assets/dist-js-BmsFWRly.js +0 -1
- package/client/dist/assets/dist-js-J5W_N5ng.js +0 -1
- package/client/dist/assets/external-link-BcU5EWZp.js +0 -1
- package/client/dist/assets/file-text-CFT-xXKN.js +0 -1
- package/client/dist/assets/i18n-nJQvD5zJ.js +0 -2
- package/client/dist/assets/index-C_sdsSZt.js +0 -63
- package/client/dist/assets/index-DCN5QbDm.css +0 -2
- package/client/dist/assets/jira-api-Bhixwn-d.js +0 -1
- package/client/dist/assets/loops-CnxqL8IL.js +0 -1
- package/client/dist/assets/save-28sIlCdr.js +0 -1
- package/client/dist/assets/settings-BveOJoaI.js +0 -1
- package/client/dist/assets/settings-Cj1C6F5k.js +0 -1
- package/client/dist/assets/settings-CuVwOpe0.js +0 -1
- package/client/dist/assets/settings-D5X4DfRc.js +0 -1
- package/client/dist/assets/settings-DXwxe-BW.js +0 -1
- package/client/dist/assets/settings-Di81Ba_V.js +0 -1
- package/client/dist/assets/settings-jbLjCYC_.js +0 -1
- package/client/dist/assets/settings-wIZNPFnn.js +0 -1
- package/client/dist/assets/useBrowserCaptureSession-BBXBN0Na.js +0 -1
- package/client/dist/assets/useDesktop-B6Du2N83.js +0 -1
- package/client/dist/assets/useSharedWebSocket-DK96ctb7.js +0 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MIN_DURATION_SAMPLES = void 0;
|
|
4
|
+
exports.percentileMs = percentileMs;
|
|
5
|
+
exports.rangeFromSamples = rangeFromSamples;
|
|
6
|
+
exports.getLoopDurationRange = getLoopDurationRange;
|
|
7
|
+
exports.jobCommandShape = jobCommandShape;
|
|
8
|
+
exports.getJobCommandDurationRange = getJobCommandDurationRange;
|
|
9
|
+
/** Fewer settled runs than this and no band is returned. */
|
|
10
|
+
exports.MIN_DURATION_SAMPLES = 5;
|
|
11
|
+
/**
|
|
12
|
+
* Nearest-rank percentile over an ASCENDING sample. Deliberately not
|
|
13
|
+
* interpolated: an interpolated value is a number no run actually took, and
|
|
14
|
+
* every figure this module emits must be traceable to a real measurement.
|
|
15
|
+
*/
|
|
16
|
+
function percentileMs(sortedAsc, fraction) {
|
|
17
|
+
if (sortedAsc.length === 0)
|
|
18
|
+
return 0;
|
|
19
|
+
const rank = Math.ceil(fraction * sortedAsc.length);
|
|
20
|
+
const index = Math.min(sortedAsc.length - 1, Math.max(0, rank - 1));
|
|
21
|
+
return sortedAsc[index];
|
|
22
|
+
}
|
|
23
|
+
function rangeFromSamples(samplesMs) {
|
|
24
|
+
const usable = samplesMs.filter((ms) => Number.isFinite(ms) && ms > 0).sort((a, b) => a - b);
|
|
25
|
+
if (usable.length < exports.MIN_DURATION_SAMPLES)
|
|
26
|
+
return null;
|
|
27
|
+
return {
|
|
28
|
+
p25Ms: percentileMs(usable, 0.25),
|
|
29
|
+
medianMs: percentileMs(usable, 0.5),
|
|
30
|
+
p75Ms: percentileMs(usable, 0.75),
|
|
31
|
+
sampleCount: usable.length,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Cap the history window so a long-lived project's ancient runs (different
|
|
35
|
+
* codebase, different model) cannot skew the band a user is shown today. */
|
|
36
|
+
const HISTORY_LIMIT = 50;
|
|
37
|
+
/** Measured band for a loop id, or null below the sample floor. */
|
|
38
|
+
function getLoopDurationRange(db, projectId, loopId) {
|
|
39
|
+
const rows = db.prepare(`
|
|
40
|
+
SELECT total_duration_ms AS ms
|
|
41
|
+
FROM loop_runs
|
|
42
|
+
WHERE project_id = ? AND loop_id = ? AND finished_at IS NOT NULL AND total_duration_ms > 0
|
|
43
|
+
ORDER BY started_at DESC
|
|
44
|
+
LIMIT ?
|
|
45
|
+
`).all(projectId, loopId, HISTORY_LIMIT);
|
|
46
|
+
return rangeFromSamples(rows.map((row) => row.ms));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Commands carry per-run arguments (`loop:<uuid>`, `/specrails:implement #12`),
|
|
50
|
+
* so history is grouped by a stable SHAPE rather than the literal string:
|
|
51
|
+
* the command's leading token with ticket refs and ids stripped.
|
|
52
|
+
*/
|
|
53
|
+
function jobCommandShape(command) {
|
|
54
|
+
const head = command.trim().split(/\s+/)[0] ?? '';
|
|
55
|
+
if (head.startsWith('loop:'))
|
|
56
|
+
return 'loop:';
|
|
57
|
+
return head;
|
|
58
|
+
}
|
|
59
|
+
/** Measured band for a job command shape, or null below the sample floor. */
|
|
60
|
+
function getJobCommandDurationRange(db, commandShape) {
|
|
61
|
+
const rows = db.prepare(`
|
|
62
|
+
SELECT duration_ms AS ms, command
|
|
63
|
+
FROM jobs
|
|
64
|
+
WHERE status = 'completed' AND duration_ms > 0
|
|
65
|
+
ORDER BY started_at DESC
|
|
66
|
+
LIMIT 500
|
|
67
|
+
`).all();
|
|
68
|
+
return rangeFromSamples(rows.filter((row) => jobCommandShape(row.command ?? '') === commandShape)
|
|
69
|
+
.slice(0, HISTORY_LIMIT)
|
|
70
|
+
.map((row) => row.ms));
|
|
71
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StuckRunDetector = exports.STUCK_SWEEP_INTERVAL_MS = exports.STUCK_FLOOR_MS = void 0;
|
|
4
|
+
exports.resolveStuckThresholdMs = resolveStuckThresholdMs;
|
|
5
|
+
exports.findStuckCandidates = findStuckCandidates;
|
|
6
|
+
const loop_runs_store_1 = require("./loop-runs-store");
|
|
7
|
+
/** Never notify sooner than this, however the threshold is configured. */
|
|
8
|
+
exports.STUCK_FLOOR_MS = 10 * 60 * 1000;
|
|
9
|
+
/** How often the sweep runs while the server is up. */
|
|
10
|
+
exports.STUCK_SWEEP_INTERVAL_MS = 60 * 1000;
|
|
11
|
+
/**
|
|
12
|
+
* `SPECRAILS_STUCK_THRESHOLD_MS` raises (never lowers) the floor; `0`/`false`/
|
|
13
|
+
* `off` disables detection entirely.
|
|
14
|
+
*/
|
|
15
|
+
function resolveStuckThresholdMs(env = process.env) {
|
|
16
|
+
const raw = env.SPECRAILS_STUCK_THRESHOLD_MS;
|
|
17
|
+
if (raw === undefined || raw === '')
|
|
18
|
+
return exports.STUCK_FLOOR_MS;
|
|
19
|
+
const normalized = raw.trim().toLowerCase();
|
|
20
|
+
if (normalized === '0' || normalized === 'false' || normalized === 'off')
|
|
21
|
+
return null;
|
|
22
|
+
const parsed = Number(normalized);
|
|
23
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
24
|
+
return exports.STUCK_FLOOR_MS;
|
|
25
|
+
return Math.max(exports.STUCK_FLOOR_MS, Math.trunc(parsed));
|
|
26
|
+
}
|
|
27
|
+
function lastActivityMs(payload) {
|
|
28
|
+
const candidates = [payload.lastActivityAtMs, payload.activeTurnStartedAtMs]
|
|
29
|
+
.filter((value) => Number.isFinite(value) && value > 0);
|
|
30
|
+
return candidates.length > 0 ? Math.max(...candidates) : null;
|
|
31
|
+
}
|
|
32
|
+
function defaultIsRunActive(db, runId) {
|
|
33
|
+
const row = db.prepare(`SELECT status FROM loop_runs WHERE id = ?`).get(runId);
|
|
34
|
+
return row?.status === 'running';
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* One pass over the persisted step checkpoints. Pure with respect to the
|
|
38
|
+
* database (reads only) so the caller owns broadcasting and episode memory.
|
|
39
|
+
*/
|
|
40
|
+
function findStuckCandidates(io) {
|
|
41
|
+
const threshold = io.thresholdMs === undefined ? resolveStuckThresholdMs() : io.thresholdMs;
|
|
42
|
+
if (threshold === null)
|
|
43
|
+
return [];
|
|
44
|
+
const now = io.now?.() ?? Date.now();
|
|
45
|
+
const isRunActive = io.isRunActive ?? ((runId) => defaultIsRunActive(io.db, runId));
|
|
46
|
+
const out = [];
|
|
47
|
+
let rows;
|
|
48
|
+
try {
|
|
49
|
+
rows = (0, loop_runs_store_1.listLoopStepRecoveries)(io.db);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
for (const row of rows) {
|
|
55
|
+
let payload;
|
|
56
|
+
try {
|
|
57
|
+
payload = JSON.parse(row.payload);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const last = lastActivityMs(payload);
|
|
63
|
+
if (last === null)
|
|
64
|
+
continue;
|
|
65
|
+
const staleMs = now - last;
|
|
66
|
+
if (staleMs < threshold)
|
|
67
|
+
continue;
|
|
68
|
+
try {
|
|
69
|
+
if (!isRunActive(row.run_id))
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
out.push({
|
|
76
|
+
runId: row.run_id,
|
|
77
|
+
stepKey: row.step_key,
|
|
78
|
+
projectId: payload.projectId,
|
|
79
|
+
staleMs,
|
|
80
|
+
episode: `${row.run_id}:${row.step_key}:${last}`,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Owns episode memory across sweeps and emits at most one message per stall.
|
|
87
|
+
* Constructed per project context so the broadcast is already project-scoped.
|
|
88
|
+
*/
|
|
89
|
+
class StuckRunDetector {
|
|
90
|
+
projectId;
|
|
91
|
+
io;
|
|
92
|
+
broadcast;
|
|
93
|
+
notified = new Set();
|
|
94
|
+
timer = null;
|
|
95
|
+
constructor(projectId, io, broadcast) {
|
|
96
|
+
this.projectId = projectId;
|
|
97
|
+
this.io = io;
|
|
98
|
+
this.broadcast = broadcast;
|
|
99
|
+
}
|
|
100
|
+
/** One sweep; returns the messages actually emitted (for tests/diagnostics). */
|
|
101
|
+
sweep() {
|
|
102
|
+
const candidates = findStuckCandidates(this.io);
|
|
103
|
+
const live = new Set(candidates.map((candidate) => candidate.episode));
|
|
104
|
+
// Forget episodes that ended so a later stall of the same step re-notifies.
|
|
105
|
+
for (const episode of [...this.notified]) {
|
|
106
|
+
if (!live.has(episode))
|
|
107
|
+
this.notified.delete(episode);
|
|
108
|
+
}
|
|
109
|
+
const emitted = [];
|
|
110
|
+
for (const candidate of candidates) {
|
|
111
|
+
if (this.notified.has(candidate.episode))
|
|
112
|
+
continue;
|
|
113
|
+
this.notified.add(candidate.episode);
|
|
114
|
+
const message = {
|
|
115
|
+
type: 'job.stuck',
|
|
116
|
+
projectId: this.projectId,
|
|
117
|
+
jobId: candidate.runId,
|
|
118
|
+
stepKey: candidate.stepKey,
|
|
119
|
+
staleMs: candidate.staleMs,
|
|
120
|
+
timestamp: new Date(this.io.now?.() ?? Date.now()).toISOString(),
|
|
121
|
+
};
|
|
122
|
+
try {
|
|
123
|
+
this.broadcast(message);
|
|
124
|
+
emitted.push(message);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// A failed broadcast must not wedge the sweep; the next pass retries
|
|
128
|
+
// because the episode is only remembered after a successful send.
|
|
129
|
+
this.notified.delete(candidate.episode);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return emitted;
|
|
133
|
+
}
|
|
134
|
+
start(intervalMs = exports.STUCK_SWEEP_INTERVAL_MS) {
|
|
135
|
+
if (this.timer)
|
|
136
|
+
return;
|
|
137
|
+
if ((this.io.thresholdMs === undefined ? resolveStuckThresholdMs() : this.io.thresholdMs) === null)
|
|
138
|
+
return;
|
|
139
|
+
this.timer = setInterval(() => {
|
|
140
|
+
try {
|
|
141
|
+
this.sweep();
|
|
142
|
+
}
|
|
143
|
+
catch { /* never let a sweep crash the server */ }
|
|
144
|
+
}, intervalMs);
|
|
145
|
+
this.timer.unref?.();
|
|
146
|
+
}
|
|
147
|
+
stop() {
|
|
148
|
+
if (this.timer)
|
|
149
|
+
clearInterval(this.timer);
|
|
150
|
+
this.timer = null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
exports.StuckRunDetector = StuckRunDetector;
|
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.awaitTelemetryAppendQuiescence = awaitTelemetryAppendQuiescence;
|
|
6
7
|
exports.dropBlobState = dropBlobState;
|
|
7
8
|
exports.dropBlobStatesForProject = dropBlobStatesForProject;
|
|
8
9
|
exports.createTelemetryRouter = createTelemetryRouter;
|
|
@@ -35,6 +36,27 @@ function getBlobState(key) {
|
|
|
35
36
|
function blobKey(projectId, jobId) {
|
|
36
37
|
return `${projectId}:${jobId}`;
|
|
37
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Await full append-queue quiescence (every blob: idle writer + empty queue).
|
|
41
|
+
* Test seam: the ingest handler ACKs 200 while queued gzip appends are still
|
|
42
|
+
* in flight; a suite that removes the telemetry dir in afterEach races them —
|
|
43
|
+
* the orphaned append then console.errors DURING vitest environment teardown
|
|
44
|
+
* ("Closing rpc while onUserConsoleLog was pending", flaked in CI 2026-07-27).
|
|
45
|
+
* Bounded so a wedged writer can never hang a suite.
|
|
46
|
+
*/
|
|
47
|
+
async function awaitTelemetryAppendQuiescence(maxWaitMs = 2000) {
|
|
48
|
+
const deadline = Date.now() + maxWaitMs;
|
|
49
|
+
const busy = () => {
|
|
50
|
+
for (const state of _blobState.values()) {
|
|
51
|
+
if (state.writing || state.queue.length > 0)
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
};
|
|
56
|
+
while (busy() && Date.now() < deadline) {
|
|
57
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
38
60
|
/** Evict the in-memory BlobState for one job (bounds the per-(project,job) leak —
|
|
39
61
|
* the map otherwise grew for the whole process lifetime). */
|
|
40
62
|
function dropBlobState(projectId, jobId) {
|
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.isWorktreeNodeModulesEnabled = isWorktreeNodeModulesEnabled;
|
|
37
|
+
exports.authenticateWarmNodeModulesLinks = authenticateWarmNodeModulesLinks;
|
|
37
38
|
exports.linkNodeModulesIntoWorktree = linkNodeModulesIntoWorktree;
|
|
38
39
|
/**
|
|
39
40
|
* Warm-dependency reuse for isolated rail worktrees.
|
|
@@ -57,10 +58,26 @@ exports.linkNodeModulesIntoWorktree = linkNodeModulesIntoWorktree;
|
|
|
57
58
|
* prior link, anything) is left untouched, so resume passes are idempotent
|
|
58
59
|
* and an agent-made install is never clobbered.
|
|
59
60
|
*
|
|
60
|
-
* The returned `
|
|
61
|
-
* list, so a linked `node_modules` inherits the same commit-time
|
|
62
|
-
* overlay scaffolding (excluded pathspecs + index audit) even if
|
|
63
|
-
* `.gitignore` were ever missing the entry.
|
|
61
|
+
* The returned `authenticated` paths are appended to the launch's overlay-
|
|
62
|
+
* exclusion list, so a linked `node_modules` inherits the same commit-time
|
|
63
|
+
* guarantees as overlay scaffolding (excluded pathspecs + index audit) even if
|
|
64
|
+
* the repo's `.gitignore` were ever missing the entry.
|
|
65
|
+
*
|
|
66
|
+
* Release safety (why `authenticated` exists next to `linked`): a repo whose
|
|
67
|
+
* `.gitignore` carries the ordinary `node_modules/` DIRECTORY pattern does not
|
|
68
|
+
* ignore a SYMLINK named `node_modules`, so the link surfaces as an untracked
|
|
69
|
+
* entry in `git status`. Without evidence authorizing it, worktree release read
|
|
70
|
+
* that as "the worktree contains changes made after settlement", parked the row
|
|
71
|
+
* at `needs-review` forever and permanently blocked checkout of the branch. The
|
|
72
|
+
* links therefore also carry `OverlayCleanupEvidence`, and a link created by an
|
|
73
|
+
* EARLIER pass (resume) is re-authenticated instead of being ignored — the old
|
|
74
|
+
* `linked`-only contract silently dropped both guarantees on resume.
|
|
75
|
+
*
|
|
76
|
+
* Authentication is deliberately narrow and source-anchored, never name-based:
|
|
77
|
+
* the worktree entry must be a symlink whose resolved target is exactly the base
|
|
78
|
+
* checkout's identically-named dependency directory. A real directory, a copy,
|
|
79
|
+
* or a link pointing anywhere else stays unauthorized and preserves the
|
|
80
|
+
* worktree, as `Recoverable work is never removed automatically` requires.
|
|
64
81
|
*
|
|
65
82
|
* Honest caveat (documented, accepted): the link SHARES the dependency tree
|
|
66
83
|
* with the base checkout. Reads (tests, builds) are safe under concurrency; an
|
|
@@ -72,8 +89,11 @@ exports.linkNodeModulesIntoWorktree = linkNodeModulesIntoWorktree;
|
|
|
72
89
|
*/
|
|
73
90
|
const fs = __importStar(require("fs"));
|
|
74
91
|
const path = __importStar(require("path"));
|
|
92
|
+
const worktree_overlay_1 = require("./worktree-overlay");
|
|
75
93
|
/** Package-dir discovery depth: 0 = repo root, 1 = `client/`-style children. */
|
|
76
94
|
const MAX_DEPTH = 2;
|
|
95
|
+
/** Name of the dependency directory this module links. */
|
|
96
|
+
const DEPS_DIR = 'node_modules';
|
|
77
97
|
function isWorktreeNodeModulesEnabled() {
|
|
78
98
|
return (process.env.SPECRAILS_WORKTREE_NODE_MODULES ?? '').toLowerCase() !== 'false';
|
|
79
99
|
}
|
|
@@ -127,31 +147,133 @@ function discoverPackageDirs(baseRepo) {
|
|
|
127
147
|
walk('', 0);
|
|
128
148
|
return found;
|
|
129
149
|
}
|
|
150
|
+
/** Resolve a path through symlinks when possible; the literal path otherwise
|
|
151
|
+
* (a nonexistent target must still compare, and must still compare EQUAL to an
|
|
152
|
+
* equally-nonexistent expectation rather than silently authorizing). */
|
|
153
|
+
function resolveRealPath(target) {
|
|
154
|
+
try {
|
|
155
|
+
return fs.realpathSync(target);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return path.resolve(target);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Prove one worktree-relative path is an app-created warm-dependency link and
|
|
163
|
+
* fingerprint it. Returns null for anything that is not EXACTLY a symlink whose
|
|
164
|
+
* target resolves to the base checkout's identically-named directory — a real
|
|
165
|
+
* directory, a dereferencing copy, a dangling link, or a link into some other
|
|
166
|
+
* tree are all unauthorized and must keep preserving the worktree.
|
|
167
|
+
*/
|
|
168
|
+
function authenticateWarmLink(baseRepo, worktreePath, rel) {
|
|
169
|
+
const dest = path.join(worktreePath, ...rel.split('/'));
|
|
170
|
+
try {
|
|
171
|
+
if (!fs.lstatSync(dest).isSymbolicLink())
|
|
172
|
+
return null;
|
|
173
|
+
const target = path.resolve(path.dirname(dest), fs.readlinkSync(dest));
|
|
174
|
+
const expected = path.join(baseRepo, ...rel.split('/'));
|
|
175
|
+
if (resolveRealPath(target) !== resolveRealPath(expected))
|
|
176
|
+
return null;
|
|
177
|
+
// A link to something that is not a live directory proves nothing about the
|
|
178
|
+
// base checkout's dependency tree.
|
|
179
|
+
if (!isDir(target))
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
const fingerprint = (0, worktree_overlay_1.fingerprintOverlayCleanupPath)(dest);
|
|
186
|
+
return fingerprint ? { path: rel, ...fingerprint } : null;
|
|
187
|
+
}
|
|
188
|
+
/** Worktree-relative POSIX paths of every `node_modules` entry the worktree
|
|
189
|
+
* holds at depth ≤ MAX_DEPTH. Mirrors the linker's own discovery depth, so it
|
|
190
|
+
* can only ever see paths this module could have created. Recursion uses lstat
|
|
191
|
+
* so a symlinked directory is never walked into. */
|
|
192
|
+
function discoverWorktreeDependencyPaths(worktreePath) {
|
|
193
|
+
const found = [];
|
|
194
|
+
const walk = (rel, depth) => {
|
|
195
|
+
const abs = rel === '' ? worktreePath : path.join(worktreePath, rel);
|
|
196
|
+
let names;
|
|
197
|
+
try {
|
|
198
|
+
names = fs.readdirSync(abs);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
for (const name of names) {
|
|
204
|
+
const childRel = rel === '' ? name : `${rel}/${name}`;
|
|
205
|
+
if (name === DEPS_DIR) {
|
|
206
|
+
found.push(childRel);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (depth >= MAX_DEPTH || name.startsWith('.'))
|
|
210
|
+
continue;
|
|
211
|
+
try {
|
|
212
|
+
if (!fs.lstatSync(path.join(abs, name)).isDirectory())
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
walk(childRel, depth + 1);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
walk('', 0);
|
|
222
|
+
return found;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Live authentication of the warm-dependency links a worktree currently holds.
|
|
226
|
+
*
|
|
227
|
+
* Deriving this from the filesystem (instead of only from persisted evidence)
|
|
228
|
+
* is what heals worktrees that settled BEFORE the links carried evidence: they
|
|
229
|
+
* are stuck at `needs-review` with the link as their only "dirt", and a
|
|
230
|
+
* persisted-only fix would never reach them. The proof is identical either way
|
|
231
|
+
* — the link target anchored to a directory the app controls.
|
|
232
|
+
*/
|
|
233
|
+
function authenticateWarmNodeModulesLinks(baseRepo, worktreePath) {
|
|
234
|
+
const evidence = [];
|
|
235
|
+
for (const rel of discoverWorktreeDependencyPaths(worktreePath)) {
|
|
236
|
+
const entry = authenticateWarmLink(baseRepo, worktreePath, rel);
|
|
237
|
+
if (entry)
|
|
238
|
+
evidence.push(entry);
|
|
239
|
+
}
|
|
240
|
+
return evidence;
|
|
241
|
+
}
|
|
130
242
|
/**
|
|
131
243
|
* Link the base checkout's installed `node_modules` trees into a fresh
|
|
132
244
|
* worktree. Best-effort and side-effect-transparent: never throws, never
|
|
133
|
-
* overwrites, returns what it created
|
|
134
|
-
*
|
|
245
|
+
* overwrites, and returns both what it created and what it can PROVE is a warm
|
|
246
|
+
* link (including links a previous pass created), so the caller can exclude
|
|
247
|
+
* those paths from commits and authorize them for release.
|
|
135
248
|
*/
|
|
136
249
|
function linkNodeModulesIntoWorktree(baseRepo, worktreePath) {
|
|
137
|
-
const result = { linked: [], warnings: [] };
|
|
250
|
+
const result = { linked: [], authenticated: [], evidence: [], warnings: [] };
|
|
138
251
|
if (!isWorktreeNodeModulesEnabled())
|
|
139
252
|
return result;
|
|
140
253
|
for (const pkgRel of discoverPackageDirs(baseRepo)) {
|
|
141
|
-
const rel = pkgRel === '' ?
|
|
254
|
+
const rel = pkgRel === '' ? DEPS_DIR : `${pkgRel}/${DEPS_DIR}`;
|
|
142
255
|
const src = path.join(baseRepo, ...rel.split('/'));
|
|
143
256
|
if (!isDir(src))
|
|
144
257
|
continue;
|
|
145
258
|
const dest = path.join(worktreePath, ...rel.split('/'));
|
|
146
|
-
if (exists(dest))
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
259
|
+
if (!exists(dest)) {
|
|
260
|
+
try {
|
|
261
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
262
|
+
fs.symlinkSync(src, dest, process.platform === 'win32' ? 'junction' : undefined);
|
|
263
|
+
result.linked.push(rel);
|
|
264
|
+
}
|
|
265
|
+
catch (err) {
|
|
266
|
+
result.warnings.push(`failed to link ${rel}: ${errMsg(err)}`);
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
152
269
|
}
|
|
153
|
-
|
|
154
|
-
|
|
270
|
+
// Authenticate what is actually on disk NOW — a link we just made, or one a
|
|
271
|
+
// previous pass made. An entry that cannot prove itself (a real dir, an
|
|
272
|
+
// agent-made install) is deliberately left unauthorized.
|
|
273
|
+
const entry = authenticateWarmLink(baseRepo, worktreePath, rel);
|
|
274
|
+
if (entry) {
|
|
275
|
+
result.authenticated.push(rel);
|
|
276
|
+
result.evidence.push(entry);
|
|
155
277
|
}
|
|
156
278
|
}
|
|
157
279
|
return result;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{o as r}from"./i18n-nJQvD5zJ.js";import{n as i,t as a}from"./createLucideIcon-Bjbbp_KS.js";import{t as o}from"./circle-x-CMYsN4ZH.js";import{t as s}from"./loader-circle-DcygOQpC.js";import{t as c}from"./zap-CHo3aCD1.js";import{r as l}from"./useSharedWebSocket-DK96ctb7.js";import{t as u}from"./api-B2M7CtDw.js";import{r as d}from"./useDesktop-B6Du2N83.js";import{Dt as f,Pt as p}from"./index-C_sdsSZt.js";var m=a(`ban`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M4.929 4.929 19.07 19.071`,key:`196cmz`}]]),h=e(t(),1);function g(e){let t=new Set;return e.filter(e=>{let n=`${e.type}:${e.jobId}`;return t.has(n)?!1:(t.add(n),!0)})}function _({activeProjectId:e,limit:t=50}){let[n,i]=(0,h.useState)([]),[a,o]=(0,h.useState)(!1),[s,c]=(0,h.useState)(!0),d=(0,h.useRef)(e);(0,h.useEffect)(()=>{d.current=e},[e]);let{registerHandler:f,unregisterHandler:p}=l();(0,h.useEffect)(()=>{if(!e){i([]),c(!0);return}let n=!1;o(!0),i([]),c(!0);async function r(){let e=u();try{let r=await fetch(`${e}/activity?limit=${t}`);if(!r.ok||n)return;let a=await r.json();if(n)return;i(a),c(a.length===t)}catch{}finally{n||o(!1)}}return r(),()=>{n=!0}},[e,t]);let m=(0,h.useCallback)(async()=>{if(a||!s)return;o(!0);let e=u(),n=d.current;try{i(r=>{let a=r[r.length-1];if(!a)return r;let s=encodeURIComponent(a.timestamp);return fetch(`${e}/activity?limit=${t}&before=${s}`).then(e=>e.json()).then(e=>{if(d.current!==n){o(!1);return}i(t=>g([...t,...e])),c(e.length===t),o(!1)}).catch(()=>o(!1)),r})}catch{o(!1)}},[a,s,t]),_=(0,h.useCallback)(e=>{let t=e,n=d.current;if(!(!t||typeof t.type!=`string`)&&!(t.projectId&&t.projectId!==n)){if(t.type===`queue`&&Array.isArray(t.jobs)){let e=[];for(let n of t.jobs){let t=n.status===`completed`?`job_completed`:n.status===`failed`?`job_failed`:n.status===`canceled`?`job_canceled`:`job_started`;e.push({id:`${t}:${n.id}`,type:t,jobId:n.id,jobCommand:n.command??``,timestamp:n.startedAt??new Date().toISOString(),summary:`${t.replace(`_`,` `)}: ${n.command??``}`,costUsd:null})}e.length>0&&i(t=>g([...e,...t]))}if(t.type===`phase`&&t.phase&&t.state&&t.timestamp){let e={id:`phase:${t.phase}:${t.state}:${t.timestamp}`,type:`job_started`,jobId:`phase-${t.phase}`,jobCommand:r.t(`activity:feed.phaseCommand`,{phase:t.phase,state:t.state}),timestamp:t.timestamp,summary:`Phase ${t.phase} is ${t.state}`,costUsd:null};i(t=>g([e,...t]))}}},[]);return(0,h.useLayoutEffect)(()=>(f(`activity`,_),()=>p(`activity`)),[_,f,p]),{items:n,loading:a,hasMore:s,loadMore:m}}var v=n();function y(e,t){let n=Date.now()-new Date(e).getTime(),r=Math.floor(n/1e3);if(r<60)return t(`feed.relativeTime.seconds`,{value:r});let i=Math.floor(r/60);if(i<60)return t(`feed.relativeTime.minutes`,{value:i});let a=Math.floor(i/60);return a<24?t(`feed.relativeTime.hours`,{value:a}):t(`feed.relativeTime.days`,{value:Math.floor(a/24)})}function b({type:e}){switch(e){case`job_completed`:return(0,v.jsx)(f,{className:`w-4 h-4 text-green-500 aurora-light:text-accent-success flex-shrink-0`});case`job_failed`:return(0,v.jsx)(o,{className:`w-4 h-4 text-red-500 aurora-light:text-destructive flex-shrink-0`});case`job_canceled`:return(0,v.jsx)(m,{className:`w-4 h-4 text-muted-foreground flex-shrink-0`});default:return(0,v.jsx)(c,{className:`w-4 h-4 text-blue-500 aurora-light:text-accent-info flex-shrink-0`})}}function x(e,t){switch(e){case`job_completed`:return t(`common:status.completed`);case`job_failed`:return t(`common:status.failed`);case`job_canceled`:return t(`common:status.canceled`);default:return t(`feed.typeStarted`)}}function S(e){switch(e){case`job_completed`:return`text-green-500 aurora-light:text-accent-success`;case`job_failed`:return`text-red-500 aurora-light:text-destructive`;case`job_canceled`:return`text-muted-foreground`;default:return`text-blue-500 aurora-light:text-accent-info`}}function C(){let{t:e}=i(`activity`),{activeProjectId:t}=d(),{items:n,loading:r,hasMore:a,loadMore:o}=_({activeProjectId:t}),c=(0,h.useRef)(null);return(0,h.useEffect)(()=>{let e=c.current;if(!e)return;let t=new IntersectionObserver(e=>{e[0].isIntersecting&&a&&!r&&o()},{threshold:.1});return t.observe(e),()=>t.disconnect()},[a,r,o]),(0,v.jsxs)(`div`,{className:`flex flex-col h-full overflow-hidden`,children:[(0,v.jsxs)(`div`,{className:`flex items-center gap-2 px-4 py-3 border-b border-border bg-background/50`,children:[(0,v.jsx)(p,{className:`w-4 h-4 text-muted-foreground`}),(0,v.jsx)(`h1`,{className:`text-sm font-medium`,children:e(`feed.title`)})]}),(0,v.jsxs)(`div`,{className:`flex-1 overflow-y-auto`,children:[r&&n.length===0?(0,v.jsx)(`div`,{className:`flex items-center justify-center h-32`,children:(0,v.jsx)(s,{className:`w-4 h-4 animate-spin text-muted-foreground`})}):n.length===0?(0,v.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-48 gap-2 text-muted-foreground`,children:[(0,v.jsx)(p,{className:`w-8 h-8 opacity-40`}),(0,v.jsx)(`p`,{className:`text-sm`,children:e(`feed.emptyTitle`)}),(0,v.jsx)(`p`,{className:`text-xs opacity-70`,children:e(`feed.emptyHint`)})]}):(0,v.jsx)(`ul`,{className:`divide-y divide-border/50`,children:n.map(t=>(0,v.jsxs)(`li`,{className:`flex items-center gap-3 px-4 py-2.5 hover:bg-accent/30 transition-colors`,children:[(0,v.jsx)(b,{type:t.type}),(0,v.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,v.jsx)(`p`,{className:`text-xs text-foreground truncate`,title:t.jobCommand,children:t.jobCommand}),(0,v.jsxs)(`div`,{className:`flex items-center gap-2 mt-0.5`,children:[(0,v.jsx)(`span`,{className:`text-xs font-medium ${S(t.type)}`,children:x(t.type,e)}),t.costUsd!=null&&(0,v.jsxs)(`span`,{className:`text-xs text-muted-foreground`,children:[`$`,t.costUsd.toFixed(4)]})]})]}),(0,v.jsx)(`span`,{className:`text-xs text-muted-foreground flex-shrink-0 tabular-nums`,children:y(t.timestamp,e)})]},`${t.type}:${t.jobId}`))}),(0,v.jsx)(`div`,{ref:c,className:`h-1`}),r&&n.length>0&&(0,v.jsx)(`div`,{className:`flex justify-center py-3`,children:(0,v.jsx)(s,{className:`w-4 h-4 animate-spin text-muted-foreground`})}),!a&&n.length>0&&(0,v.jsx)(`p`,{className:`text-center text-xs text-muted-foreground py-3`,children:e(`feed.allLoaded`)})]})]})}export{C as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{n as r}from"./createLucideIcon-Bjbbp_KS.js";import{n as i}from"./dist-bVqfYmby.js";import{D as a,i as o,l as s}from"./index-C_sdsSZt.js";var c=e(t(),1);function l(e,t){let n=e.indexOf(`,`);if(!e.startsWith(`data:`)||n===-1)throw Error(`dataUrlToFile: not a data URL`);let r=e.slice(0,n),i=/^data:([^;,]+)/.exec(r)?.[1]||`application/octet-stream`,a=e.slice(n+1);if(/;base64$/i.test(r)){let e=atob(a),n=new Uint8Array(e.length);for(let t=0;t<e.length;t++)n[t]=e.charCodeAt(t);return new File([n],t,{type:i})}return new File([decodeURIComponent(a)],t,{type:i})}var u=n();function d({projectId:e,conversationId:t}){let{t:n}=r(`agent`),{closeBrowser:d,queueCapture:f}=o(),p=(0,c.useMemo)(()=>`agent-${t??`home`}-${Math.round(performance.now())}`,[t]),m=async e=>{if(!t){i.error(n(`workspace.requiresConversation`));return}try{let r=await s(t,l(e.screenshotDataUrl,`capture-${Date.now()}.png`));f(r),i.success(n(`workspace.browserCaptured`)),d()}catch(e){i.error(n(`workspace.uploadFailed`),{description:e instanceof Error?e.message:void 0})}};return(0,u.jsx)(a,{open:!0,onClose:d,projectId:e,pendingSpecId:p,confirmLabel:n(`workspace.browserConfirm`),onCaptured:e=>{m(e)}})}export{d as AgentBrowserCapture};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/AnalyticsPage-C0LwYELz.js","assets/rolldown-runtime-CNC7AqOf.js","assets/jsx-runtime-zu2_FqZY.js","assets/chunk-KS7C4IRE-Cx5sqopX.js","assets/preload-helper-Czpn1I53.js","assets/i18n-nJQvD5zJ.js","assets/createLucideIcon-Bjbbp_KS.js","assets/activity-DiyjsgQV.js","assets/addspec-D2YIQ0GU.js","assets/agent-mTCsAbCl.js","assets/agents-ms7TwG16.js","assets/agentstudio-CN2pokQu.js","assets/aiedit-iIjz2_Qk.js","assets/analytics-BMzCvrCU.js","assets/attachments-B53PG733.js","assets/browser-CXUcnTMI.js","assets/builder-CHuahItu.js","assets/chat-BGD7MXAy.js","assets/code-CHPkOkDS.js","assets/commands-DyUZQ1FI.js","assets/common-DHsv9N5d.js","assets/dashboard-CTsM8x3m.js","assets/explore-DgBACn39.js","assets/integrations-mB-7zoyT.js","assets/jira-CKJg3HZd.js","assets/jobs-B_TeSQxb.js","assets/loops-CnxqL8IL.js","assets/mcp-uDkXYu4L.js","assets/nav-DjkmQJjd.js","assets/settings-CuVwOpe0.js","assets/setup-3jWaoVUO.js","assets/specs-DII0C8gw.js","assets/terminal-BFRZnMPP.js","assets/tickets-W6pBmbrX.js","assets/Trans-CTqw6rr9.js","assets/arrow-up-right-9DoMn7fD.js","assets/useSharedWebSocket-DK96ctb7.js","assets/auth-D_5bQv5W.js","assets/api-B2M7CtDw.js","assets/useDesktop-B6Du2N83.js","assets/dist-bVqfYmby.js","assets/react-dom-DiWzBDP9.js","assets/clsx-CjueKrWZ.js","assets/provider-capabilities-Doej9lfa.js","assets/formatDistanceToNow-BOuXgebD.js","assets/getTimezoneOffsetInMilliseconds-EjGOpn4b.js","assets/spending-BpYPsevQ.js","assets/index-C_sdsSZt.js","assets/arrow-left-CpIR4j7G.js","assets/useBrowserCaptureSession-BBXBN0Na.js","assets/MinimizedChatsContext-CHsu3NCX.js","assets/sparkles-DrY8hb0X.js","assets/x-CBMI9ejn.js","assets/external-link-BcU5EWZp.js","assets/chevron-right-CViMxucY.js","assets/TicketDetailModal-MhJzrqbi.js","assets/circle-x-CMYsN4ZH.js","assets/download-CFHyyHcw.js","assets/file-text-CFT-xXKN.js","assets/git-branch-BRM0F5GN.js","assets/loader-circle-DcygOQpC.js","assets/pencil-C9RkrIPZ.js","assets/play-DNlrawbS.js","assets/plus-CsnjfkmX.js","assets/save-28sIlCdr.js","assets/search-Cq_gE3Ik.js","assets/trash-2-BOU3WdZZ.js","assets/triangle-alert-H9T4jAmh.js","assets/zap-CHo3aCD1.js","assets/button-BUIIBQPT.js","assets/dist-Cez5CRkX.js","assets/utils-BC4DHlz2.js","assets/dialog-CzIekCw0.js","assets/jira-api-Bhixwn-d.js","assets/lib-CGdyxIWg.js","assets/index-DCN5QbDm.css","assets/ExportDropdown-BrtfWzEG.js","assets/BarChart-nBOMNfiy.js","assets/value-s-gzBeyF.js"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{t as r}from"./preload-helper-Czpn1I53.js";import{n as i}from"./createLucideIcon-Bjbbp_KS.js";import{t as a}from"./loader-circle-DcygOQpC.js";import{t as o}from"./x-CBMI9ejn.js";import{t as s}from"./utils-BC4DHlz2.js";import{dt as c,ft as l,i as u}from"./index-C_sdsSZt.js";var d=e(t(),1),f=n(),p=(0,d.lazy)(()=>r(()=>import(`./AnalyticsPage-C0LwYELz.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78]))),m=420,h=560;function g(){let{t:e}=i(`agent`),{closeAnalyticsPane:t}=u(),[n,r]=(0,d.useState)(h),[g,_]=(0,d.useState)(!1),v=(0,d.useCallback)(e=>{let t=e.clientX,i=n,a=window.innerWidth-m;try{e.currentTarget.setPointerCapture(e.pointerId)}catch{}let o=e=>{let n=Math.min(Math.max(i+(t-e.clientX),m),a);r(n)},s=()=>{window.removeEventListener(`pointermove`,o),window.removeEventListener(`pointerup`,s),window.removeEventListener(`pointercancel`,s)};window.addEventListener(`pointermove`,o),window.addEventListener(`pointerup`,s),window.addEventListener(`pointercancel`,s)},[n]);return(0,f.jsxs)(`div`,{className:s(`flex h-full flex-col overflow-hidden border-l border-border bg-background`,g&&`absolute inset-0 z-30 border-l-0`),style:g?void 0:{width:n,flexShrink:0},"data-testid":`agent-analytics-pane`,children:[(0,f.jsx)(`div`,{className:`relative flex items-center`,children:!g&&(0,f.jsx)(`div`,{role:`separator`,"aria-orientation":`vertical`,onPointerDown:v,className:`absolute left-0 top-0 h-full w-1.5 -translate-x-full cursor-col-resize select-none touch-none hover:bg-accent-primary/20`,title:e(`workspace.resizeAnalytics`)})}),(0,f.jsxs)(`div`,{className:`flex items-center gap-2 border-b border-border/60 bg-surface/40 px-3 py-1.5`,children:[(0,f.jsx)(`span`,{className:`text-xs font-medium text-foreground/70`,children:e(`workspace.analytics`)}),(0,f.jsxs)(`div`,{className:`ml-auto flex items-center gap-1`,children:[(0,f.jsx)(`button`,{type:`button`,onClick:()=>_(e=>!e),"aria-label":e(g?`restore`:`maximize`),title:e(g?`restore`:`maximize`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:g?(0,f.jsx)(c,{className:`h-4 w-4`}):(0,f.jsx)(l,{className:`h-4 w-4`})}),(0,f.jsx)(`button`,{type:`button`,onClick:t,"aria-label":e(`workspace.closeAnalytics`),title:e(`workspace.closeAnalytics`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:(0,f.jsx)(o,{className:`h-4 w-4`})})]})]}),(0,f.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto`,children:(0,f.jsx)(d.Suspense,{fallback:(0,f.jsx)(`div`,{className:`flex h-24 items-center justify-center text-muted-foreground`,children:(0,f.jsx)(a,{className:`h-4 w-4 animate-spin`})}),children:(0,f.jsx)(p,{})})})]})}export{g as AgentModeAnalyticsPane};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/CodePage-RtKvHmAO.js","assets/rolldown-runtime-CNC7AqOf.js","assets/jsx-runtime-zu2_FqZY.js","assets/react-dom-DiWzBDP9.js","assets/preload-helper-Czpn1I53.js","assets/chunk-KS7C4IRE-Cx5sqopX.js","assets/createLucideIcon-Bjbbp_KS.js","assets/dist-bVqfYmby.js","assets/useBrowserCaptureSession-BBXBN0Na.js","assets/i18n-nJQvD5zJ.js","assets/activity-DiyjsgQV.js","assets/addspec-D2YIQ0GU.js","assets/agent-mTCsAbCl.js","assets/agents-ms7TwG16.js","assets/agentstudio-CN2pokQu.js","assets/aiedit-iIjz2_Qk.js","assets/analytics-BMzCvrCU.js","assets/attachments-B53PG733.js","assets/browser-CXUcnTMI.js","assets/builder-CHuahItu.js","assets/chat-BGD7MXAy.js","assets/code-CHPkOkDS.js","assets/commands-DyUZQ1FI.js","assets/common-DHsv9N5d.js","assets/dashboard-CTsM8x3m.js","assets/explore-DgBACn39.js","assets/integrations-mB-7zoyT.js","assets/jira-CKJg3HZd.js","assets/jobs-B_TeSQxb.js","assets/loops-CnxqL8IL.js","assets/mcp-uDkXYu4L.js","assets/nav-DjkmQJjd.js","assets/settings-CuVwOpe0.js","assets/setup-3jWaoVUO.js","assets/specs-DII0C8gw.js","assets/terminal-BFRZnMPP.js","assets/tickets-W6pBmbrX.js","assets/auth-D_5bQv5W.js","assets/api-B2M7CtDw.js","assets/MinimizedChatsContext-CHsu3NCX.js","assets/sparkles-DrY8hb0X.js","assets/x-CBMI9ejn.js","assets/useSharedWebSocket-DK96ctb7.js","assets/useDesktop-B6Du2N83.js","assets/external-link-BcU5EWZp.js","assets/chevron-right-CViMxucY.js","assets/TicketDetailModal-MhJzrqbi.js","assets/Trans-CTqw6rr9.js","assets/arrow-left-CpIR4j7G.js","assets/circle-x-CMYsN4ZH.js","assets/download-CFHyyHcw.js","assets/file-text-CFT-xXKN.js","assets/git-branch-BRM0F5GN.js","assets/loader-circle-DcygOQpC.js","assets/pencil-C9RkrIPZ.js","assets/play-DNlrawbS.js","assets/plus-CsnjfkmX.js","assets/save-28sIlCdr.js","assets/search-Cq_gE3Ik.js","assets/trash-2-BOU3WdZZ.js","assets/triangle-alert-H9T4jAmh.js","assets/zap-CHo3aCD1.js","assets/button-BUIIBQPT.js","assets/dist-Cez5CRkX.js","assets/clsx-CjueKrWZ.js","assets/utils-BC4DHlz2.js","assets/dialog-CzIekCw0.js","assets/jira-api-Bhixwn-d.js","assets/provider-capabilities-Doej9lfa.js","assets/lib-CGdyxIWg.js","assets/formatDistanceToNow-BOuXgebD.js","assets/getTimezoneOffsetInMilliseconds-EjGOpn4b.js","assets/spending-BpYPsevQ.js","assets/index-C_sdsSZt.js","assets/arrow-up-right-9DoMn7fD.js","assets/index-DCN5QbDm.css"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{t as r}from"./preload-helper-Czpn1I53.js";import{n as i}from"./createLucideIcon-Bjbbp_KS.js";import{t as a}from"./x-CBMI9ejn.js";import{dt as o,ft as s,i as c}from"./index-C_sdsSZt.js";var l=e(t(),1),u=n(),d=(0,l.lazy)(()=>r(()=>import(`./CodePage-RtKvHmAO.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75]))),f=new Map,p=420,m=560;function h({projectId:e,conversationId:t}){let{t:n}=i(`agent`),{closeCodePane:r}=c(),[h,g]=(0,l.useState)(m),[_,v]=(0,l.useState)(!1),y=(0,l.useRef)(null),b=`${e}:${t}`,x=(0,l.useCallback)(e=>{f.set(b,e)},[b]),S=(0,l.useCallback)(e=>{let t=e.clientX,n=h,r=window.innerWidth-p;try{e.currentTarget.setPointerCapture(e.pointerId)}catch{}let i=e=>{let i=Math.min(Math.max(n+(t-e.clientX),p),r);g(i)},a=()=>{window.removeEventListener(`pointermove`,i),window.removeEventListener(`pointerup`,a),window.removeEventListener(`pointercancel`,a)};window.addEventListener(`pointermove`,i),window.addEventListener(`pointerup`,a),window.addEventListener(`pointercancel`,a)},[h]);return(0,u.jsxs)(`div`,{ref:y,className:`flex h-full flex-col overflow-hidden border-l border-border bg-background${_?` absolute inset-0 z-30 border-l-0`:``}`,style:_?void 0:{width:h,flexShrink:0},children:[(0,u.jsx)(`div`,{className:`relative flex items-center`,children:!_&&(0,u.jsx)(`div`,{role:`separator`,"aria-orientation":`vertical`,onPointerDown:S,className:`absolute left-0 top-0 h-full w-1.5 -translate-x-full cursor-col-resize select-none touch-none hover:bg-accent-primary/20`,title:n(`workspace.resizeCode`)})}),(0,u.jsxs)(`div`,{className:`flex items-center gap-2 border-b border-border/60 bg-surface/40 px-3 py-1.5`,children:[(0,u.jsx)(`span`,{className:`text-xs font-medium text-foreground/70`,children:n(`workspace.files`)}),(0,u.jsxs)(`div`,{className:`ml-auto flex items-center gap-1`,children:[(0,u.jsx)(`button`,{type:`button`,onClick:()=>v(e=>!e),"aria-label":n(_?`restore`:`maximize`),title:n(_?`restore`:`maximize`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:_?(0,u.jsx)(o,{className:`h-4 w-4`}):(0,u.jsx)(s,{className:`h-4 w-4`})}),(0,u.jsx)(`button`,{type:`button`,onClick:r,"aria-label":n(`workspace.closeFiles`),title:n(`workspace.closeFiles`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:(0,u.jsx)(a,{className:`h-4 w-4`})})]})]}),(0,u.jsx)(`div`,{className:`min-h-0 flex-1 overflow-hidden`,children:(0,u.jsx)(l.Suspense,{fallback:(0,u.jsx)(`div`,{className:`flex h-full items-center justify-center text-sm text-muted-foreground`,children:`…`}),children:(0,u.jsx)(d,{embedded:!0,initialPath:f.get(b)??null,onSelectedPathChange:x})})})]},b)}export{h as AgentModeCodePane};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{a as r}from"./i18n-nJQvD5zJ.js";import{n as i}from"./createLucideIcon-Bjbbp_KS.js";import{t as a}from"./loader-circle-DcygOQpC.js";import{t as o}from"./x-CBMI9ejn.js";import{r as s}from"./useSharedWebSocket-DK96ctb7.js";import{t as c}from"./api-B2M7CtDw.js";import{i as l,r as u}from"./useDesktop-B6Du2N83.js";import{t as d}from"./utils-BC4DHlz2.js";import{t as f}from"./formatDistanceToNow-BOuXgebD.js";import{At as p,I as m,dt as h,ft as g,i as _}from"./index-C_sdsSZt.js";import{n as v,t as y}from"./job-time-BCL7wee9.js";import{JobDetailModal as b}from"./JobDetailModal-1UZkqH4I.js";import{t as x}from"./format-command-C0D9IPmV.js";var S=e(t(),1),C=n(),w=360,T=480,E={running:`running`,completed:`success`,failed:`failed`,canceled:`canceled`,queued:`queued`};function D({projectId:e}){let{t}=i(`agent`),{t:n}=i(`jobs`),{closeJobsPane:D}=_(),{projects:O}=u(),k=O.find(t=>t.id===e)?.provider,[A,j]=(0,S.useState)(T),[M,N]=(0,S.useState)(!1),[P,F]=(0,S.useState)(null),{data:I,isFirstLoad:L,refresh:R}=l({namespace:`agent-jobs`,projectId:e,initialValue:[],fetcher:async()=>{let e=await fetch(`${c()}/jobs?limit=50`);return e.ok?(await e.json()).jobs:[]},pollInterval:5e3}),{registerHandler:z,unregisterHandler:B}=s(),V=(0,S.useRef)(e);V.current=e;let H=(0,S.useRef)(R);H.current=R,(0,S.useEffect)(()=>(z(`agent-jobs-pane`,e=>{let t=e;t.type===`queue`&&(t.projectId&&t.projectId!==V.current||H.current())}),()=>B(`agent-jobs-pane`)),[z,B]);let U=(0,S.useCallback)(e=>{let t=e.clientX,n=A,r=window.innerWidth-w;try{e.currentTarget.setPointerCapture(e.pointerId)}catch{}let i=e=>{let i=Math.min(Math.max(n+(t-e.clientX),w),r);j(i)},a=()=>{window.removeEventListener(`pointermove`,i),window.removeEventListener(`pointerup`,a),window.removeEventListener(`pointercancel`,a)};window.addEventListener(`pointermove`,i),window.addEventListener(`pointerup`,a),window.addEventListener(`pointercancel`,a)},[A]);return(0,C.jsxs)(`div`,{className:d(`flex h-full flex-col overflow-hidden border-l border-border bg-background`,M&&`absolute inset-0 z-30 border-l-0`),style:M?void 0:{width:A,flexShrink:0},children:[(0,C.jsx)(`div`,{className:`relative flex items-center`,children:!M&&(0,C.jsx)(`div`,{role:`separator`,"aria-orientation":`vertical`,onPointerDown:U,className:`absolute left-0 top-0 h-full w-1.5 -translate-x-full cursor-col-resize select-none touch-none hover:bg-accent-primary/20`,title:t(`workspace.resizeJobs`)})}),(0,C.jsxs)(`div`,{className:`flex items-center gap-2 border-b border-border/60 bg-surface/40 px-3 py-1.5`,children:[(0,C.jsx)(`span`,{className:`text-xs font-medium text-foreground/70`,children:t(`workspace.jobs`)}),(0,C.jsxs)(`div`,{className:`ml-auto flex items-center gap-1`,children:[(0,C.jsx)(`button`,{type:`button`,onClick:()=>N(e=>!e),"aria-label":t(M?`restore`:`maximize`),title:t(M?`restore`:`maximize`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:M?(0,C.jsx)(h,{className:`h-4 w-4`}):(0,C.jsx)(g,{className:`h-4 w-4`})}),(0,C.jsx)(`button`,{type:`button`,onClick:D,"aria-label":t(`workspace.closeJobs`),title:t(`workspace.closeJobs`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:(0,C.jsx)(o,{className:`h-4 w-4`})})]})]}),(0,C.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto p-2`,children:L?(0,C.jsx)(`div`,{className:`flex h-24 items-center justify-center text-muted-foreground`,children:(0,C.jsx)(a,{className:`h-4 w-4 animate-spin`})}):I.length===0?(0,C.jsxs)(`div`,{className:`flex h-full flex-col items-center justify-center gap-2 text-center text-muted-foreground`,children:[(0,C.jsx)(p,{className:`h-6 w-6 opacity-40`}),(0,C.jsx)(`p`,{className:`text-xs`,children:t(`workspace.noJobs`)})]}):(0,C.jsx)(`ul`,{className:`space-y-1`,children:I.map(e=>(0,C.jsx)(`li`,{children:(0,C.jsxs)(`button`,{type:`button`,onClick:()=>F(e.id),className:d(`flex w-full flex-col gap-1 rounded-lg border border-border/50 bg-surface/30 px-2.5 py-2 text-left transition-colors`,`hover:border-accent-primary/40 hover:bg-surface/60`),children:[(0,C.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,C.jsx)(m,{variant:E[e.status]??`queued`,children:n(`statusLabel.${e.status in E?e.status:`queued`}`)}),e.status===`running`&&(0,C.jsx)(a,{className:`h-3 w-3 animate-spin text-accent-primary`}),(0,C.jsx)(`span`,{className:`ml-auto text-[10px] text-muted-foreground`,children:(()=>{let t=v(y(e));return t?f(t,{addSuffix:!0,locale:r()}):`—`})()})]}),(0,C.jsx)(`code`,{className:`truncate font-mono text-xs text-foreground/85`,children:x(e.command,k)})]})},e.id))})}),P&&(0,C.jsx)(b,{jobId:P,onClose:()=>F(null)})]})}export{D as AgentModeJobsPane};
|