specrails-desktop 2.32.0 → 2.34.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-DvnFkjVG.js → ActivityFeedPage-DIDxNsDj.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-CBjcliD7.js → AgentBrowserCapture-7pJ2G5VY.js} +1 -1
- package/client/dist/assets/AgentModeAnalyticsPane-BSSyTnTM.js +2 -0
- package/client/dist/assets/{AgentModeCodePane-CokuFVdV.js → AgentModeCodePane-DzW6gvDw.js} +2 -2
- package/client/dist/assets/{AgentModeJobsPane-rf_DlWNo.js → AgentModeJobsPane-9wJff15f.js} +1 -1
- package/client/dist/assets/AgentsPage-CIA67psb.js +87 -0
- package/client/dist/assets/AnalyticsPage-C0LwYELz.js +1 -0
- package/client/dist/assets/CodePage-RtKvHmAO.js +2 -0
- package/client/dist/assets/{DesktopAnalyticsPage-gBRVqiw6.js → DesktopAnalyticsPage-BU8Y_Ts1.js} +1 -1
- package/client/dist/assets/{DocsDialog-BzO5dg2B.js → DocsDialog-DIpoaGlQ.js} +1 -1
- package/client/dist/assets/{DocsPage-Db_uxr1j.js → DocsPage-ME47KZpK.js} +1 -1
- package/client/dist/assets/InteractiveJobComposer-BGjIe9R6.js +19 -0
- package/client/dist/assets/{JobDetailModal-DPHh2WOS.js → JobDetailModal-1UZkqH4I.js} +1 -1
- package/client/dist/assets/JobDetailPage-CN65sAf1.js +1 -0
- package/client/dist/assets/JobsPage-f-4dqys7.js +1 -0
- package/client/dist/assets/LoopBuilderPage-WeiofeL6.js +7 -0
- package/client/dist/assets/LoopPreviewModal-DDTnTyH0.js +1 -0
- package/client/dist/assets/LoopsPage-BVNchIex.js +1 -0
- package/client/dist/assets/MinimizedChatsContext-CHsu3NCX.js +1 -0
- package/client/dist/assets/PluginsPage-CdFzGEfQ.js +2 -0
- package/client/dist/assets/ProjectSettingsDialog-BkCXBQWk.js +1 -0
- package/client/dist/assets/TemplatePreviewModal-RDi0khFG.js +1 -0
- package/client/dist/assets/TicketDetailModal-MhJzrqbi.js +14 -0
- package/client/dist/assets/{Trans-B2i5HVcM.js → Trans-CTqw6rr9.js} +1 -1
- package/client/dist/assets/{addspec-DhmnXER8.js → addspec-A1lDQa4M.js} +1 -1
- package/client/dist/assets/{addspec-BRlk8jON.js → addspec-BHZd0N5F.js} +1 -1
- package/client/dist/assets/{addspec-CHW9xf_X.js → addspec-BPpZ_olp.js} +1 -1
- package/client/dist/assets/{addspec-Di41DpYV.js → addspec-B_TPJt-R.js} +1 -1
- package/client/dist/assets/{addspec-4WmLJmBG.js → addspec-CLYLvaVP.js} +1 -1
- package/client/dist/assets/{addspec-C7XRejU4.js → addspec-CQBjzt24.js} +1 -1
- package/client/dist/assets/{addspec-x3mizDxc.js → addspec-D2YIQ0GU.js} +1 -1
- package/client/dist/assets/{addspec-DTmRrWrF.js → addspec-iFdQ6D99.js} +1 -1
- package/client/dist/assets/agent-Bd-q3Wqp.js +1 -0
- package/client/dist/assets/agent-BlvdW4Hv.js +1 -0
- package/client/dist/assets/agent-C-KQhxeM.js +1 -0
- package/client/dist/assets/agent-Ca15w5eu.js +1 -0
- package/client/dist/assets/agent-D6fKaoMd.js +1 -0
- package/client/dist/assets/agent-HFMYMTbB.js +1 -0
- package/client/dist/assets/agent-QIEbkBjg.js +1 -0
- package/client/dist/assets/agent-mTCsAbCl.js +1 -0
- package/client/dist/assets/agentstudio-BNUth0uj.js +1 -0
- package/client/dist/assets/agentstudio-BNdB5hkr.js +1 -0
- package/client/dist/assets/agentstudio-C7-bt8FO.js +1 -0
- package/client/dist/assets/agentstudio-C8uNchpV.js +1 -0
- package/client/dist/assets/agentstudio-CKabyMlg.js +1 -0
- package/client/dist/assets/agentstudio-CN2pokQu.js +1 -0
- package/client/dist/assets/agentstudio-DR7doNBV.js +1 -0
- package/client/dist/assets/agentstudio-y2dVDXeM.js +1 -0
- package/client/dist/assets/{analytics-C9RaQcE3.js → analytics-47pYygtV.js} +1 -1
- package/client/dist/assets/{analytics-B8Qtptbl.js → analytics-B0vt1g5U.js} +1 -1
- package/client/dist/assets/analytics-BMzCvrCU.js +1 -0
- package/client/dist/assets/{analytics-BPptRY4f.js → analytics-BlFN3Zwx.js} +1 -1
- package/client/dist/assets/{analytics-BxDGOIwf.js → analytics-CZYzL-nR.js} +1 -1
- package/client/dist/assets/{analytics-D_OZdQ9O.js → analytics-DAsb9MJ8.js} +1 -1
- package/client/dist/assets/{analytics-CJIwqrEl.js → analytics-FXQlQ2Yt.js} +1 -1
- package/client/dist/assets/{analytics-We6lUMI8.js → analytics-RnFCNkah.js} +1 -1
- package/client/dist/assets/{builder-CWYPw7Cw.js → builder-0IWtosUm.js} +1 -1
- package/client/dist/assets/{builder-CX1437hF.js → builder-Bf2N6XWn.js} +1 -1
- package/client/dist/assets/{builder-BiUG9Z5K.js → builder-BnqaaBkT.js} +1 -1
- package/client/dist/assets/{builder-CGRz_-zP.js → builder-BxxfGgAO.js} +1 -1
- package/client/dist/assets/{builder-DwDexYRE.js → builder-C5IIgWsq.js} +1 -1
- package/client/dist/assets/builder-CHuahItu.js +1 -0
- package/client/dist/assets/{builder-Cowk9jtL.js → builder-CjWosXL_.js} +1 -1
- package/client/dist/assets/{builder-3bygQtZi.js → builder-CpCIOoKj.js} +1 -1
- package/client/dist/assets/dashboard-BHRqsGCV.js +1 -0
- package/client/dist/assets/dashboard-Bfr90vOl.js +1 -0
- package/client/dist/assets/dashboard-CTsM8x3m.js +1 -0
- package/client/dist/assets/dashboard-CqC6wX0h.js +1 -0
- package/client/dist/assets/dashboard-CuZtAszc.js +1 -0
- package/client/dist/assets/dashboard-DAcqIc-n.js +1 -0
- package/client/dist/assets/dashboard-DekiV-86.js +1 -0
- package/client/dist/assets/dashboard-DoDczc5S.js +1 -0
- package/client/dist/assets/{dist-js-CkTZKBF9.js → dist-js-BmsFWRly.js} +1 -1
- package/client/dist/assets/{dist-js-ClShHcGa.js → dist-js-J5W_N5ng.js} +1 -1
- package/client/dist/assets/{formatDistanceToNow-BjGwPf6I.js → formatDistanceToNow-BOuXgebD.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-CSCbUI4W.js → getTimezoneOffsetInMilliseconds-EjGOpn4b.js} +1 -1
- package/client/dist/assets/{i18n-Ch-Db_x2.js → i18n-nJQvD5zJ.js} +2 -2
- package/client/dist/assets/index-C_sdsSZt.js +63 -0
- package/client/dist/assets/index-DCN5QbDm.css +2 -0
- package/client/dist/assets/{jira-BySfUuQZ.js → jira-BNZu8f9O.js} +1 -1
- package/client/dist/assets/{jira-gAvOyije.js → jira-BOoaUXbb.js} +1 -1
- package/client/dist/assets/{jira-pRaL7JQo.js → jira-BYVN4L2u.js} +1 -1
- package/client/dist/assets/jira-CKJg3HZd.js +1 -0
- package/client/dist/assets/{jira-Byw2C0F8.js → jira-DDDsZ4ub.js} +1 -1
- package/client/dist/assets/{jira-D38Gj7AT.js → jira-DXwYjVE4.js} +1 -1
- package/client/dist/assets/{jira-D11gPhxz.js → jira-Dn6U2nRW.js} +1 -1
- package/client/dist/assets/{jira-api-BgFIr8eD.js → jira-api-Bhixwn-d.js} +1 -1
- package/client/dist/assets/{jira-CNeYi06w.js → jira-j7hVolR5.js} +1 -1
- package/client/dist/assets/nav-ApJVg3xu.js +1 -0
- package/client/dist/assets/nav-BKNky1Bg.js +1 -0
- package/client/dist/assets/nav-C6vWWKYx.js +1 -0
- package/client/dist/assets/nav-CXJ6Aw89.js +1 -0
- package/client/dist/assets/nav-CsmFBIDf.js +1 -0
- package/client/dist/assets/nav-DjkmQJjd.js +1 -0
- package/client/dist/assets/nav-iwjCg5FR.js +1 -0
- package/client/dist/assets/nav-pYuXq7sf.js +1 -0
- package/client/dist/assets/{provider-capabilities-BvoExILy.js → provider-capabilities-Doej9lfa.js} +1 -1
- package/client/dist/assets/setup-3jWaoVUO.js +1 -0
- package/client/dist/assets/setup-BjgRneb2.js +1 -0
- package/client/dist/assets/setup-BzFIjv-f.js +1 -0
- package/client/dist/assets/setup-C4VfQsxE.js +1 -0
- package/client/dist/assets/setup-DfUkpDbN.js +1 -0
- package/client/dist/assets/setup-ECe90Pmu.js +1 -0
- package/client/dist/assets/setup-dlqC4Jmm.js +1 -0
- package/client/dist/assets/setup-jwfLMiM2.js +1 -0
- package/client/dist/assets/{spending-Dq43JYmD.js → spending-BpYPsevQ.js} +1 -1
- package/client/dist/assets/tickets-B4rjuSYi.js +1 -0
- package/client/dist/assets/tickets-Bc71ce1C.js +1 -0
- package/client/dist/assets/tickets-CLg7VTJ8.js +1 -0
- package/client/dist/assets/tickets-CP9U1GJt.js +1 -0
- package/client/dist/assets/tickets-CZNBiHbq.js +1 -0
- package/client/dist/assets/tickets-DlX2mEkD.js +1 -0
- package/client/dist/assets/tickets-W6pBmbrX.js +1 -0
- package/client/dist/assets/tickets-_yKvCt4Y.js +1 -0
- package/client/dist/assets/{useBrowserCaptureSession-TTO8vHtd.js → useBrowserCaptureSession-BBXBN0Na.js} +1 -1
- package/client/dist/assets/useDesktop-B6Du2N83.js +1 -0
- package/client/dist/assets/{useSharedWebSocket-DaPh7QBL.js → useSharedWebSocket-DK96ctb7.js} +1 -1
- package/client/dist/index.html +24 -24
- package/docs/guide/de/getting-started/3-adding-your-first-project.md +13 -32
- package/docs/guide/de/integrations/1-ai-providers.md +27 -16
- package/docs/guide/en/getting-started/3-adding-your-first-project.md +8 -27
- package/docs/guide/en/integrations/1-ai-providers.md +21 -18
- package/docs/guide/es/getting-started/3-adding-your-first-project.md +12 -31
- package/docs/guide/es/integrations/1-ai-providers.md +24 -16
- package/docs/guide/fr/getting-started/3-adding-your-first-project.md +15 -34
- package/docs/guide/fr/integrations/1-ai-providers.md +25 -16
- package/docs/guide/it/getting-started/3-adding-your-first-project.md +16 -35
- package/docs/guide/it/integrations/1-ai-providers.md +25 -16
- package/docs/guide/ja/getting-started/3-adding-your-first-project.md +16 -35
- package/docs/guide/ja/integrations/1-ai-providers.md +7 -12
- package/docs/guide/pt/getting-started/3-adding-your-first-project.md +15 -34
- package/docs/guide/pt/integrations/1-ai-providers.md +24 -16
- package/docs/guide/zh/getting-started/3-adding-your-first-project.md +18 -37
- package/docs/guide/zh/integrations/1-ai-providers.md +7 -12
- package/docs/internals/project-builder.md +11 -6
- package/package.json +1 -1
- package/server/dist/ai-invocations.js +35 -0
- package/server/dist/blueprint-commit.js +1 -1
- package/server/dist/desktop-db.js +22 -1
- package/server/dist/desktop-router.js +132 -43
- package/server/dist/framework-reseed.js +144 -0
- package/server/dist/index.js +41 -2
- package/server/dist/jira/jira-sync-manager.js +49 -2
- package/server/dist/jira-router.js +21 -0
- package/server/dist/job-phase-breakdown.js +132 -0
- package/server/dist/legacy-migration.js +395 -0
- package/server/dist/loop-executors.js +5 -1
- package/server/dist/offline-assemble.js +32 -4
- package/server/dist/pr-body.js +13 -0
- package/server/dist/project-router-jobs.js +16 -0
- package/server/dist/project-router-spending.js +11 -1
- package/server/dist/provider-detection.js +222 -0
- package/server/dist/provider-selection.js +71 -19
- package/server/dist/queue-manager.js +4 -0
- package/server/dist/rail-pr-decision.js +10 -0
- package/server/dist/rails-router.js +11 -0
- package/server/dist/rails-store.js +5 -1
- package/server/dist/repo-map.js +293 -0
- package/server/dist/setup-manager.js +57 -0
- package/server/dist/spending.js +45 -0
- package/client/dist/assets/AgentsPage-B648IzGI.js +0 -87
- package/client/dist/assets/AnalyticsPage-0rBoULPX.js +0 -1
- package/client/dist/assets/CodePage-ju302Vl7.js +0 -2
- package/client/dist/assets/InteractiveJobComposer-_jmsE_Az.js +0 -19
- package/client/dist/assets/JobDetailPage-7nwHyHGV.js +0 -1
- package/client/dist/assets/JobsPage-C_6YrcmE.js +0 -1
- package/client/dist/assets/LoopBuilderPage-DJ1UPDwL.js +0 -7
- package/client/dist/assets/LoopPreviewModal-12cUnljK.js +0 -1
- package/client/dist/assets/LoopsPage-CHYK0Xjr.js +0 -1
- package/client/dist/assets/MinimizedChatsContext-Gt6KQySM.js +0 -1
- package/client/dist/assets/PluginsPage-E59oQmwc.js +0 -2
- package/client/dist/assets/ProjectSettingsDialog-DQTLQ7ie.js +0 -1
- package/client/dist/assets/TemplatePreviewModal-nXUcDgfk.js +0 -1
- package/client/dist/assets/TicketDetailModal-C-tHW-2s.js +0 -14
- package/client/dist/assets/agent-BKsuIgIT.js +0 -1
- package/client/dist/assets/agent-Bs9NilfI.js +0 -1
- package/client/dist/assets/agent-C23h684q.js +0 -1
- package/client/dist/assets/agent-CqEjy3Z2.js +0 -1
- package/client/dist/assets/agent-D5DZTks1.js +0 -1
- package/client/dist/assets/agent-DE8hUYPl.js +0 -1
- package/client/dist/assets/agent-DsTuJGSc.js +0 -1
- package/client/dist/assets/agent-DwZM05-x.js +0 -1
- package/client/dist/assets/agentstudio-8TQqBenW.js +0 -1
- package/client/dist/assets/agentstudio-B8Z_mmEX.js +0 -1
- package/client/dist/assets/agentstudio-BZR9YmLs.js +0 -1
- package/client/dist/assets/agentstudio-Bge9pAWb.js +0 -1
- package/client/dist/assets/agentstudio-Bz169Vzt.js +0 -1
- package/client/dist/assets/agentstudio-D6L2jpNi.js +0 -1
- package/client/dist/assets/agentstudio-K2-Z7CL0.js +0 -1
- package/client/dist/assets/agentstudio-Zmgzthv8.js +0 -1
- package/client/dist/assets/analytics-BHqUazhP.js +0 -1
- package/client/dist/assets/builder-skfnZ1uC.js +0 -1
- package/client/dist/assets/dashboard-BVq6kRJe.js +0 -1
- package/client/dist/assets/dashboard-BaKPPmLH.js +0 -1
- package/client/dist/assets/dashboard-Bm8e4zYG.js +0 -1
- package/client/dist/assets/dashboard-Bt6IesnM.js +0 -1
- package/client/dist/assets/dashboard-C_NQZle_.js +0 -1
- package/client/dist/assets/dashboard-CyZKoGoI.js +0 -1
- package/client/dist/assets/dashboard-HIUdUBQ0.js +0 -1
- package/client/dist/assets/dashboard-pm4Mr229.js +0 -1
- package/client/dist/assets/index-Bz0TeGRs.css +0 -2
- package/client/dist/assets/index-kCUcnT8G.js +0 -77
- package/client/dist/assets/jira-CmKWERE0.js +0 -1
- package/client/dist/assets/nav-BHDiSIVu.js +0 -1
- package/client/dist/assets/nav-CMiYAq4i.js +0 -1
- package/client/dist/assets/nav-CqS7NrDW.js +0 -1
- package/client/dist/assets/nav-D-0M7xfv.js +0 -1
- package/client/dist/assets/nav-D65O6u9I.js +0 -1
- package/client/dist/assets/nav-D_LKYvBr.js +0 -1
- package/client/dist/assets/nav-FDTJmU79.js +0 -1
- package/client/dist/assets/nav-VaB-C4k5.js +0 -1
- package/client/dist/assets/setup-BBiHEO7h.js +0 -1
- package/client/dist/assets/setup-BlTWhNIQ.js +0 -1
- package/client/dist/assets/setup-Bp2L_oiP.js +0 -1
- package/client/dist/assets/setup-CInYXp1-.js +0 -1
- package/client/dist/assets/setup-CYH3bP6K.js +0 -1
- package/client/dist/assets/setup-DwcQ921P.js +0 -1
- package/client/dist/assets/setup-PBLjuDSs.js +0 -1
- package/client/dist/assets/setup-bx6FtoBp.js +0 -1
- package/client/dist/assets/tickets-BL9r1G0e.js +0 -1
- package/client/dist/assets/tickets-B_NyHJ8f.js +0 -1
- package/client/dist/assets/tickets-BarF1wuG.js +0 -1
- package/client/dist/assets/tickets-CclVhpQA.js +0 -1
- package/client/dist/assets/tickets-CqqOHbli.js +0 -1
- package/client/dist/assets/tickets-DCkFSiqt.js +0 -1
- package/client/dist/assets/tickets-Djn3ccfT.js +0 -1
- package/client/dist/assets/tickets-MiHBPsy_.js +0 -1
- package/client/dist/assets/useDesktop-DUvS4fuP.js +0 -1
|
@@ -64,7 +64,17 @@ function registerSpendingRoutes(deps) {
|
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
66
|
try {
|
|
67
|
-
|
|
67
|
+
const spending = (0, spending_1.getSpending)(ctx(req).db, ctx(req).project.id, filters, makeTitleResolver(req));
|
|
68
|
+
// Agent-mission spend (app-level desktop.sqlite ledger, pinned to this
|
|
69
|
+
// project) folds in additively — a failure never breaks the dashboard.
|
|
70
|
+
let agentMissions;
|
|
71
|
+
try {
|
|
72
|
+
agentMissions = (0, spending_1.getAgentMissionSpending)(registry.desktopDb, ctx(req).project.id, filters);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
console.error('[project-router] agent-mission spending error (non-fatal):', err);
|
|
76
|
+
}
|
|
77
|
+
res.json({ ...spending, ...(agentMissions ? { agentMissions } : {}) });
|
|
68
78
|
}
|
|
69
79
|
catch (err) {
|
|
70
80
|
console.error('[project-router] spending error:', err);
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// App-level provider detection singleton.
|
|
3
|
+
//
|
|
4
|
+
// Provider availability is a property of the MACHINE, not of any project: this
|
|
5
|
+
// module probes every registered adapter (binary presence, version, auth state),
|
|
6
|
+
// applies the beta-flag vetoes at the source, and caches the result for 60s.
|
|
7
|
+
// Consumers (provider-selection, routers, setup) read the same snapshot, so the
|
|
8
|
+
// whole app agrees on which providers exist at any moment.
|
|
9
|
+
//
|
|
10
|
+
// Spec: openspec/changes/global-core-zero-friction/specs/provider-auto-detection/spec.md
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.AUTH_PROBE_TIMEOUT_MS = void 0;
|
|
46
|
+
exports.isCodexBetaDisabled = isCodexBetaDisabled;
|
|
47
|
+
exports.isGeminiBetaDisabled = isGeminiBetaDisabled;
|
|
48
|
+
exports.getDetectionSnapshot = getDetectionSnapshot;
|
|
49
|
+
exports.getDetectedIdsSync = getDetectedIdsSync;
|
|
50
|
+
exports.getSnapshotSync = getSnapshotSync;
|
|
51
|
+
exports.refreshDetection = refreshDetection;
|
|
52
|
+
exports._resetDetectionForTests = _resetDetectionForTests;
|
|
53
|
+
const fs = __importStar(require("fs"));
|
|
54
|
+
const os = __importStar(require("os"));
|
|
55
|
+
const path = __importStar(require("path"));
|
|
56
|
+
const providers_1 = require("./providers");
|
|
57
|
+
const core_compat_1 = require("./core-compat");
|
|
58
|
+
const CACHE_TTL_MS = 60_000;
|
|
59
|
+
/** Auth probes are offline file checks today, but keep the contract bounded. */
|
|
60
|
+
exports.AUTH_PROBE_TIMEOUT_MS = 1500;
|
|
61
|
+
// Emergency rollback for the codex provider: SPECRAILS_CODEX_BETA=0 forces
|
|
62
|
+
// codex unavailable without redeploying. The pre-rebrand SPECRAILS_HUB_CODEX_BETA
|
|
63
|
+
// name is read as a legacy fallback when the new var is unset.
|
|
64
|
+
function isCodexBetaDisabled() {
|
|
65
|
+
const v = process.env.SPECRAILS_CODEX_BETA ?? process.env.SPECRAILS_HUB_CODEX_BETA;
|
|
66
|
+
return v === '0';
|
|
67
|
+
}
|
|
68
|
+
// Gemini parity kill switch: only the exact string '0' disables.
|
|
69
|
+
function isGeminiBetaDisabled() {
|
|
70
|
+
return process.env.SPECRAILS_GEMINI_BETA === '0';
|
|
71
|
+
}
|
|
72
|
+
function isVetoed(id) {
|
|
73
|
+
if (id === 'codex')
|
|
74
|
+
return isCodexBetaDisabled();
|
|
75
|
+
if (id === 'gemini')
|
|
76
|
+
return isGeminiBetaDisabled();
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Offline, provider-specific auth probe. Never throws; degrades to 'unknown'.
|
|
81
|
+
* These are file-presence heuristics — cheap, offline, and bounded — chosen
|
|
82
|
+
* over spawning login-status subcommands (slow, sometimes network-bound).
|
|
83
|
+
*/
|
|
84
|
+
function probeAuthState(id, home) {
|
|
85
|
+
try {
|
|
86
|
+
switch (id) {
|
|
87
|
+
case 'claude':
|
|
88
|
+
// ~/.claude.json exists once the CLI has run/logged in. Credentials may
|
|
89
|
+
// also live in the OS keychain, so absence is 'unknown', never a hard
|
|
90
|
+
// 'unauthenticated'.
|
|
91
|
+
return fs.existsSync(path.join(home, '.claude.json')) ? 'authenticated' : 'unknown';
|
|
92
|
+
case 'codex':
|
|
93
|
+
// Codex persists auth at ~/.codex/auth.json; its absence reliably means
|
|
94
|
+
// no login has happened on this machine.
|
|
95
|
+
return fs.existsSync(path.join(home, '.codex', 'auth.json'))
|
|
96
|
+
? 'authenticated'
|
|
97
|
+
: 'unauthenticated';
|
|
98
|
+
case 'gemini': {
|
|
99
|
+
// OAuth creds land under ~/.gemini; an API key via env also counts.
|
|
100
|
+
if (process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY)
|
|
101
|
+
return 'authenticated';
|
|
102
|
+
return fs.existsSync(path.join(home, '.gemini', 'oauth_creds.json'))
|
|
103
|
+
? 'authenticated'
|
|
104
|
+
: 'unknown';
|
|
105
|
+
}
|
|
106
|
+
default:
|
|
107
|
+
// Kimi (and future providers) have no safe offline probe today.
|
|
108
|
+
return 'unknown';
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return 'unknown';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function usabilityError(adapter, d) {
|
|
116
|
+
if (!d.installed)
|
|
117
|
+
return d.error ?? `${adapter.displayName} CLI was not found on PATH.`;
|
|
118
|
+
if (!d.executable)
|
|
119
|
+
return d.error ?? `${adapter.displayName} was found but its readiness probe failed.`;
|
|
120
|
+
if (adapter.minCliVersion && d.meetsMinimum !== true) {
|
|
121
|
+
return d.error
|
|
122
|
+
?? `${adapter.displayName} ${d.version ?? '(unknown version)'} does not satisfy the required minimum ${adapter.minCliVersion}.`;
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
async function probeAdapter(adapter, home) {
|
|
127
|
+
const vetoed = isVetoed(adapter.id);
|
|
128
|
+
let detection;
|
|
129
|
+
try {
|
|
130
|
+
detection = await adapter.detectInstalled();
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
detection = {
|
|
134
|
+
installed: false,
|
|
135
|
+
executable: false,
|
|
136
|
+
error: err instanceof Error ? err.message : String(err),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const error = usabilityError(adapter, detection);
|
|
140
|
+
let usable = !vetoed && error === undefined;
|
|
141
|
+
// Kimi selection additionally requires a Core build that can render its
|
|
142
|
+
// skills — same gate /available-providers applies.
|
|
143
|
+
if (usable && adapter.id === 'kimi') {
|
|
144
|
+
try {
|
|
145
|
+
const core = await (0, core_compat_1.checkCoreCompat)();
|
|
146
|
+
if (!(0, core_compat_1.coreCompatSupportsProvider)(core, 'kimi'))
|
|
147
|
+
usable = false;
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
usable = false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
id: adapter.id,
|
|
155
|
+
displayName: adapter.displayName,
|
|
156
|
+
installed: detection.installed,
|
|
157
|
+
executable: detection.executable,
|
|
158
|
+
version: detection.version,
|
|
159
|
+
meetsMinimum: detection.meetsMinimum,
|
|
160
|
+
authState: detection.installed ? probeAuthState(adapter.id, home) : 'unknown',
|
|
161
|
+
usable,
|
|
162
|
+
error,
|
|
163
|
+
...(vetoed ? { vetoed: true } : {}),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
let _snapshot = null;
|
|
167
|
+
let _inflight = null;
|
|
168
|
+
async function runDetection() {
|
|
169
|
+
const home = os.homedir();
|
|
170
|
+
const adapters = (0, providers_1.listAdapters)();
|
|
171
|
+
const results = await Promise.all(adapters.map((a) => probeAdapter(a, home)));
|
|
172
|
+
const providers = {};
|
|
173
|
+
for (const r of results)
|
|
174
|
+
providers[r.id] = r;
|
|
175
|
+
const snapshot = {
|
|
176
|
+
providers,
|
|
177
|
+
detected: results.filter((r) => r.usable).map((r) => r.id),
|
|
178
|
+
at: Date.now(),
|
|
179
|
+
};
|
|
180
|
+
_snapshot = snapshot;
|
|
181
|
+
return snapshot;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Read the detection snapshot. Cached for 60s; `refresh: true` bypasses the
|
|
185
|
+
* cache (still coalescing concurrent refreshes into one probe run).
|
|
186
|
+
*/
|
|
187
|
+
async function getDetectionSnapshot(opts) {
|
|
188
|
+
const fresh = _snapshot && Date.now() - _snapshot.at < CACHE_TTL_MS;
|
|
189
|
+
if (fresh && !opts?.refresh)
|
|
190
|
+
return _snapshot;
|
|
191
|
+
if (_inflight)
|
|
192
|
+
return _inflight;
|
|
193
|
+
_inflight = runDetection().finally(() => {
|
|
194
|
+
_inflight = null;
|
|
195
|
+
});
|
|
196
|
+
return _inflight;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Synchronous accessor for the last snapshot's usable ids. Returns null when no
|
|
200
|
+
* detection has completed yet — callers fall back to legacy project-row data.
|
|
201
|
+
*/
|
|
202
|
+
function getDetectedIdsSync() {
|
|
203
|
+
return _snapshot ? _snapshot.detected : null;
|
|
204
|
+
}
|
|
205
|
+
function getSnapshotSync() {
|
|
206
|
+
return _snapshot;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Refresh and report whether the usable set changed vs the previous snapshot.
|
|
210
|
+
* Callers use the flag to decide whether to broadcast `providers.detected_changed`.
|
|
211
|
+
*/
|
|
212
|
+
async function refreshDetection() {
|
|
213
|
+
const before = _snapshot?.detected.join(',') ?? null;
|
|
214
|
+
const snapshot = await getDetectionSnapshot({ refresh: true });
|
|
215
|
+
const after = snapshot.detected.join(',');
|
|
216
|
+
return { snapshot, changed: before !== null && before !== after };
|
|
217
|
+
}
|
|
218
|
+
/** Test-only: clear module state. */
|
|
219
|
+
function _resetDetectionForTests() {
|
|
220
|
+
_snapshot = null;
|
|
221
|
+
_inflight = null;
|
|
222
|
+
}
|
|
@@ -1,60 +1,112 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Per-invocation provider selection
|
|
2
|
+
// Per-invocation provider selection.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
4
|
+
// Provider availability is a MACHINE property (see provider-detection.ts): every
|
|
5
|
+
// project offers every detected, non-vetoed provider. These helpers resolve and
|
|
6
|
+
// validate per-invocation engine requests against that detected set, with the
|
|
7
|
+
// project row's `provider`/`providers` columns as the legacy fallback when no
|
|
8
|
+
// detection snapshot exists yet (startup, unit tests).
|
|
9
9
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
10
|
+
// The detected set is injected via `setDetectedProvidersSupplier` so this module
|
|
11
|
+
// stays synchronous and unit-testable with no detection singleton import cycle.
|
|
12
|
+
//
|
|
13
|
+
// Invariant: when the effective list has length 1 every helper collapses to the
|
|
14
|
+
// single provider, so single-provider machines behave exactly as before.
|
|
15
|
+
//
|
|
16
|
+
// Spec: openspec/changes/global-core-zero-friction/specs/provider-auto-detection/spec.md
|
|
12
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.PROVIDER_PREFERENCE_ORDER = void 0;
|
|
19
|
+
exports.setDetectedProvidersSupplier = setDetectedProvidersSupplier;
|
|
20
|
+
exports.installedProviders = installedProviders;
|
|
13
21
|
exports.isProviderEnabled = isProviderEnabled;
|
|
14
22
|
exports.isMultiProvider = isMultiProvider;
|
|
23
|
+
exports.derivePrimaryProvider = derivePrimaryProvider;
|
|
15
24
|
exports.resolveProvider = resolveProvider;
|
|
16
25
|
exports.validateRequestedProvider = validateRequestedProvider;
|
|
26
|
+
/** Fixed preference order for primary derivation when the stored primary is gone. */
|
|
27
|
+
exports.PROVIDER_PREFERENCE_ORDER = ['claude', 'codex', 'gemini', 'kimi'];
|
|
28
|
+
let _detectedSupplier = null;
|
|
29
|
+
/**
|
|
30
|
+
* Wire the app-level detection singleton in (done once at startup in index.ts).
|
|
31
|
+
* A supplier returning null (no snapshot yet) falls back to project-row data.
|
|
32
|
+
*/
|
|
33
|
+
function setDetectedProvidersSupplier(fn) {
|
|
34
|
+
_detectedSupplier = fn;
|
|
35
|
+
}
|
|
36
|
+
function detectedProviders() {
|
|
37
|
+
if (!_detectedSupplier)
|
|
38
|
+
return null;
|
|
39
|
+
const list = _detectedSupplier();
|
|
40
|
+
if (!Array.isArray(list) || list.length === 0)
|
|
41
|
+
return null;
|
|
42
|
+
return list;
|
|
43
|
+
}
|
|
17
44
|
/**
|
|
18
|
-
*
|
|
19
|
-
* `providers`,
|
|
20
|
-
* hand-built rows) by falling back to the primary provider.
|
|
45
|
+
* The effective provider list for a project: the detected set when available,
|
|
46
|
+
* else the row's `providers`, else the primary. Never empty.
|
|
21
47
|
*/
|
|
22
48
|
function installedProviders(project) {
|
|
49
|
+
const detected = detectedProviders();
|
|
50
|
+
if (detected)
|
|
51
|
+
return detected;
|
|
23
52
|
if (Array.isArray(project.providers) && project.providers.length > 0)
|
|
24
53
|
return project.providers;
|
|
25
54
|
return [project.provider ?? 'claude'];
|
|
26
55
|
}
|
|
27
|
-
/** True when `id` is one of the providers
|
|
56
|
+
/** True when `id` is one of the providers effectively available. */
|
|
28
57
|
function isProviderEnabled(project, id) {
|
|
29
58
|
if (!id)
|
|
30
59
|
return false;
|
|
31
60
|
return installedProviders(project).includes(id);
|
|
32
61
|
}
|
|
33
|
-
/** True when the project offers a choice of engines (more than one
|
|
62
|
+
/** True when the project offers a choice of engines (more than one available). */
|
|
34
63
|
function isMultiProvider(project) {
|
|
35
64
|
return installedProviders(project).length > 1;
|
|
36
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Primary derivation (spec: "Primary provider is derived with stability"):
|
|
68
|
+
* the stored primary while it is still available; otherwise claude if
|
|
69
|
+
* available; otherwise the first available provider in the fixed preference
|
|
70
|
+
* order; otherwise the stored primary (legacy, no detection snapshot).
|
|
71
|
+
*/
|
|
72
|
+
function derivePrimaryProvider(project) {
|
|
73
|
+
const available = installedProviders(project);
|
|
74
|
+
const stored = project.provider;
|
|
75
|
+
if (stored && available.includes(stored))
|
|
76
|
+
return stored;
|
|
77
|
+
for (const p of exports.PROVIDER_PREFERENCE_ORDER) {
|
|
78
|
+
if (available.includes(p))
|
|
79
|
+
return p;
|
|
80
|
+
}
|
|
81
|
+
return stored ?? available[0];
|
|
82
|
+
}
|
|
37
83
|
/**
|
|
38
84
|
* Resolve the effective provider for a per-invocation request.
|
|
39
|
-
* Returns the requested provider when it is
|
|
40
|
-
*
|
|
41
|
-
*
|
|
85
|
+
* Returns the requested provider when it is available; otherwise falls back to
|
|
86
|
+
* the derived primary. Never throws — callers that want strict validation
|
|
87
|
+
* should use `validateRequestedProvider` first.
|
|
42
88
|
*/
|
|
43
89
|
function resolveProvider(project, requested) {
|
|
44
90
|
if (isProviderEnabled(project, requested))
|
|
45
91
|
return requested;
|
|
46
|
-
|
|
92
|
+
const primary = derivePrimaryProvider(project);
|
|
93
|
+
if (typeof requested === 'string' && requested !== '') {
|
|
94
|
+
// A stored engine reference (rail ai_engine, conversation provider) naming
|
|
95
|
+
// a provider that is no longer detected — substitute, never block.
|
|
96
|
+
console.warn(`[provider-selection] provider '${requested}' unavailable; falling back to '${primary}'`);
|
|
97
|
+
}
|
|
98
|
+
return primary;
|
|
47
99
|
}
|
|
48
100
|
/**
|
|
49
101
|
* Strict validation for route handlers. Returns the resolved provider when the
|
|
50
102
|
* request is acceptable, or an `error` string when an explicit, non-empty
|
|
51
|
-
* provider was requested that
|
|
103
|
+
* provider was requested that is not currently available. Omitting the
|
|
52
104
|
* provider (undefined/null/empty) is always acceptable and resolves to primary.
|
|
53
105
|
*/
|
|
54
106
|
function validateRequestedProvider(project, requested) {
|
|
55
107
|
const installed = installedProviders(project);
|
|
56
108
|
if (requested === undefined || requested === null || requested === '') {
|
|
57
|
-
return { ok: true, provider: project
|
|
109
|
+
return { ok: true, provider: derivePrimaryProvider(project) };
|
|
58
110
|
}
|
|
59
111
|
if (typeof requested !== 'string') {
|
|
60
112
|
return { ok: false, error: 'provider must be a string' };
|
|
@@ -15,6 +15,7 @@ const win_spawn_1 = require("./util/win-spawn");
|
|
|
15
15
|
const types_1 = require("./types");
|
|
16
16
|
const command_resolver_1 = require("./command-resolver");
|
|
17
17
|
const rail_isolation_1 = require("./rail-isolation");
|
|
18
|
+
const repo_map_1 = require("./repo-map");
|
|
18
19
|
const cli_prompt_1 = require("./util/cli-prompt");
|
|
19
20
|
const stream_display_1 = require("./util/stream-display");
|
|
20
21
|
const hooks_1 = require("./hooks");
|
|
@@ -2368,6 +2369,9 @@ class QueueManager {
|
|
|
2368
2369
|
if (profileSnapshotPath) {
|
|
2369
2370
|
spawnEnv = { ...spawnEnv, SPECRAILS_PROFILE_PATH: profileSnapshotPath };
|
|
2370
2371
|
}
|
|
2372
|
+
// Deterministic repo map (zero-AI, best-effort) — orients the pipeline's
|
|
2373
|
+
// exploration phase so it doesn't spend its first turns on `ls`/`find`.
|
|
2374
|
+
spawnEnv = (0, repo_map_1.injectRepoMapEnv)(spawnEnv, execution.repoDir);
|
|
2371
2375
|
// ─── Plugin resolution + snapshot ──────────────────────────────────────
|
|
2372
2376
|
// Active = installed + verify ok; degraded = installed but verify failed
|
|
2373
2377
|
// or timed out. Degraded does NOT block spawn — rail proceeds, UI gets
|
|
@@ -73,6 +73,7 @@ const rail_pr_delivery_1 = require("./rail-pr-delivery");
|
|
|
73
73
|
const rail_worktree_release_1 = require("./rail-worktree-release");
|
|
74
74
|
const pr_naming_1 = require("./pr-naming");
|
|
75
75
|
const pr_body_1 = require("./pr-body");
|
|
76
|
+
const ai_invocations_1 = require("./ai-invocations");
|
|
76
77
|
const jira_db_1 = require("./jira/jira-db");
|
|
77
78
|
const rail_pr_store_1 = require("./rail-pr-store");
|
|
78
79
|
const rail_worktrees_store_1 = require("./rail-worktrees-store");
|
|
@@ -1292,12 +1293,21 @@ async function runCreatePr(deps, row) {
|
|
|
1292
1293
|
catch {
|
|
1293
1294
|
changes = null;
|
|
1294
1295
|
}
|
|
1296
|
+
// Cost footer is best-effort: a lookup failure omits the line, never blocks.
|
|
1297
|
+
let costUsd = null;
|
|
1298
|
+
try {
|
|
1299
|
+
costUsd = (0, ai_invocations_1.sumInvocationCostForRuns)(deps.db, snap.runIds);
|
|
1300
|
+
}
|
|
1301
|
+
catch {
|
|
1302
|
+
costUsd = null;
|
|
1303
|
+
}
|
|
1295
1304
|
const title = (0, pr_naming_1.buildPrTitle)(ticketData, { loopName: row.loop_name });
|
|
1296
1305
|
const body = (0, pr_body_1.buildCanonicalPrBody)({
|
|
1297
1306
|
loopName: row.loop_name,
|
|
1298
1307
|
baseBranch: row.base_branch,
|
|
1299
1308
|
tickets: ticketData.map((t) => ({ ...t, branch: branchFor(t.ticketId) })),
|
|
1300
1309
|
changes,
|
|
1310
|
+
costUsd,
|
|
1301
1311
|
});
|
|
1302
1312
|
let next;
|
|
1303
1313
|
// Underlying git/gh failure detail for a degraded or failed delivery —
|
|
@@ -460,6 +460,17 @@ function createRailsRouter() {
|
|
|
460
460
|
res.status(400).json({ error: 'Rail has no tickets assigned' });
|
|
461
461
|
return;
|
|
462
462
|
}
|
|
463
|
+
// Per-rail spec cap: a single launch may carry at most
|
|
464
|
+
// MAX_TICKETS_PER_RAIL_LAUNCH specs — bigger batches must be chunked
|
|
465
|
+
// across rails (the milestone launcher does this automatically).
|
|
466
|
+
if (rail.ticketIds.length > rails_store_1.MAX_TICKETS_PER_RAIL_LAUNCH) {
|
|
467
|
+
res.status(400).json({
|
|
468
|
+
error: 'rail_ticket_cap_exceeded',
|
|
469
|
+
max: rails_store_1.MAX_TICKETS_PER_RAIL_LAUNCH,
|
|
470
|
+
ticketCount: rail.ticketIds.length,
|
|
471
|
+
});
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
463
474
|
// Concurrent-launch safety (parallel rails): a ticket already worked by an
|
|
464
475
|
// ACTIVE run — this rail relaunched mid-run, or the same ticket sitting on
|
|
465
476
|
// two rails — must not spawn a second concurrent writer. The per-ticket
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MAX_RAILS = exports.BASE_RAIL_COUNT = void 0;
|
|
3
|
+
exports.MAX_TICKETS_PER_RAIL_LAUNCH = exports.MAX_RAILS = exports.BASE_RAIL_COUNT = void 0;
|
|
4
4
|
exports.ensureRailMeta = ensureRailMeta;
|
|
5
5
|
exports.listRailIndices = listRailIndices;
|
|
6
6
|
exports.railCount = railCount;
|
|
@@ -22,6 +22,10 @@ exports.BASE_RAIL_COUNT = 3;
|
|
|
22
22
|
/** Hard cap on rails per project — keeps railIndex-keyed maps (metrics,
|
|
23
23
|
* pr-deliveries, worktree progress) bounded and the board renderable. */
|
|
24
24
|
exports.MAX_RAILS = 12;
|
|
25
|
+
/** Hard cap on specs a single rail may LAUNCH with — a bigger batch must be
|
|
26
|
+
* chunked across several rails (the milestone launcher does this client-side;
|
|
27
|
+
* the launch route rejects oversized rails with `rail_ticket_cap_exceeded`). */
|
|
28
|
+
exports.MAX_TICKETS_PER_RAIL_LAUNCH = 3;
|
|
25
29
|
/** Read the per-rail display names from rail_meta into a lookup map. */
|
|
26
30
|
function railNames(db) {
|
|
27
31
|
const rows = db.prepare('SELECT rail_index, name FROM rail_meta').all();
|