specrails-desktop 2.63.2 → 2.64.1
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-B1bpTy7O.js → ActivityFeedPage-Dtp7EyHc.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-BussBbN3.js → AgentBrowserCapture-CLqlEFw8.js} +1 -1
- package/client/dist/assets/{AgentModeAnalyticsPane-BiT1n3BU.js → AgentModeAnalyticsPane-B8_hM3Tw.js} +2 -2
- package/client/dist/assets/{AgentModeCodePane-CUjNfeKa.js → AgentModeCodePane-CNF8ZNjE.js} +2 -2
- package/client/dist/assets/{AgentModeJobsPane-RVEwyS59.js → AgentModeJobsPane-CJpJbO9m.js} +1 -1
- package/client/dist/assets/AgentsPage-BC8XSR79.js +120 -0
- package/client/dist/assets/{AnalyticsPage-Bser2Ly3.js → AnalyticsPage-Cdv4jLtW.js} +1 -1
- package/client/dist/assets/{CodePage-BK0grm6e.js → CodePage-DI1kegAr.js} +1 -1
- package/client/dist/assets/{DesktopAnalyticsPage-DEBidZbs.js → DesktopAnalyticsPage-Bdp8E8U6.js} +1 -1
- package/client/dist/assets/{DocsDialog-Dhyo57n9.js → DocsDialog-Bm4RS7FX.js} +1 -1
- package/client/dist/assets/{DocsPage-BcEkPXd6.js → DocsPage-DihYcU8r.js} +1 -1
- package/client/dist/assets/{ExportDropdown-mRmqdkjr.js → ExportDropdown-Bb1lHx3r.js} +1 -1
- package/client/dist/assets/{InteractiveJobComposer-BlCe3vrU.js → InteractiveJobComposer-BO6ASN-2.js} +1 -1
- package/client/dist/assets/{JobDetailModal-CkAejD1F.js → JobDetailModal-CuPYq7tP.js} +1 -1
- package/client/dist/assets/{JobDetailPage-P-FS_lKz.js → JobDetailPage-BusMjJi9.js} +1 -1
- package/client/dist/assets/{JobsPage-CoUXgYse.js → JobsPage-C0QbsF2d.js} +1 -1
- package/client/dist/assets/{LoopBuilderPage-iFylNk68.js → LoopBuilderPage-B7TlU4iS.js} +1 -1
- package/client/dist/assets/{LoopPreviewModal-4obS_Ptg.js → LoopPreviewModal-BRWtXOD2.js} +1 -1
- package/client/dist/assets/{LoopWindowSurface-CLr5OPqj.js → LoopWindowSurface-DE0v2K1p.js} +1 -1
- package/client/dist/assets/{LoopsPage-eKpWFtCp.js → LoopsPage-C4q1DmCE.js} +1 -1
- package/client/dist/assets/{MinimizedChatsContext-BHIUPVmC.js → MinimizedChatsContext-DHfigjHS.js} +1 -1
- package/client/dist/assets/{PluginsPage-BgTC2CQx.js → PluginsPage-Z1JHSMdR.js} +1 -1
- package/client/dist/assets/{ProjectSettingsDialog-B4chTfU8.js → ProjectSettingsDialog-BUNj0tvN.js} +1 -1
- package/client/dist/assets/{RepositoryDeliveries-DAIxIYsl.js → RepositoryDeliveries-B4ybHOyB.js} +1 -1
- package/client/dist/assets/{RepositoryScopeSelector-DeCoUsRN.js → RepositoryScopeSelector-2eSqR9WK.js} +1 -1
- package/client/dist/assets/{ReviewPacketPage-Cktv0-9b.js → ReviewPacketPage-hwfPNjyy.js} +1 -1
- package/client/dist/assets/{TemplatePreviewModal-BZTV1TM1.js → TemplatePreviewModal-V7FpMWuw.js} +1 -1
- package/client/dist/assets/{TicketDetailModalContext-DiPngt2g.js → TicketDetailModalContext-Dkwo2cDs.js} +1 -1
- package/client/dist/assets/{Trans-ZiXIfj50.js → Trans-BbFxRFtR.js} +1 -1
- package/client/dist/assets/{formatDistanceToNow-CdN5lTyD.js → formatDistanceToNow-NjPHBLfB.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-62h40I4t.js → getTimezoneOffsetInMilliseconds-vJ7qUoqN.js} +1 -1
- package/client/dist/assets/{index-CECj3joE.js → index-Ce4yZtKf.js} +36 -36
- package/client/dist/assets/{jira-api-C_D_RDhl.js → jira-api-Cabj-H0J.js} +1 -1
- package/client/dist/assets/{project-repositories-Be0CWklo.js → project-repositories-Cnnuj9Ia.js} +1 -1
- package/client/dist/assets/{settings-Hw7av-B1.js → settings-B1NV8Ow9.js} +1 -1
- package/client/dist/assets/settings-BQ8XFZeE.js +1 -0
- package/client/dist/assets/settings-BRkxfBbO.js +1 -0
- package/client/dist/assets/settings-C_CcU1dP.js +1 -0
- package/client/dist/assets/settings-Cto9n0eL.js +1 -0
- package/client/dist/assets/{settings-nryN6wuS.js → settings-Pyw8uYtL.js} +1 -1
- package/client/dist/assets/settings-b3HVCNNd.js +1 -0
- package/client/dist/assets/settings-hdive4u6.js +1 -0
- package/client/dist/assets/{spending-eWiZGhC_.js → spending-CQnrWiR3.js} +1 -1
- package/client/dist/assets/{useDesktop-C0mRIlHT.js → useDesktop-BjLT0_EE.js} +1 -1
- package/client/dist/assets/{useRuntimeRuns-B1a6GChu.js → useRuntimeRuns-C97qEqMg.js} +1 -1
- package/client/dist/assets/{useSharedWebSocket-CcsE5986.js → useSharedWebSocket-Bj4LMp7b.js} +2 -2
- package/client/dist/index.html +15 -15
- package/docs/internals/api-reference.md +26 -0
- package/docs/internals/core-runtime-updates.md +3 -3
- package/docs/internals/source-map.md +9 -2
- package/local-runner/dist/specrails-local-runner.js +487 -148
- package/mcp-bridge/dist/specrails-mcp.js +330 -42
- package/package.json +6 -3
- package/server/dist/index.js +14 -1
- package/server/dist/mobile/mobile-admin-router.js +16 -0
- package/server/dist/mobile/mobile-gateway.js +18 -0
- package/server/dist/mobile/mobile-missions.js +6 -4
- package/server/dist/mobile/mobile-preview-channel.js +179 -0
- package/server/dist/mobile/mobile-preview.js +366 -0
- package/server/dist/mobile/mobile-router.js +9 -1
- package/server/dist/mobile/mobile-webrtc-peer.js +54 -4
- package/server/dist/mobile/mobile-ws.js +4 -2
- package/server/dist/modules/browser/domain/preview-origins.js +117 -0
- package/server/dist/modules/browser/runtime/browser-capture-manager.js +141 -18
- package/server/dist/modules/browser/runtime/browser-capture-types.js +10 -1
- package/server/dist/modules/browser/runtime/browser-playwright.js +19 -0
- package/server/dist/modules/browser/runtime/preview-targets.js +80 -0
- package/server/dist/modules/terminals/runtime/background-process-service.js +24 -1
- package/server/dist/project-registry.js +10 -0
- package/client/dist/assets/AgentsPage-DKE-eMC5.js +0 -118
- package/client/dist/assets/settings-BO2N-oPf.js +0 -1
- package/client/dist/assets/settings-BQChAs_w.js +0 -1
- package/client/dist/assets/settings-BzmYlcQB.js +0 -1
- package/client/dist/assets/settings-CGjXb6TX.js +0 -1
- package/client/dist/assets/settings-CMxEWY46.js +0 -1
- package/client/dist/assets/settings-C_dRSgI2.js +0 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createPreviewTargetSource = createPreviewTargetSource;
|
|
4
|
+
const transient_children_1 = require("../../../transient-children");
|
|
5
|
+
const preview_origins_1 = require("../domain/preview-origins");
|
|
6
|
+
/** Background-process statuses whose announced URLs are still served. */
|
|
7
|
+
const LIVE_STATUSES = new Set(['starting', 'running']);
|
|
8
|
+
/** Dev servers announce their URL in the first lines; a long-running process
|
|
9
|
+
* can still re-announce after a restart, so scan the full retained tail. */
|
|
10
|
+
const SCAN_LIMIT = 2000;
|
|
11
|
+
const defaultReader = {
|
|
12
|
+
list: (projectId) => (0, transient_children_1.listBackgroundProcesses)({ projectId }),
|
|
13
|
+
logs: (process) => (0, transient_children_1.getBackgroundProcessLogs)(process.pid, { projectId: process.projectId, processId: process.processId, limit: SCAN_LIMIT }),
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Derives preview targets from mission background-process logs on demand.
|
|
17
|
+
* Targets are derived state: nothing is persisted, an exited process simply
|
|
18
|
+
* stops contributing, and a process recovered after a restart (`interrupted`,
|
|
19
|
+
* OS state unknown) never does. A per-process memo keyed on the log tail keeps
|
|
20
|
+
* the per-request navigation guard cheap.
|
|
21
|
+
*/
|
|
22
|
+
function createPreviewTargetSource(reader = defaultReader) {
|
|
23
|
+
const memo = new Map();
|
|
24
|
+
const urlsFor = (process) => {
|
|
25
|
+
let snapshot = null;
|
|
26
|
+
try {
|
|
27
|
+
snapshot = reader.logs(process);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
if (!snapshot)
|
|
33
|
+
return [];
|
|
34
|
+
const last = snapshot.lines[snapshot.lines.length - 1];
|
|
35
|
+
const key = `${snapshot.nextSequence}:${last?.sequence ?? 0}:${last?.line.length ?? 0}`;
|
|
36
|
+
const cached = memo.get(process.processId);
|
|
37
|
+
if (cached?.key === key)
|
|
38
|
+
return cached.urls;
|
|
39
|
+
const urls = (0, preview_origins_1.previewTargetsFromLines)(snapshot.lines.map((line) => line.line));
|
|
40
|
+
memo.set(process.processId, { projectId: process.projectId, key, urls });
|
|
41
|
+
return urls;
|
|
42
|
+
};
|
|
43
|
+
const live = (projectId) => {
|
|
44
|
+
let processes = [];
|
|
45
|
+
try {
|
|
46
|
+
processes = reader.list(projectId);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const running = processes.filter((process) => process.projectId === projectId && LIVE_STATUSES.has(process.status));
|
|
52
|
+
// Drop memo entries of processes that are no longer live so the cache is
|
|
53
|
+
// bounded by the running set of the projects being previewed.
|
|
54
|
+
const ids = new Set(running.map((process) => process.processId));
|
|
55
|
+
for (const [processId, entry] of memo) {
|
|
56
|
+
if (entry.projectId === projectId && !ids.has(processId))
|
|
57
|
+
memo.delete(processId);
|
|
58
|
+
}
|
|
59
|
+
return running;
|
|
60
|
+
};
|
|
61
|
+
const target = (process) => ({ processId: process.processId, chatId: process.chatId, urls: urlsFor(process) });
|
|
62
|
+
return {
|
|
63
|
+
list: (projectId) => live(projectId).map(target).filter((entry) => entry.urls.length > 0),
|
|
64
|
+
forProcess: (projectId, processId) => {
|
|
65
|
+
const process = live(projectId).find((candidate) => candidate.processId === processId);
|
|
66
|
+
return process ? target(process) : null;
|
|
67
|
+
},
|
|
68
|
+
announcedOrigins: (projectId) => {
|
|
69
|
+
const origins = new Set();
|
|
70
|
+
for (const process of live(projectId)) {
|
|
71
|
+
for (const url of urlsFor(process)) {
|
|
72
|
+
const origin = (0, preview_origins_1.previewOrigin)(url);
|
|
73
|
+
if (origin)
|
|
74
|
+
origins.add(origin);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return origins;
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -12,6 +12,7 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
12
12
|
const loop_runs_store_1 = require("../../loops/runtime/loop-runs-store");
|
|
13
13
|
const project_repositories_1 = require("../../../project-repositories");
|
|
14
14
|
const transient_children_1 = require("../../../transient-children");
|
|
15
|
+
const preview_origins_1 = require("../../browser/domain/preview-origins");
|
|
15
16
|
/** The same scope and lifecycle policy applies to REST and the mission MCP. */
|
|
16
17
|
function resolveBackgroundCwd(repositoryRoot, cwd) {
|
|
17
18
|
if (cwd !== undefined && typeof cwd !== 'string')
|
|
@@ -25,7 +26,29 @@ function resolveBackgroundCwd(repositoryRoot, cwd) {
|
|
|
25
26
|
function backgroundProcessHooks(broadcast) {
|
|
26
27
|
const publish = (type) => (process) => broadcast({ type, process,
|
|
27
28
|
projectId: process.projectId, timestamp: new Date().toISOString() });
|
|
28
|
-
|
|
29
|
+
// Companion app preview: announce once per newly seen local origin so phones
|
|
30
|
+
// refetch the derived targets instead of tailing every log line.
|
|
31
|
+
const announced = new Set();
|
|
32
|
+
const onOutput = (event) => {
|
|
33
|
+
let fresh = false;
|
|
34
|
+
for (const url of (0, preview_origins_1.extractPreviewUrls)(event.line)) {
|
|
35
|
+
const origin = (0, preview_origins_1.previewOrigin)(url);
|
|
36
|
+
if (origin && !announced.has(origin)) {
|
|
37
|
+
announced.add(origin);
|
|
38
|
+
fresh = true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (fresh)
|
|
42
|
+
broadcast({ type: 'background_process.preview_targets', processId: event.processId, chatId: event.chatId,
|
|
43
|
+
projectId: event.projectId, timestamp: new Date().toISOString() });
|
|
44
|
+
};
|
|
45
|
+
const exited = publish('background_process.exited');
|
|
46
|
+
return {
|
|
47
|
+
onStarted: publish('background_process.started'),
|
|
48
|
+
onUpdated: publish('background_process.updated'),
|
|
49
|
+
onOutput,
|
|
50
|
+
onExited: (process) => { announced.clear(); exited(process); },
|
|
51
|
+
};
|
|
29
52
|
}
|
|
30
53
|
function backgroundStartBusyReason(c) {
|
|
31
54
|
const activeJobId = typeof c.queueManager?.getActiveJobId === 'function' ? c.queueManager.getActiveJobId() : null;
|
|
@@ -30,6 +30,7 @@ const ticket_watcher_1 = require("./modules/specs/runtime/ticket-watcher");
|
|
|
30
30
|
const terminal_manager_1 = require("./modules/terminals/runtime/terminal-manager");
|
|
31
31
|
const browser_capture_manager_1 = require("./modules/browser/runtime/browser-capture-manager");
|
|
32
32
|
const browser_context_pool_1 = require("./modules/browser/runtime/browser-context-pool");
|
|
33
|
+
const preview_targets_1 = require("./modules/browser/runtime/preview-targets");
|
|
33
34
|
const explore_cwd_manager_1 = require("./modules/conversations/runtime/explore-cwd-manager");
|
|
34
35
|
const hooks_1 = require("./hooks");
|
|
35
36
|
const transient_children_1 = require("./transient-children");
|
|
@@ -119,6 +120,9 @@ class ProjectRegistry {
|
|
|
119
120
|
// Chromium profile under ~/.specrails/browser-profile, so cookies/logins are
|
|
120
121
|
// shared across every project. Launched lazily, disposed once at shutdown().
|
|
121
122
|
_browserContextPool;
|
|
123
|
+
// Companion app preview: URLs announced by running mission processes,
|
|
124
|
+
// derived on demand from their logs (shared by every project's browser).
|
|
125
|
+
_previewTargets = (0, preview_targets_1.createPreviewTargetSource)();
|
|
122
126
|
constructor(broadcast, desktopDbPath, desktopPort) {
|
|
123
127
|
this._broadcast = broadcast;
|
|
124
128
|
this._desktopDb = (0, desktop_db_1.initDesktopDb)(desktopDbPath ?? (0, desktop_db_1.getDesktopDbPath)());
|
|
@@ -128,6 +132,9 @@ class ProjectRegistry {
|
|
|
128
132
|
this._failedProjects = new Map();
|
|
129
133
|
this._browserContextPool = new browser_context_pool_1.SharedBrowserContextPool();
|
|
130
134
|
}
|
|
135
|
+
get previewTargets() {
|
|
136
|
+
return this._previewTargets;
|
|
137
|
+
}
|
|
131
138
|
get desktopDb() {
|
|
132
139
|
return this._desktopDb;
|
|
133
140
|
}
|
|
@@ -1136,6 +1143,9 @@ class ProjectRegistry {
|
|
|
1136
1143
|
// Share ONE persistent Chromium profile (global cookies/logins) across all
|
|
1137
1144
|
// projects instead of a per-project profile.
|
|
1138
1145
|
contextPool: this._browserContextPool,
|
|
1146
|
+
// Only device-owned (Companion preview) sessions may load these loopback
|
|
1147
|
+
// origins; Desktop sessions keep the regular SSRF guard.
|
|
1148
|
+
previewOrigins: () => this._previewTargets.announcedOrigins(project.id),
|
|
1139
1149
|
});
|
|
1140
1150
|
// ── Loops engine (the Loops feature) ──────────────────────────────────────
|
|
1141
1151
|
const railLoopRuns = new Map();
|