specrails-desktop 2.62.0 → 2.63.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/cli/dist/specrails-desktop.js +39 -5
- package/client/dist/assets/{ActivityFeedPage-C_pqc281.js → ActivityFeedPage-Bn2MpG5M.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-vz6rbDtu.js → AgentBrowserCapture-CHlXWdXq.js} +1 -1
- package/client/dist/assets/AgentModeAnalyticsPane-yj-YCgrK.js +2 -0
- package/client/dist/assets/AgentModeCodePane-Xczcyghb.js +2 -0
- package/client/dist/assets/{AgentModeJobsPane-99fBBLri.js → AgentModeJobsPane-DtOW01ln.js} +1 -1
- package/client/dist/assets/AgentsPage-BMfJ4mzU.js +118 -0
- package/client/dist/assets/AnalyticsPage-CGn5cgtc.js +1 -0
- package/client/dist/assets/BarChart-BDMqVFJy.js +55 -0
- package/client/dist/assets/CodePage-C8NIginr.js +3 -0
- package/client/dist/assets/DesktopAnalyticsPage-C87r4y26.js +1 -0
- package/client/dist/assets/{DocsDialog-XUCYKoq0.js → DocsDialog-Z50a_KPv.js} +1 -1
- package/client/dist/assets/{DocsPage-dOLC8bXA.js → DocsPage-DIArbEPl.js} +1 -1
- package/client/dist/assets/{ExportDropdown-BvKQgwM9.js → ExportDropdown-CJdCqYd7.js} +1 -1
- package/client/dist/assets/IntegrationsPage-EAipiDWm.js +1 -0
- package/client/dist/assets/InteractiveJobComposer-BxLh4me-.js +19 -0
- package/client/dist/assets/{JobDetailModal-CkiubrkZ.js → JobDetailModal-C7dWoRu6.js} +1 -1
- package/client/dist/assets/{JobDetailPage-CNe4Wn7l.js → JobDetailPage-Ilu294WS.js} +1 -1
- package/client/dist/assets/JobsPage-fPq1hefb.js +1 -0
- package/client/dist/assets/LoopBuilderPage-5M7IypEP.js +1 -0
- package/client/dist/assets/{LoopPreviewModal-Dv3dxHr_.js → LoopPreviewModal-ChI3NQC7.js} +1 -1
- package/client/dist/assets/{LoopWindowSurface-DN2lbSnL.js → LoopWindowSurface-D1WB9i-9.js} +1 -1
- package/client/dist/assets/LoopsPage-CCwc0Biu.js +2 -0
- package/client/dist/assets/MinimizedChatsContext-eH_KaAZ1.js +1 -0
- package/client/dist/assets/PluginsPage-DfDfEfbW.js +2 -0
- package/client/dist/assets/ProjectSettingsDialog-9NHnG8JQ.js +1 -0
- package/client/dist/assets/RepositoryDeliveries-DgzNnJmn.js +5 -0
- package/client/dist/assets/{RepositoryScopeSelector-DrtPqvc_.js → RepositoryScopeSelector-BSgUqk_u.js} +1 -1
- package/client/dist/assets/ReviewPacketPage-D34bGpSA.js +4 -0
- package/client/dist/assets/{TemplatePreviewModal-DcFlhUxy.js → TemplatePreviewModal-CcfSxWQ7.js} +1 -1
- package/client/dist/assets/TicketDetailModalContext-BoafEcm4.js +16 -0
- package/client/dist/assets/Trans-CVSryAL1.js +1 -0
- package/client/dist/assets/ban-wqfdzoXt.js +1 -0
- package/client/dist/assets/{button-D1xExSTF.js → button-4FS1ZDTp.js} +1 -1
- package/client/dist/assets/chevron-up-6EFqEHBj.js +1 -0
- package/client/dist/assets/chunk-OB3PAWPO-B1EMk45A.js +3 -0
- package/client/dist/assets/circle-x-D2ttKotB.js +1 -0
- package/client/dist/assets/createLucideIcon-DtODIU64.js +1 -0
- package/client/dist/assets/{dialog-CMgps2MY.js → dialog-DtdR05v1.js} +1 -1
- package/client/dist/assets/download-C9wP_42h.js +1 -0
- package/client/dist/assets/external-link-CZ6Q-v1-.js +1 -0
- package/client/dist/assets/file-text-C0blZHLh.js +1 -0
- package/client/dist/assets/{formatDistanceToNow-DlQY1Lm_.js → formatDistanceToNow-NVM6rK3i.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-COUeq65G.js → getTimezoneOffsetInMilliseconds-ZHg7WpdH.js} +1 -1
- package/client/dist/assets/git-branch-yf6LE1Bw.js +1 -0
- package/client/dist/assets/{index-CONHvEJw.js → index-Culan6y7.js} +40 -40
- package/client/dist/assets/{index-BacrO_ub.css → index-D1v33giW.css} +1 -1
- package/client/dist/assets/jira-api-Bl8xEIpk.js +1 -0
- package/client/dist/assets/jsx-runtime-BbYvbtY8.js +1 -0
- package/client/dist/assets/layers-BCLTgHXO.js +1 -0
- package/client/dist/assets/{lib-C7Mhm-gT.js → lib-CQJT8S6Q.js} +1 -1
- package/client/dist/assets/{lib-CGdyxIWg.js → lib-DWJfei6y.js} +1 -1
- package/client/dist/assets/loader-circle-BqV9xYGf.js +1 -0
- package/client/dist/assets/{loop-layout-BF8xMiNv.js → loop-layout-CyXj9223.js} +1 -1
- package/client/dist/assets/pencil-CxrRbDwb.js +1 -0
- package/client/dist/assets/plus-fHDdjBk1.js +1 -0
- package/client/dist/assets/{project-repositories-Cc3oNv8X.js → project-repositories-CvJf1a6H.js} +1 -1
- package/client/dist/assets/react-dom-DDD8FYaU.js +1 -0
- package/client/dist/assets/rotate-ccw-DalJAQQX.js +1 -0
- package/client/dist/assets/save-LmPSfsvZ.js +1 -0
- package/client/dist/assets/search-BDVp3fkH.js +1 -0
- package/client/dist/assets/{settings-BKJZ2KEM.js → settings-BO2N-oPf.js} +1 -1
- package/client/dist/assets/{settings-LfFnl1UH.js → settings-BQChAs_w.js} +1 -1
- package/client/dist/assets/{settings-BV3jocIx.js → settings-BzmYlcQB.js} +1 -1
- package/client/dist/assets/{settings-BjM2Dw8_.js → settings-CGjXb6TX.js} +1 -1
- package/client/dist/assets/{settings-DA77M48m.js → settings-CMxEWY46.js} +1 -1
- package/client/dist/assets/{settings-P1IQvhnL.js → settings-C_dRSgI2.js} +1 -1
- package/client/dist/assets/{settings-eIMJjyK_.js → settings-Hw7av-B1.js} +1 -1
- package/client/dist/assets/{settings-CKu0LTib.js → settings-nryN6wuS.js} +1 -1
- package/client/dist/assets/sparkles-CCfUdFGY.js +1 -0
- package/client/dist/assets/spending-BfjSJjBd.js +1 -0
- package/client/dist/assets/square-D0JTbRSR.js +1 -0
- package/client/dist/assets/trash-D8zYUmTg.js +1 -0
- package/client/dist/assets/triangle-alert-Cxpn8bqr.js +1 -0
- package/client/dist/assets/upload-CCHvUkRs.js +1 -0
- package/client/dist/assets/{useDesktop-9Lwoel_8.js → useDesktop-C5JVBeur.js} +1 -1
- package/client/dist/assets/{useRuntimeRuns-DL0xYoZ2.js → useRuntimeRuns-DohBmeg8.js} +1 -1
- package/client/dist/assets/useSharedWebSocket-CNOvmgTr.js +2 -0
- package/client/dist/assets/{value-s-gzBeyF.js → value-DtOOK0wQ.js} +1 -1
- package/client/dist/assets/x-Bu7X_eR2.js +1 -0
- package/client/dist/assets/zap-bugHyWzQ.js +1 -0
- package/client/dist/index.html +43 -44
- package/docs/ci-cd.md +8 -1
- package/docs/customizing.md +1 -0
- package/docs/getting-started.md +1 -1
- package/docs/guide/de/settings/5-project-environment.md +27 -0
- package/docs/guide/en/settings/5-project-environment.md +27 -0
- package/docs/guide/es/settings/5-project-environment.md +27 -0
- package/docs/guide/fr/settings/5-project-environment.md +27 -0
- package/docs/guide/it/settings/5-project-environment.md +27 -0
- package/docs/guide/ja/settings/5-project-environment.md +27 -0
- package/docs/guide/pt/settings/5-project-environment.md +27 -0
- package/docs/guide/zh/settings/5-project-environment.md +27 -0
- package/docs/internals/api-reference.md +3 -1
- package/docs/internals/configuration.md +46 -5
- package/docs/internals/safe-pr-review-flow.md +15 -0
- package/docs/internals/source-map.md +3 -1
- package/docs/mcp.md +1 -1
- package/docs/platforms/macos.md +2 -0
- package/docs/platforms/windows-parity.md +4 -4
- package/docs/platforms/windows.md +9 -9
- package/local-runner/dist/specrails-local-runner.js +1 -1
- package/package.json +8 -9
- package/server/dist/mcp/tools/env.js +38 -3
- package/server/dist/modules/execution/runtime/queue-manager.js +8 -0
- package/server/dist/modules/loops/runtime/loop-executors.js +1 -0
- package/server/dist/modules/loops/runtime/loop-run-manager.js +7 -0
- package/server/dist/modules/project-settings/adapters/http.js +24 -0
- package/server/dist/path-resolver.js +135 -20
- package/server/dist/project-env.js +265 -17
- package/server/dist/project-registry.js +14 -0
- package/server/dist/project-router-settings.js +17 -0
- package/server/dist/worktree-node-modules.js +95 -1
- package/client/dist/assets/AgentModeAnalyticsPane-BHCcUahS.js +0 -2
- package/client/dist/assets/AgentModeCodePane-DhmxgG3W.js +0 -2
- package/client/dist/assets/AgentsPage-CTwm_6T8.js +0 -118
- package/client/dist/assets/AnalyticsPage-YD8MLoPa.js +0 -1
- package/client/dist/assets/BarChart-nBOMNfiy.js +0 -33
- package/client/dist/assets/CodePage-CLJn2F2e.js +0 -3
- package/client/dist/assets/DesktopAnalyticsPage-MWGQVbwp.js +0 -1
- package/client/dist/assets/IntegrationsPage-Bhy58lnQ.js +0 -1
- package/client/dist/assets/InteractiveJobComposer-DxOvwmBF.js +0 -19
- package/client/dist/assets/JobsPage-DSts_L_d.js +0 -1
- package/client/dist/assets/LoopBuilderPage-C9D9eer5.js +0 -1
- package/client/dist/assets/LoopsPage-DHgIreEW.js +0 -2
- package/client/dist/assets/MinimizedChatsContext-Ds2-R6qJ.js +0 -1
- package/client/dist/assets/PluginsPage-D1EJ9oja.js +0 -2
- package/client/dist/assets/ProjectSettingsDialog-DKpa1wKk.js +0 -1
- package/client/dist/assets/RepositoryDeliveries-CW4eKG3x.js +0 -5
- package/client/dist/assets/ReviewPacketPage-BtjPI0Cc.js +0 -4
- package/client/dist/assets/TicketDetailModalContext-Dea22CI-.js +0 -16
- package/client/dist/assets/Trans-CAY2Rd2f.js +0 -1
- package/client/dist/assets/ban-E4EaZe3a.js +0 -1
- package/client/dist/assets/chevron-up-CwGUHd6D.js +0 -1
- package/client/dist/assets/chunk-KS7C4IRE-DEKu087k.js +0 -3
- package/client/dist/assets/circle-x-CMYsN4ZH.js +0 -1
- package/client/dist/assets/createLucideIcon-Bjbbp_KS.js +0 -1
- package/client/dist/assets/dist-Bq10nS2m.js +0 -1
- package/client/dist/assets/download-CFHyyHcw.js +0 -1
- package/client/dist/assets/external-link-D6XxC_Cu.js +0 -1
- package/client/dist/assets/file-text-BcLCfcWZ.js +0 -1
- package/client/dist/assets/git-branch-BRM0F5GN.js +0 -1
- package/client/dist/assets/jira-api-C94VNHNY.js +0 -1
- package/client/dist/assets/jsx-runtime-zu2_FqZY.js +0 -1
- package/client/dist/assets/layers-eDMjG41I.js +0 -1
- package/client/dist/assets/loader-circle-DcygOQpC.js +0 -1
- package/client/dist/assets/pencil-C9RkrIPZ.js +0 -1
- package/client/dist/assets/plus-CsnjfkmX.js +0 -1
- package/client/dist/assets/react-dom-DiWzBDP9.js +0 -1
- package/client/dist/assets/rotate-ccw-Wh4id13q.js +0 -1
- package/client/dist/assets/save-B6wGWwBR.js +0 -1
- package/client/dist/assets/search-Cq_gE3Ik.js +0 -1
- package/client/dist/assets/sparkles-DrY8hb0X.js +0 -1
- package/client/dist/assets/spending-DemeFfrH.js +0 -1
- package/client/dist/assets/square-Ci-PFcFe.js +0 -1
- package/client/dist/assets/trash-2-BOU3WdZZ.js +0 -1
- package/client/dist/assets/triangle-alert-H9T4jAmh.js +0 -1
- package/client/dist/assets/upload-DYMjAQ6M.js +0 -1
- package/client/dist/assets/useSharedWebSocket-BEHPeU1Y.js +0 -2
- package/client/dist/assets/x-CBMI9ejn.js +0 -1
- package/client/dist/assets/zap-CHo3aCD1.js +0 -1
|
@@ -1,37 +1,237 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PROJECT_ENV_FAILURE_TTL_MS = exports.PROJECT_ENV_SUCCESS_TTL_MS = void 0;
|
|
4
|
+
exports.refreshProjectEnv = refreshProjectEnv;
|
|
5
|
+
exports.ensureProjectEnvFresh = ensureProjectEnvFresh;
|
|
6
|
+
exports.startProjectEnvWarmup = startProjectEnvWarmup;
|
|
7
|
+
exports.stopProjectEnvWarmup = stopProjectEnvWarmup;
|
|
3
8
|
exports.resolveWorktreeEnvPassthrough = resolveWorktreeEnvPassthrough;
|
|
4
9
|
exports.applyWorktreeEnvPassthrough = applyWorktreeEnvPassthrough;
|
|
10
|
+
exports.getProjectEnvStatus = getProjectEnvStatus;
|
|
11
|
+
exports.worktreeEnvPassthroughWarning = worktreeEnvPassthroughWarning;
|
|
5
12
|
const db_1 = require("./db");
|
|
6
13
|
const path_resolver_1 = require("./path-resolver");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
/** Successful recoveries stay valid for 10 minutes; the background refresh
|
|
15
|
+
* rotates them well inside that window. */
|
|
16
|
+
exports.PROJECT_ENV_SUCCESS_TTL_MS = 10 * 60_000;
|
|
17
|
+
/** Failed or empty lookups are retried after 30 seconds. */
|
|
18
|
+
exports.PROJECT_ENV_FAILURE_TTL_MS = 30_000;
|
|
19
|
+
/** The background refresh runs this long before a success expires. */
|
|
20
|
+
const PROJECT_ENV_REFRESH_LEAD_MS = 60_000;
|
|
21
|
+
const states = new WeakMap();
|
|
22
|
+
function shellIdentity() {
|
|
23
|
+
return JSON.stringify([process.env.SHELL, process.env.HOME, process.env.ZDOTDIR]);
|
|
24
|
+
}
|
|
25
|
+
function stateFor(db) {
|
|
26
|
+
const identity = shellIdentity();
|
|
27
|
+
let state = states.get(db);
|
|
28
|
+
if (!state) {
|
|
29
|
+
state = { identity, records: new Map(), values: new Map(), inflight: null, queued: null, timer: null, active: false };
|
|
30
|
+
states.set(db, state);
|
|
31
|
+
}
|
|
32
|
+
else if (state.identity !== identity) {
|
|
33
|
+
state.identity = identity;
|
|
34
|
+
state.records.clear();
|
|
35
|
+
state.values.clear();
|
|
36
|
+
}
|
|
37
|
+
return state;
|
|
38
|
+
}
|
|
39
|
+
function configuredNames(db) {
|
|
40
|
+
try {
|
|
41
|
+
return (0, db_1.getProjectSettings)(db).worktreeEnvPassthrough;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// A closed/unloaded project connection has no configured names.
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function missingNames(names) {
|
|
49
|
+
return names.filter(name => !process.env[name]);
|
|
50
|
+
}
|
|
51
|
+
/** Drop records for names no longer configured or now inherited. */
|
|
52
|
+
function prune(state, names) {
|
|
53
|
+
for (const name of [...state.records.keys()]) {
|
|
54
|
+
if (!names.includes(name) || process.env[name]) {
|
|
55
|
+
state.records.delete(name);
|
|
56
|
+
state.values.delete(name);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function isValid(record, now) {
|
|
61
|
+
return Boolean(record && now < record.expiresAt);
|
|
62
|
+
}
|
|
63
|
+
function statusFromProbe(probe, value) {
|
|
64
|
+
if (value)
|
|
65
|
+
return 'recovered';
|
|
66
|
+
if (probe.status === 'timeout')
|
|
67
|
+
return 'probe-timeout';
|
|
68
|
+
if (probe.status === 'failed')
|
|
69
|
+
return 'probe-failed';
|
|
70
|
+
// `ok` without a value, or `skipped` (Windows / no login-shell recovery).
|
|
71
|
+
return 'not-defined';
|
|
72
|
+
}
|
|
73
|
+
function recordProbe(state, probe, names, now) {
|
|
74
|
+
for (const name of names) {
|
|
75
|
+
const value = probe.values[name];
|
|
76
|
+
const status = statusFromProbe(probe, value);
|
|
77
|
+
const previous = state.records.get(name);
|
|
78
|
+
// A transient probe failure never discards a still-valid recovered value.
|
|
79
|
+
if (status !== 'recovered' && previous?.status === 'recovered' && isValid(previous, now) && probe.status !== 'ok')
|
|
80
|
+
continue;
|
|
81
|
+
state.records.set(name, {
|
|
82
|
+
status,
|
|
83
|
+
shell: probe.shell,
|
|
84
|
+
checkedAt: now,
|
|
85
|
+
expiresAt: now + (status === 'recovered' ? exports.PROJECT_ENV_SUCCESS_TTL_MS : exports.PROJECT_ENV_FAILURE_TTL_MS),
|
|
86
|
+
exitCode: probe.exitCode,
|
|
87
|
+
});
|
|
88
|
+
if (value)
|
|
89
|
+
state.values.set(name, value);
|
|
90
|
+
else
|
|
91
|
+
state.values.delete(name);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function scheduleRefresh(db, state) {
|
|
95
|
+
if (state.timer) {
|
|
96
|
+
clearTimeout(state.timer);
|
|
97
|
+
state.timer = null;
|
|
98
|
+
}
|
|
99
|
+
if (!state.active || states.get(db) !== state)
|
|
100
|
+
return;
|
|
101
|
+
let earliest = Infinity;
|
|
102
|
+
for (const record of state.records.values()) {
|
|
103
|
+
if (record.status === 'recovered')
|
|
104
|
+
earliest = Math.min(earliest, record.expiresAt);
|
|
105
|
+
}
|
|
106
|
+
if (earliest === Infinity)
|
|
107
|
+
return;
|
|
108
|
+
const now = Date.now();
|
|
109
|
+
const at = Math.max(now + exports.PROJECT_ENV_FAILURE_TTL_MS, earliest - PROJECT_ENV_REFRESH_LEAD_MS);
|
|
110
|
+
state.timer = setTimeout(() => {
|
|
111
|
+
state.timer = null;
|
|
112
|
+
if (state.active && states.get(db) === state)
|
|
113
|
+
void refreshProjectEnv(db);
|
|
114
|
+
}, at - now);
|
|
115
|
+
state.timer.unref?.();
|
|
116
|
+
}
|
|
117
|
+
/** Probe the login shell asynchronously for every configured name missing from
|
|
118
|
+
* the process environment and update this project's records. De-duplicated
|
|
119
|
+
* while a probe is in flight: a call during a probe resolves after one more
|
|
120
|
+
* probe that starts when the current one ends, so a settings change or an
|
|
121
|
+
* explicit recheck is never answered with stale results. Never rejects. */
|
|
122
|
+
function refreshProjectEnv(db) {
|
|
123
|
+
const state = stateFor(db);
|
|
124
|
+
if (state.inflight) {
|
|
125
|
+
// A request during a probe (settings change, recheck) gets a fresh probe
|
|
126
|
+
// after the current one; concurrent requests share that single follow-up.
|
|
127
|
+
if (!state.queued) {
|
|
128
|
+
state.queued = state.inflight.then(() => {
|
|
129
|
+
state.queued = null;
|
|
130
|
+
return refreshProjectEnv(db);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return state.queued;
|
|
134
|
+
}
|
|
135
|
+
const names = configuredNames(db);
|
|
136
|
+
prune(state, names);
|
|
137
|
+
const missing = missingNames(names);
|
|
138
|
+
if (missing.length === 0) {
|
|
139
|
+
scheduleRefresh(db, state);
|
|
140
|
+
return Promise.resolve();
|
|
141
|
+
}
|
|
142
|
+
const run = (0, path_resolver_1.readEnvFromLoginShell)(missing, { timeoutMs: (0, path_resolver_1.resolveLoginShellProbeTimeoutMs)() })
|
|
143
|
+
.then(probe => {
|
|
144
|
+
if (states.get(db) !== state)
|
|
145
|
+
return;
|
|
146
|
+
const current = configuredNames(db);
|
|
147
|
+
prune(state, current);
|
|
148
|
+
// Only names still configured and still missing are recorded.
|
|
149
|
+
const stillMissing = missingNames(current);
|
|
150
|
+
recordProbe(state, probe, missing.filter(name => stillMissing.includes(name)), Date.now());
|
|
151
|
+
})
|
|
152
|
+
.catch(() => { })
|
|
153
|
+
.finally(() => {
|
|
154
|
+
state.inflight = null;
|
|
155
|
+
scheduleRefresh(db, state);
|
|
156
|
+
});
|
|
157
|
+
state.inflight = run;
|
|
158
|
+
return run;
|
|
159
|
+
}
|
|
160
|
+
/** Probe only when a configured missing name has no valid record yet. */
|
|
161
|
+
function ensureProjectEnvFresh(db) {
|
|
162
|
+
const state = stateFor(db);
|
|
163
|
+
if (state.inflight)
|
|
164
|
+
return state.inflight;
|
|
165
|
+
const names = configuredNames(db);
|
|
166
|
+
prune(state, names);
|
|
167
|
+
const now = Date.now();
|
|
168
|
+
const stale = missingNames(names).some(name => !isValid(state.records.get(name), now));
|
|
169
|
+
return stale ? refreshProjectEnv(db) : Promise.resolve();
|
|
170
|
+
}
|
|
171
|
+
/** Start background warm-up for a loaded project: probe now, then refresh
|
|
172
|
+
* before successful results expire, until `stopProjectEnvWarmup`. */
|
|
173
|
+
function startProjectEnvWarmup(db) {
|
|
174
|
+
const state = stateFor(db);
|
|
175
|
+
state.active = true;
|
|
176
|
+
void refreshProjectEnv(db);
|
|
177
|
+
}
|
|
178
|
+
/** Stop the background refresh and forget this project's recovered values. */
|
|
179
|
+
function stopProjectEnvWarmup(db) {
|
|
180
|
+
const state = states.get(db);
|
|
181
|
+
if (!state)
|
|
182
|
+
return;
|
|
183
|
+
state.active = false;
|
|
184
|
+
if (state.timer) {
|
|
185
|
+
clearTimeout(state.timer);
|
|
186
|
+
state.timer = null;
|
|
187
|
+
}
|
|
188
|
+
states.delete(db);
|
|
189
|
+
}
|
|
11
190
|
function recoverProjectEnv(db, names) {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
191
|
+
const state = stateFor(db);
|
|
192
|
+
prune(state, names);
|
|
193
|
+
const missing = missingNames(names);
|
|
194
|
+
if (!missing.length)
|
|
15
195
|
return {};
|
|
196
|
+
const now = Date.now();
|
|
197
|
+
// Cold fallback: a name with no record (or an expired success) gets the
|
|
198
|
+
// short synchronous probe. Expired failures are retried asynchronously.
|
|
199
|
+
const cold = missing.filter(name => {
|
|
200
|
+
const record = state.records.get(name);
|
|
201
|
+
return !record || (record.status === 'recovered' && !isValid(record, now));
|
|
202
|
+
});
|
|
203
|
+
let retry = missing.some(name => {
|
|
204
|
+
const record = state.records.get(name);
|
|
205
|
+
return record !== undefined && record.status !== 'recovered' && !isValid(record, now);
|
|
206
|
+
});
|
|
207
|
+
if (cold.length) {
|
|
208
|
+
const probe = (0, path_resolver_1.readEnvFromLoginShellSyncDetailed)(cold);
|
|
209
|
+
recordProbe(state, probe, cold, now);
|
|
210
|
+
// The async probe has a larger budget than the cold sync one.
|
|
211
|
+
if (probe.status === 'timeout' || probe.status === 'failed')
|
|
212
|
+
retry = true;
|
|
213
|
+
}
|
|
214
|
+
if (retry)
|
|
215
|
+
void refreshProjectEnv(db);
|
|
216
|
+
const out = {};
|
|
217
|
+
for (const name of missing) {
|
|
218
|
+
const value = state.values.get(name);
|
|
219
|
+
if (value && isValid(state.records.get(name), now))
|
|
220
|
+
out[name] = value;
|
|
16
221
|
}
|
|
17
|
-
|
|
18
|
-
const cached = shellValues.get(db);
|
|
19
|
-
if (cached?.identity === identity && Date.now() < cached.expiresAt)
|
|
20
|
-
return cached.env;
|
|
21
|
-
const env = (0, path_resolver_1.readEnvFromLoginShellSync)(missing);
|
|
22
|
-
shellValues.set(db, { identity, expiresAt: Date.now() + LOGIN_SHELL_CACHE_MS, env });
|
|
23
|
-
return env;
|
|
222
|
+
return out;
|
|
24
223
|
}
|
|
25
224
|
/** Resolve the per-project env passthrough overlay for a spawn.
|
|
26
225
|
*
|
|
27
226
|
* The project setting stores names only. Values are read from `sourceEnv` at
|
|
28
227
|
* spawn time so credentials can rotate without touching SQLite. When the
|
|
29
|
-
* source is `process.env`, missing configured names
|
|
30
|
-
*
|
|
228
|
+
* source is `process.env`, missing configured names come from the project's
|
|
229
|
+
* warm login-shell cache; the short synchronous probe runs only when cold.
|
|
230
|
+
* Unresolved names remain absent.
|
|
31
231
|
*/
|
|
32
232
|
function resolveWorktreeEnvPassthrough(db, sourceEnv = process.env) {
|
|
33
233
|
const out = {};
|
|
34
|
-
const names = (
|
|
234
|
+
const names = configuredNames(db);
|
|
35
235
|
const recovered = sourceEnv === process.env ? recoverProjectEnv(db, names) : {};
|
|
36
236
|
for (const name of names) {
|
|
37
237
|
const value = sourceEnv[name] || recovered[name] || sourceEnv[name];
|
|
@@ -44,3 +244,51 @@ function applyWorktreeEnvPassthrough(db, env, sourceEnv = process.env) {
|
|
|
44
244
|
const passthrough = resolveWorktreeEnvPassthrough(db, sourceEnv);
|
|
45
245
|
return Object.keys(passthrough).length === 0 ? env : { ...env, ...passthrough };
|
|
46
246
|
}
|
|
247
|
+
/** Value-free status of every configured name for this project. */
|
|
248
|
+
function getProjectEnvStatus(db) {
|
|
249
|
+
const state = stateFor(db);
|
|
250
|
+
const names = configuredNames(db);
|
|
251
|
+
prune(state, names);
|
|
252
|
+
const nowIso = new Date(Date.now()).toISOString();
|
|
253
|
+
return {
|
|
254
|
+
loginShellRecovery: process.platform !== 'win32',
|
|
255
|
+
timeoutMs: (0, path_resolver_1.resolveLoginShellProbeTimeoutMs)(),
|
|
256
|
+
checking: state.inflight !== null,
|
|
257
|
+
names: names.map((name) => {
|
|
258
|
+
if (process.env[name])
|
|
259
|
+
return { name, status: 'inherited', shell: null, checkedAt: nowIso, exitCode: null };
|
|
260
|
+
const record = state.records.get(name);
|
|
261
|
+
if (!record)
|
|
262
|
+
return { name, status: 'pending', shell: null, checkedAt: null, exitCode: null };
|
|
263
|
+
return {
|
|
264
|
+
name,
|
|
265
|
+
status: record.status,
|
|
266
|
+
shell: record.shell,
|
|
267
|
+
checkedAt: new Date(record.checkedAt).toISOString(),
|
|
268
|
+
exitCode: record.exitCode,
|
|
269
|
+
};
|
|
270
|
+
}),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/** One aggregated, value-free run-log line naming every configured name that
|
|
274
|
+
* is absent from `env`, or null when all configured names resolved. */
|
|
275
|
+
function worktreeEnvPassthroughWarning(db, env) {
|
|
276
|
+
const names = configuredNames(db);
|
|
277
|
+
const unresolved = names.filter(name => !env[name]);
|
|
278
|
+
if (!unresolved.length)
|
|
279
|
+
return null;
|
|
280
|
+
const state = states.get(db);
|
|
281
|
+
const now = Date.now();
|
|
282
|
+
const parts = unresolved.map(name => {
|
|
283
|
+
const record = state?.records.get(name);
|
|
284
|
+
// An expired success no longer counts as recovered; failures keep their cause.
|
|
285
|
+
const status = !record ? 'not-defined' : record.status === 'recovered' && now >= record.expiresAt ? 'not-defined' : record.status;
|
|
286
|
+
return { name, status };
|
|
287
|
+
});
|
|
288
|
+
const hint = process.platform === 'win32'
|
|
289
|
+
? 'set it in the environment Specrails starts with'
|
|
290
|
+
: 'configure it in the login shell or launch from a terminal';
|
|
291
|
+
if (parts.length === 1)
|
|
292
|
+
return `[environment] ${parts[0].name} not available (${parts[0].status}); ${hint}`;
|
|
293
|
+
return `[environment] ${parts.map(p => `${p.name} (${p.status})`).join(', ')} not available; ${hint.replace(' it ', ' them ')}`;
|
|
294
|
+
}
|
|
@@ -390,6 +390,11 @@ class ProjectRegistry {
|
|
|
390
390
|
(0, telemetry_receiver_1.dropBlobStatesForProject)(id);
|
|
391
391
|
}
|
|
392
392
|
catch { /* ignore */ }
|
|
393
|
+
// Stop the login-shell env refresh and forget recovered values.
|
|
394
|
+
try {
|
|
395
|
+
(0, project_env_1.stopProjectEnvWarmup)(ctx.db);
|
|
396
|
+
}
|
|
397
|
+
catch { /* ignore */ }
|
|
393
398
|
// Close the DB connection BEFORE removing the project's data dir below.
|
|
394
399
|
try {
|
|
395
400
|
ctx.db.close();
|
|
@@ -639,6 +644,10 @@ class ProjectRegistry {
|
|
|
639
644
|
ctx.fileSummaryManager.dispose();
|
|
640
645
|
}
|
|
641
646
|
catch { /* ignore */ }
|
|
647
|
+
try {
|
|
648
|
+
(0, project_env_1.stopProjectEnvWarmup)(ctx.db);
|
|
649
|
+
}
|
|
650
|
+
catch { /* ignore */ }
|
|
642
651
|
ctx.ticketWatcher.close().catch(() => { });
|
|
643
652
|
}
|
|
644
653
|
// Close the shared browser context once, after every per-project manager has
|
|
@@ -1152,6 +1161,8 @@ class ProjectRegistry {
|
|
|
1152
1161
|
sourcePath: () => (0, project_repositories_1.resolveProjectRepository)(project).path,
|
|
1153
1162
|
workspacePaths: () => { const primary = (0, project_repositories_1.resolveProjectRepository)(project); return primary.workspacePaths ?? (primary.workspacePath ? [primary.workspacePath] : undefined); },
|
|
1154
1163
|
env: () => (0, workspace_resolution_1.resolveLoopBaseEnv)({ slug: project.slug, path: project.path }, undefined, (0, project_env_1.applyWorktreeEnvPassthrough)(db, process.env)),
|
|
1164
|
+
// One value-free run-log line when configured names are unresolved.
|
|
1165
|
+
environmentNotice: () => (0, project_env_1.worktreeEnvPassthroughWarning)(db, (0, project_env_1.applyWorktreeEnvPassthrough)(db, process.env)),
|
|
1155
1166
|
// Named rail profiles and global per-agent defaults ride the same
|
|
1156
1167
|
// SPECRAILS_PROFILE_PATH snapshot in both AI transports. An explicit null
|
|
1157
1168
|
// rail selection opts out; omitted selections keep Core's default rules.
|
|
@@ -1486,6 +1497,9 @@ class ProjectRegistry {
|
|
|
1486
1497
|
const ctx = { project, db, queueManager, chatManager, setupManager, proposalManager, agentRefineManager, fileSummaryManager, specLauncherManager, ticketWatcher, browserCaptureManager, jiraSyncManager, stuckRunDetector, broadcast: boundBroadcast, railJobs, loopRunManager, railLoopRuns, onLoopRunFinished, getTicketSpec, desktopDb: this._desktopDb, milestoneProgress };
|
|
1487
1498
|
ctx.milestoneChains = milestoneChains;
|
|
1488
1499
|
this._contexts.set(project.id, ctx);
|
|
1500
|
+
// Warm the project's login-shell env cache off the event loop so the first
|
|
1501
|
+
// rail/loop spawn reads recovered credentials instead of a cold 1.5 s probe.
|
|
1502
|
+
(0, project_env_1.startProjectEnvWarmup)(db);
|
|
1489
1503
|
const recoverAfterProbe = (definitionStates) => {
|
|
1490
1504
|
if (this._contexts.get(project.id) !== ctx)
|
|
1491
1505
|
return;
|
|
@@ -9,6 +9,7 @@ exports.registerSettingsRoutes = registerSettingsRoutes;
|
|
|
9
9
|
const project_settings_1 = require("./modules/project-settings");
|
|
10
10
|
const sqlite_1 = require("./modules/project-settings/adapters/sqlite");
|
|
11
11
|
const http_1 = require("./modules/project-settings/adapters/http");
|
|
12
|
+
const project_env_1 = require("./project-env");
|
|
12
13
|
const fs_1 = __importDefault(require("fs"));
|
|
13
14
|
const path_1 = __importDefault(require("path"));
|
|
14
15
|
const db_1 = require("./db");
|
|
@@ -35,6 +36,22 @@ function registerSettingsRoutes(deps) {
|
|
|
35
36
|
const runtimeChanged = JSON.stringify(settings.subagentRuntime) !== JSON.stringify(previous.subagentRuntime);
|
|
36
37
|
if (settings.allowSubagents !== previous.allowSubagents || runtimeChanged)
|
|
37
38
|
registry.notifySettingsChanged({ projectId: String(req.params.projectId) });
|
|
39
|
+
// Configured env names changed: re-warm this project's login-shell cache.
|
|
40
|
+
if (JSON.stringify(settings.worktreeEnvPassthrough) !== JSON.stringify(previous.worktreeEnvPassthrough))
|
|
41
|
+
void (0, project_env_1.refreshProjectEnv)(ctx(req).db);
|
|
42
|
+
});
|
|
43
|
+
// Value-free resolution status of the configured env names (+ recheck).
|
|
44
|
+
(0, http_1.registerEnvPassthroughStatusHttp)(router, {
|
|
45
|
+
read: async (req) => {
|
|
46
|
+
const { db } = ctx(req);
|
|
47
|
+
await (0, project_env_1.ensureProjectEnvFresh)(db);
|
|
48
|
+
return (0, project_env_1.getProjectEnvStatus)(db);
|
|
49
|
+
},
|
|
50
|
+
recheck: async (req) => {
|
|
51
|
+
const { db } = ctx(req);
|
|
52
|
+
await (0, project_env_1.refreshProjectEnv)(db);
|
|
53
|
+
return (0, project_env_1.getProjectEnvStatus)(db);
|
|
54
|
+
},
|
|
38
55
|
});
|
|
39
56
|
// ─── Per-project Quick mode Contract Refine last-used value ─────────────────
|
|
40
57
|
router.get('/:projectId/add-spec-quick-contract-refine-last', (req, res) => {
|
|
@@ -33,9 +33,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.WORKTREE_DEPENDENCY_CACHES = void 0;
|
|
36
|
+
exports.DEPENDENCY_LOCKFILES = exports.WORKTREE_DEPENDENCY_CACHES = void 0;
|
|
37
37
|
exports.isWorktreeNodeModulesEnabled = isWorktreeNodeModulesEnabled;
|
|
38
38
|
exports.discoverPackageDirs = discoverPackageDirs;
|
|
39
|
+
exports.dependencyInputs = dependencyInputs;
|
|
40
|
+
exports.differingDependencyInput = differingDependencyInput;
|
|
39
41
|
exports.authenticateWarmNodeModulesLinks = authenticateWarmNodeModulesLinks;
|
|
40
42
|
exports.linkNodeModulesIntoWorktree = linkNodeModulesIntoWorktree;
|
|
41
43
|
/**
|
|
@@ -48,7 +50,14 @@ exports.linkNodeModulesIntoWorktree = linkNodeModulesIntoWorktree;
|
|
|
48
50
|
* Cleanup authority covers individual source-anchored links, never the entire
|
|
49
51
|
* writable directory. Replaced packages and newly created files therefore keep
|
|
50
52
|
* the existing ignored-artifact / recoverable-work settlement guarantees.
|
|
53
|
+
*
|
|
54
|
+
* Freshness guard: a package directory is linked only when its dependency inputs
|
|
55
|
+
* (nearest lockfile up to the Git top level + the manifest's dependency fields)
|
|
56
|
+
* match between the base checkout and the worktree. Otherwise the directory is
|
|
57
|
+
* left without node_modules so the runtime installs it cold from the worktree's
|
|
58
|
+
* own lockfile. The base checkout is only ever read.
|
|
51
59
|
*/
|
|
60
|
+
const crypto_1 = require("crypto");
|
|
52
61
|
const fs = __importStar(require("fs"));
|
|
53
62
|
const path = __importStar(require("path"));
|
|
54
63
|
const worktree_overlay_1 = require("./worktree-overlay");
|
|
@@ -112,6 +121,81 @@ function discoverPackageDirs(baseRepo) {
|
|
|
112
121
|
walk('', 0);
|
|
113
122
|
return found;
|
|
114
123
|
}
|
|
124
|
+
/** Lockfiles in lookup priority order; the first one found in a directory wins. */
|
|
125
|
+
exports.DEPENDENCY_LOCKFILES = ['pnpm-lock.yaml', 'yarn.lock', 'package-lock.json', 'npm-shrinkwrap.json', 'bun.lock'];
|
|
126
|
+
const DEPENDENCY_FIELDS = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];
|
|
127
|
+
function sha256(data) {
|
|
128
|
+
return (0, crypto_1.createHash)('sha256').update(data).digest('hex');
|
|
129
|
+
}
|
|
130
|
+
function canonicalJson(value) {
|
|
131
|
+
if (Array.isArray(value))
|
|
132
|
+
return `[${value.map(canonicalJson).join(',')}]`;
|
|
133
|
+
if (value && typeof value === 'object') {
|
|
134
|
+
const record = value;
|
|
135
|
+
return `{${Object.keys(record).sort().map(key => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(',')}}`;
|
|
136
|
+
}
|
|
137
|
+
return JSON.stringify(value) ?? 'null';
|
|
138
|
+
}
|
|
139
|
+
/** Git top level of a checkout: nearest ancestor holding a `.git` entry (dir
|
|
140
|
+
* for a clone, file for a linked worktree). Outside Git, the checkout itself
|
|
141
|
+
* bounds the lockfile search so it never escapes into unrelated parents. */
|
|
142
|
+
function gitTopLevel(checkoutRoot) {
|
|
143
|
+
const start = path.resolve(checkoutRoot);
|
|
144
|
+
for (let current = start;; current = path.dirname(current)) {
|
|
145
|
+
if (exists(path.join(current, '.git')))
|
|
146
|
+
return current;
|
|
147
|
+
if (path.dirname(current) === current)
|
|
148
|
+
return start;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Fingerprint the inputs that decide what a package directory installs. Never
|
|
153
|
+
* throws: unreadable inputs yield null digests, which compare as different.
|
|
154
|
+
*/
|
|
155
|
+
function dependencyInputs(checkoutRoot, pkgRel) {
|
|
156
|
+
const top = gitTopLevel(checkoutRoot);
|
|
157
|
+
const pkgDir = pkgRel === '' ? path.resolve(checkoutRoot) : path.resolve(checkoutRoot, ...pkgRel.split('/'));
|
|
158
|
+
let lockfile = null;
|
|
159
|
+
for (let current = pkgDir; lockfile === null; current = path.dirname(current)) {
|
|
160
|
+
for (const name of exports.DEPENDENCY_LOCKFILES) {
|
|
161
|
+
const file = path.join(current, name);
|
|
162
|
+
if (!exists(file))
|
|
163
|
+
continue;
|
|
164
|
+
let digest = null;
|
|
165
|
+
try {
|
|
166
|
+
digest = sha256(fs.readFileSync(file));
|
|
167
|
+
}
|
|
168
|
+
catch { /* unreadable: never equal */ }
|
|
169
|
+
lockfile = { name, location: path.relative(pkgDir, current).split(path.sep).join('/'), digest };
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
if (current === top || path.dirname(current) === current)
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
let manifestDigest = null;
|
|
176
|
+
try {
|
|
177
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf8'));
|
|
178
|
+
const fields = {};
|
|
179
|
+
for (const field of DEPENDENCY_FIELDS)
|
|
180
|
+
if (manifest?.[field] !== undefined)
|
|
181
|
+
fields[field] = manifest[field];
|
|
182
|
+
manifestDigest = sha256(canonicalJson(fields));
|
|
183
|
+
}
|
|
184
|
+
catch { /* missing or unparsable manifest: never equal */ }
|
|
185
|
+
return { lockfile, manifestDigest };
|
|
186
|
+
}
|
|
187
|
+
/** Name of the first dependency input that differs, or null when warm reuse is safe. */
|
|
188
|
+
function differingDependencyInput(base, worktree) {
|
|
189
|
+
const a = base.lockfile, b = worktree.lockfile;
|
|
190
|
+
if (a || b) {
|
|
191
|
+
if (!a || !b || a.name !== b.name || a.location !== b.location || a.digest === null || a.digest !== b.digest) {
|
|
192
|
+
return (a ?? b).name;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (base.manifestDigest === null || base.manifestDigest !== worktree.manifestDigest)
|
|
196
|
+
return 'package.json';
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
115
199
|
/** Resolve a path through symlinks when possible; the literal path otherwise
|
|
116
200
|
* (a nonexistent target must still compare, and must still compare EQUAL to an
|
|
117
201
|
* equally-nonexistent expectation rather than silently authorizing). */
|
|
@@ -305,6 +389,16 @@ function linkNodeModulesIntoWorktree(baseRepo, worktreePath) {
|
|
|
305
389
|
}
|
|
306
390
|
const dest = path.join(worktreePath, ...rel.split('/'));
|
|
307
391
|
const legacy = authenticateWarmLink(baseRepo, worktreePath, rel, sourceRel);
|
|
392
|
+
if (!exists(dest)) {
|
|
393
|
+
// Freshness guard, only when a NEW link would be prepared: an existing
|
|
394
|
+
// (resumed or legacy) link keeps its authentication below.
|
|
395
|
+
const differing = differingDependencyInput(dependencyInputs(baseRepo, pkgRel), dependencyInputs(prefix ? path.join(worktreePath, ...prefix.split('/')) : worktreePath, pkgRel));
|
|
396
|
+
if (differing) {
|
|
397
|
+
const pkgDir = [prefix, pkgRel].filter(Boolean).join('/') || '.';
|
|
398
|
+
result.warnings.push(`${pkgDir}: ${differing} differs from the base checkout; dependencies will be installed in the worktree`);
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
308
402
|
if (!exists(dest) || legacy) {
|
|
309
403
|
let staging;
|
|
310
404
|
let removedLegacy = false;
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/AnalyticsPage-YD8MLoPa.js","assets/rolldown-runtime-CNC7AqOf.js","assets/jsx-runtime-zu2_FqZY.js","assets/chunk-KS7C4IRE-DEKu087k.js","assets/preload-helper-Czpn1I53.js","assets/useSharedWebSocket-BEHPeU1Y.js","assets/activity-DiyjsgQV.js","assets/addspec-BOvgmcgk.js","assets/agent-Dip4h2YE.js","assets/agentRuntime-C_yLPKDy.js","assets/agents-Doe1sHY5.js","assets/agentstudio-BzugqB_N.js","assets/aiedit-iIjz2_Qk.js","assets/analytics-BWynXPEm.js","assets/attachments-B53PG733.js","assets/browser-CG0PE1j_.js","assets/builder-CpS9liE9.js","assets/chat-BGD7MXAy.js","assets/code-DaZ-ZwCT.js","assets/commands-BAWSYHjQ.js","assets/common-Bdi0qN4S.js","assets/dashboard-XGdQ5mjw.js","assets/explore-DgBACn39.js","assets/integrations-Dwl9KQp0.js","assets/jira-FXr7u2lh.js","assets/jobs-De3EqaO0.js","assets/loops-WyWDkhok.js","assets/mcp-CUAN6ORK.js","assets/narration-DbHnz0qV.js","assets/nav--u7iPYlS.js","assets/packet-B0H15elv.js","assets/settings-CKu0LTib.js","assets/setup-CKGUxKr-.js","assets/specs-DfIdoU5L.js","assets/subscriptionUsage-CdlE5Mg1.js","assets/terminal-BAzpHtpY.js","assets/tickets-D41ZMQpf.js","assets/createLucideIcon-Bjbbp_KS.js","assets/Trans-CAY2Rd2f.js","assets/useDesktop-9Lwoel_8.js","assets/react-dom-DiWzBDP9.js","assets/provider-capabilities-lePfeT_Y.js","assets/spending-DemeFfrH.js","assets/clsx-CjueKrWZ.js","assets/formatDistanceToNow-DlQY1Lm_.js","assets/getTimezoneOffsetInMilliseconds-COUeq65G.js","assets/index-CONHvEJw.js","assets/tauri-shell-DE6LfGPw.js","assets/TicketDetailModalContext-Dea22CI-.js","assets/save-B6wGWwBR.js","assets/MinimizedChatsContext-Ds2-R6qJ.js","assets/chevron-up-CwGUHd6D.js","assets/sparkles-DrY8hb0X.js","assets/x-CBMI9ejn.js","assets/ban-E4EaZe3a.js","assets/file-text-BcLCfcWZ.js","assets/circle-x-CMYsN4ZH.js","assets/download-CFHyyHcw.js","assets/external-link-D6XxC_Cu.js","assets/git-branch-BRM0F5GN.js","assets/layers-eDMjG41I.js","assets/loader-circle-DcygOQpC.js","assets/pencil-C9RkrIPZ.js","assets/square-Ci-PFcFe.js","assets/plus-CsnjfkmX.js","assets/rotate-ccw-Wh4id13q.js","assets/search-Cq_gE3Ik.js","assets/trash-2-BOU3WdZZ.js","assets/triangle-alert-H9T4jAmh.js","assets/jira-api-C94VNHNY.js","assets/zap-CHo3aCD1.js","assets/dialog-CMgps2MY.js","assets/utils-BC4DHlz2.js","assets/lib-CGdyxIWg.js","assets/button-D1xExSTF.js","assets/RepositoryScopeSelector-DrtPqvc_.js","assets/project-repositories-Cc3oNv8X.js","assets/core-B3mKBIh5.js","assets/RepositoryDeliveries-CW4eKG3x.js","assets/dist-Bq10nS2m.js","assets/useRuntimeRuns-DL0xYoZ2.js","assets/event-Bt7f3wXe.js","assets/index-BacrO_ub.css","assets/ExportDropdown-BvKQgwM9.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{$ as c,Mt as l,Nt as u}from"./index-CONHvEJw.js";var d=e(t(),1),f=n(),p=(0,d.lazy)(()=>r(()=>import(`./AnalyticsPage-YD8MLoPa.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,79,80,81,82,83,84,85]))),m=420,h=560;function g(){let{t:e}=i(`agent`),{closeAnalyticsPane:t}=c(),[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)(l,{className:`h-4 w-4`}):(0,f.jsx)(u,{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-CLJn2F2e.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-DEKu087k.js","assets/useSharedWebSocket-BEHPeU1Y.js","assets/activity-DiyjsgQV.js","assets/addspec-BOvgmcgk.js","assets/agent-Dip4h2YE.js","assets/agentRuntime-C_yLPKDy.js","assets/agents-Doe1sHY5.js","assets/agentstudio-BzugqB_N.js","assets/aiedit-iIjz2_Qk.js","assets/analytics-BWynXPEm.js","assets/attachments-B53PG733.js","assets/browser-CG0PE1j_.js","assets/builder-CpS9liE9.js","assets/chat-BGD7MXAy.js","assets/code-DaZ-ZwCT.js","assets/commands-BAWSYHjQ.js","assets/common-Bdi0qN4S.js","assets/dashboard-XGdQ5mjw.js","assets/explore-DgBACn39.js","assets/integrations-Dwl9KQp0.js","assets/jira-FXr7u2lh.js","assets/jobs-De3EqaO0.js","assets/loops-WyWDkhok.js","assets/mcp-CUAN6ORK.js","assets/narration-DbHnz0qV.js","assets/nav--u7iPYlS.js","assets/packet-B0H15elv.js","assets/settings-CKu0LTib.js","assets/setup-CKGUxKr-.js","assets/specs-DfIdoU5L.js","assets/subscriptionUsage-CdlE5Mg1.js","assets/terminal-BAzpHtpY.js","assets/tickets-D41ZMQpf.js","assets/createLucideIcon-Bjbbp_KS.js","assets/useDesktop-9Lwoel_8.js","assets/TicketDetailModalContext-Dea22CI-.js","assets/tauri-shell-DE6LfGPw.js","assets/Trans-CAY2Rd2f.js","assets/provider-capabilities-lePfeT_Y.js","assets/save-B6wGWwBR.js","assets/spending-DemeFfrH.js","assets/MinimizedChatsContext-Ds2-R6qJ.js","assets/chevron-up-CwGUHd6D.js","assets/sparkles-DrY8hb0X.js","assets/x-CBMI9ejn.js","assets/ban-E4EaZe3a.js","assets/file-text-BcLCfcWZ.js","assets/circle-x-CMYsN4ZH.js","assets/download-CFHyyHcw.js","assets/external-link-D6XxC_Cu.js","assets/git-branch-BRM0F5GN.js","assets/layers-eDMjG41I.js","assets/loader-circle-DcygOQpC.js","assets/pencil-C9RkrIPZ.js","assets/square-Ci-PFcFe.js","assets/plus-CsnjfkmX.js","assets/rotate-ccw-Wh4id13q.js","assets/search-Cq_gE3Ik.js","assets/trash-2-BOU3WdZZ.js","assets/triangle-alert-H9T4jAmh.js","assets/jira-api-C94VNHNY.js","assets/zap-CHo3aCD1.js","assets/dialog-CMgps2MY.js","assets/utils-BC4DHlz2.js","assets/clsx-CjueKrWZ.js","assets/lib-CGdyxIWg.js","assets/formatDistanceToNow-DlQY1Lm_.js","assets/getTimezoneOffsetInMilliseconds-COUeq65G.js","assets/button-D1xExSTF.js","assets/RepositoryScopeSelector-DrtPqvc_.js","assets/project-repositories-Cc3oNv8X.js","assets/index-CONHvEJw.js","assets/core-B3mKBIh5.js","assets/RepositoryDeliveries-CW4eKG3x.js","assets/dist-Bq10nS2m.js","assets/useRuntimeRuns-DL0xYoZ2.js","assets/event-Bt7f3wXe.js","assets/index-BacrO_ub.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{$ as o,Mt as s,Nt as c,dt as l,ft as u}from"./index-CONHvEJw.js";var d=e(t(),1),f=n(),p=(0,d.lazy)(()=>r(()=>import(`./CodePage-CLJn2F2e.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,79,80,81,82]))),m=420,h=560;function g({projectId:e,conversationId:t}){let{t:n}=i(`agent`),{closeCodePane:r}=o(),[g,_]=(0,d.useState)(h),[v,y]=(0,d.useState)(()=>window.innerWidth),[b,x]=(0,d.useState)(!1),S=(0,d.useRef)(null),C=(0,d.useRef)(null),w=Math.max(0,v-Math.min(m,v/2)),T=Math.min(m,w),E=Math.min(w,Math.max(T,g)),D=(0,d.useCallback)(e=>Math.min(w,Math.max(T,e)),[w,T]),O=`${e}:${t}`,k=(0,d.useCallback)(n=>{u(e,t,{path:n})},[e,t]);(0,d.useLayoutEffect)(()=>{let e=S.current,t=e?.parentElement;if(!e||!t)return;let n=()=>{let n=t.getBoundingClientRect().width||t.clientWidth||window.innerWidth,r=0;for(let n of t.children){if(n===e||!(n instanceof HTMLElement))continue;let t=getComputedStyle(n);t.display===`none`||t.position===`absolute`||t.position===`fixed`||Number(t.flexGrow)>0||(r+=n.getBoundingClientRect().width)}y(Math.max(0,n-r))},r=new ResizeObserver(n),i=()=>{r.disconnect(),r.observe(t);for(let n of t.children)n!==e&&r.observe(n);n()},a=new MutationObserver(i);return a.observe(t,{childList:!0}),i(),window.addEventListener(`resize`,n),()=>{r.disconnect(),a.disconnect(),window.removeEventListener(`resize`,n)}},[O]),(0,d.useEffect)(()=>()=>{C.current?.()},[O,b,w]);let A=(0,d.useCallback)(e=>{if(e.button!==0)return;e.preventDefault(),C.current?.();let t=e.clientX,n=E,r=e.pointerId,i=e.currentTarget;try{i.setPointerCapture(r)}catch{}let a=e=>{e.pointerId===r&&_(D(n+t-e.clientX))},o=()=>{window.removeEventListener(`pointermove`,a),window.removeEventListener(`pointerup`,s),window.removeEventListener(`pointercancel`,s),window.removeEventListener(`blur`,o),i.removeEventListener(`lostpointercapture`,o);try{i.hasPointerCapture(r)&&i.releasePointerCapture(r)}catch{}C.current=null},s=e=>{e.pointerId===r&&o()};C.current=o,window.addEventListener(`pointermove`,a),window.addEventListener(`pointerup`,s),window.addEventListener(`pointercancel`,s),window.addEventListener(`blur`,o),i.addEventListener(`lostpointercapture`,o)},[E,D]);return(0,f.jsxs)(`div`,{ref:S,className:`flex h-full min-w-0 flex-col overflow-hidden border-l border-border bg-background${b?` absolute inset-0 z-30 border-l-0`:` relative`}`,style:b?void 0:{width:E,maxWidth:`100%`,flexShrink:0},"data-testid":`agent-code-pane`,children:[!b&&(0,f.jsx)(`div`,{role:`separator`,"aria-orientation":`vertical`,onPointerDown:A,tabIndex:0,"aria-label":n(`workspace.resizeCode`),"aria-valuemin":Math.round(T),"aria-valuemax":Math.round(w),"aria-valuenow":Math.round(E),onKeyDown:e=>{let t=e.key===`ArrowLeft`?E+32:e.key===`ArrowRight`?E-32:e.key===`Home`?T:e.key===`End`?w:null;t!==null&&(e.preventDefault(),_(D(t)))},className:`absolute inset-y-0 left-0 z-20 w-1.5 cursor-col-resize select-none touch-none hover:bg-accent-primary/20 focus-visible:bg-accent-primary/30 focus-visible:outline-none`,title:n(`workspace.resizeCode`)}),(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:n(`workspace.files`)}),(0,f.jsxs)(`div`,{className:`ml-auto flex items-center gap-1`,children:[(0,f.jsx)(`button`,{type:`button`,onClick:()=>{C.current?.(),x(e=>!e)},"aria-label":n(b?`restore`:`maximize`),title:n(b?`restore`:`maximize`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:b?(0,f.jsx)(s,{className:`h-4 w-4`}):(0,f.jsx)(c,{className:`h-4 w-4`})}),(0,f.jsx)(`button`,{type:`button`,onClick:()=>{C.current?.(),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,f.jsx)(a,{className:`h-4 w-4`})})]})]}),(0,f.jsx)(`div`,{className:`min-h-0 flex-1 overflow-hidden`,children:(0,f.jsx)(d.Suspense,{fallback:(0,f.jsx)(`div`,{className:`flex h-full items-center justify-center text-sm text-muted-foreground`,children:`…`}),children:(0,f.jsx)(p,{embedded:!0,initialRepositoryId:l(e,t).repositoryId,onRepositoryChange:n=>u(e,t,{repositoryId:n}),initialPath:l(e,t).path,onSelectedPathChange:k},O)})})]},O)}export{g as AgentModeCodePane};
|