specrails-desktop 2.30.1 → 2.30.3
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-AQgpr9qG.js → ActivityFeedPage-CcBOllqm.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-I7a0usG7.js → AgentBrowserCapture-BOJkZUj_.js} +1 -1
- package/client/dist/assets/{AgentModeCodePane-BqOVg15n.js → AgentModeCodePane-BIh67rGi.js} +2 -2
- package/client/dist/assets/{AgentModeJobsPane-4SG6FF27.js → AgentModeJobsPane-C5tkU2Ed.js} +1 -1
- package/client/dist/assets/{AgentsPage-C3FgYlEi.js → AgentsPage-CNlE_Uh4.js} +1 -1
- package/client/dist/assets/{AnalyticsPage-BcMNByW5.js → AnalyticsPage-cYcJxWPn.js} +1 -1
- package/client/dist/assets/{CodePage-CgdDkXp-.js → CodePage-D4QYa4t1.js} +1 -1
- package/client/dist/assets/{DesktopAnalyticsPage-DPMcS88n.js → DesktopAnalyticsPage-DCmgYV86.js} +1 -1
- package/client/dist/assets/{DocsDialog-BC7574Zi.js → DocsDialog-BXnOgm7V.js} +1 -1
- package/client/dist/assets/{DocsPage-Dd9yilUg.js → DocsPage-nFkHh-Dc.js} +1 -1
- package/client/dist/assets/{InteractiveJobComposer-CqC_0VQL.js → InteractiveJobComposer-qoeZjwVY.js} +1 -1
- package/client/dist/assets/{JobDetailModal-BFNXOhk1.js → JobDetailModal-ChRBB9bc.js} +1 -1
- package/client/dist/assets/{JobDetailPage-DM-6zYpA.js → JobDetailPage-BuOnlndB.js} +1 -1
- package/client/dist/assets/{JobsPage-1dkjPk1k.js → JobsPage-C_UEIXWA.js} +1 -1
- package/client/dist/assets/{LoopBuilderPage-BPeqz6cG.js → LoopBuilderPage-BV_wge0g.js} +1 -1
- package/client/dist/assets/{LoopPreviewModal-EdkyYaGN.js → LoopPreviewModal-Qk_Jaxo8.js} +1 -1
- package/client/dist/assets/{LoopsPage-nZfgiaK8.js → LoopsPage-lLi6ax0p.js} +1 -1
- package/client/dist/assets/{PluginsPage-CwFfFBzW.js → PluginsPage-DZIFv9kw.js} +1 -1
- package/client/dist/assets/{ProjectSettingsDialog-DHsxLFMT.js → ProjectSettingsDialog-DEHcyt_I.js} +1 -1
- package/client/dist/assets/{TemplatePreviewModal-wcI3nGNr.js → TemplatePreviewModal-Bxbtnzob.js} +1 -1
- package/client/dist/assets/{TicketDetailModal-7EloKP1C.js → TicketDetailModal-CqQ5gx7O.js} +2 -2
- package/client/dist/assets/{dist-js-Bt2zW2YG.js → dist-js-CBhuoTO6.js} +1 -1
- package/client/dist/assets/{dist-js-zvt5ap3o.js → dist-js-eSxaoEw0.js} +1 -1
- package/client/dist/assets/{index-xkzK3E5i.js → index-CewRMo0W.js} +10 -10
- package/client/dist/index.html +2 -2
- package/package.json +1 -1
- package/server/dist/core-update-manager.js +27 -4
- package/server/dist/framework-manager.js +41 -10
package/client/dist/index.html
CHANGED
|
@@ -204,7 +204,7 @@
|
|
|
204
204
|
.specrails-splash__mark .pill { opacity: 1; }
|
|
205
205
|
}
|
|
206
206
|
</style>
|
|
207
|
-
<script type="module" crossorigin src="/assets/index-
|
|
207
|
+
<script type="module" crossorigin src="/assets/index-CewRMo0W.js"></script>
|
|
208
208
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-CNC7AqOf.js">
|
|
209
209
|
<link rel="modulepreload" crossorigin href="/assets/jsx-runtime-zu2_FqZY.js">
|
|
210
210
|
<link rel="modulepreload" crossorigin href="/assets/react-dom-DiWzBDP9.js">
|
|
@@ -276,7 +276,7 @@
|
|
|
276
276
|
<link rel="modulepreload" crossorigin href="/assets/getTimezoneOffsetInMilliseconds-qrcBaLW1.js">
|
|
277
277
|
<link rel="modulepreload" crossorigin href="/assets/formatDistanceToNow-ByKli7Q_.js">
|
|
278
278
|
<link rel="modulepreload" crossorigin href="/assets/spending-DMnCXy84.js">
|
|
279
|
-
<link rel="modulepreload" crossorigin href="/assets/TicketDetailModal-
|
|
279
|
+
<link rel="modulepreload" crossorigin href="/assets/TicketDetailModal-CqQ5gx7O.js">
|
|
280
280
|
<link rel="stylesheet" crossorigin href="/assets/index-Dh9i4CnN.css">
|
|
281
281
|
</head>
|
|
282
282
|
<body>
|
package/package.json
CHANGED
|
@@ -116,7 +116,13 @@ class CoreUpdateManager {
|
|
|
116
116
|
}
|
|
117
117
|
this.updating = true;
|
|
118
118
|
this.emit('downloading', { version: requested });
|
|
119
|
-
|
|
119
|
+
// realpath the tmp root: on macOS os.tmpdir() is a symlinked path
|
|
120
|
+
// (/var/folders → /private/var/folders). The staged core's cli.js has an
|
|
121
|
+
// auto-run guard comparing import.meta.url (realpathed by Node's ESM
|
|
122
|
+
// loader) against process.argv[1] (the literal spawn arg) — a symlinked
|
|
123
|
+
// staging path makes them differ, so main() never runs and every
|
|
124
|
+
// install-framework/swap-current child exits 0 as a silent no-op.
|
|
125
|
+
const tmp = fs_1.default.mkdtempSync(path_1.default.join(realTmpDir(), 'core-update-'));
|
|
120
126
|
try {
|
|
121
127
|
this.npmInstallFn(`${exports.CORE_PACKAGE}@${requested}`, tmp);
|
|
122
128
|
const coreRoot = path_1.default.join(tmp, 'node_modules', exports.CORE_PACKAGE);
|
|
@@ -136,9 +142,16 @@ class CoreUpdateManager {
|
|
|
136
142
|
if (mat.errors.length > 0) {
|
|
137
143
|
throw new Error(`framework materialize failed: ${mat.errors.map((e) => `${e.provider}: ${e.message}`).join('; ')}`);
|
|
138
144
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
145
|
+
// A materialize that reports no errors but ALSO no materialized providers
|
|
146
|
+
// wrote nothing — swapping `current` at a nonexistent version dir would
|
|
147
|
+
// fail downstream with a misleading "swap-current failed". Surface the
|
|
148
|
+
// real condition instead (mirrors FrameworkManager.versionCheck's guard).
|
|
149
|
+
if (mat.providers.length === 0) {
|
|
150
|
+
throw new Error(`framework materialize completed without materializing any provider (requested: ${providers.join(', ') || 'none'})`);
|
|
151
|
+
}
|
|
152
|
+
const swapped = fm.swapCurrentDetailed(installed);
|
|
153
|
+
if (!swapped.ok) {
|
|
154
|
+
throw new Error(`framework swap-current failed${swapped.detail ? `: ${swapped.detail}` : ''}`);
|
|
142
155
|
}
|
|
143
156
|
this.latestVersion = installed;
|
|
144
157
|
this.lastCheckedAt = Date.now();
|
|
@@ -180,6 +193,16 @@ function uniqueProviders(values) {
|
|
|
180
193
|
const out = Array.from(new Set(values.filter((v) => typeof v === 'string' && v.length > 0)));
|
|
181
194
|
return out.length > 0 ? out : ['claude'];
|
|
182
195
|
}
|
|
196
|
+
/** os.tmpdir() with symlinks resolved (macOS /var/folders → /private/var/folders). */
|
|
197
|
+
function realTmpDir() {
|
|
198
|
+
const dir = os_1.default.tmpdir();
|
|
199
|
+
try {
|
|
200
|
+
return fs_1.default.realpathSync(dir);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return dir;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
183
206
|
/** GET the latest published version from the npm registry (no npm binary needed). */
|
|
184
207
|
async function fetchLatestFromRegistry() {
|
|
185
208
|
const controller = new AbortController();
|
|
@@ -26,6 +26,22 @@ const path_resolver_1 = require("./path-resolver");
|
|
|
26
26
|
function nodeInterpreter() {
|
|
27
27
|
return (0, path_resolver_1.resolveBundledNodeExe)() ?? process.execPath;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve symlinks in the CLI path we pass as the child's argv[1]. Core's
|
|
31
|
+
* cli.js auto-run guard compares import.meta.url (realpathed by Node's ESM
|
|
32
|
+
* loader) against pathToFileURL(process.argv[1]) — a symlinked argv[1] (e.g.
|
|
33
|
+
* a staging dir under macOS /var/folders) makes them differ, so main() never
|
|
34
|
+
* runs and the child exits 0 as a silent no-op. Realpathing here keeps the
|
|
35
|
+
* guard true regardless of where the core package lives.
|
|
36
|
+
*/
|
|
37
|
+
function realpathOrSelf(p) {
|
|
38
|
+
try {
|
|
39
|
+
return fs_1.default.realpathSync(p);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return p;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
29
45
|
/** `~/.specrails/framework` — same home as the registry. */
|
|
30
46
|
function frameworkRoot(home) {
|
|
31
47
|
return path_1.default.join((0, artifact_registry_1.resolveHome)(home), '.specrails', 'framework');
|
|
@@ -121,9 +137,10 @@ class FrameworkManager {
|
|
|
121
137
|
resolveCli() {
|
|
122
138
|
if (this.coreRoot) {
|
|
123
139
|
const cli = path_1.default.join(this.coreRoot, 'dist', 'installer', 'cli.js');
|
|
124
|
-
return fs_1.default.existsSync(cli) ? cli : null;
|
|
140
|
+
return fs_1.default.existsSync(cli) ? realpathOrSelf(cli) : null;
|
|
125
141
|
}
|
|
126
|
-
|
|
142
|
+
const bundled = (0, bundled_core_1.getBundledCoreCli)();
|
|
143
|
+
return bundled ? realpathOrSelf(bundled) : null;
|
|
127
144
|
}
|
|
128
145
|
/** True when a usable core (override or bundled) is present (else methods no-op). */
|
|
129
146
|
isAvailable() {
|
|
@@ -204,16 +221,30 @@ class FrameworkManager {
|
|
|
204
221
|
* `swap-current` only moves the version pointer (provider-invariant).
|
|
205
222
|
*/
|
|
206
223
|
swapCurrent(version, _provider = 'claude') {
|
|
224
|
+
return this.swapCurrentDetailed(version).ok;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Like `swapCurrent`, but on failure carries WHY it failed (`detail`) — the
|
|
228
|
+
* subprocess stderr/exit or the missing-CLI/verify-mismatch case — so callers
|
|
229
|
+
* (the core update channel) can surface an actionable error instead of the
|
|
230
|
+
* bare "swap-current failed".
|
|
231
|
+
*/
|
|
232
|
+
swapCurrentDetailed(version) {
|
|
207
233
|
const cli = this.resolveCli();
|
|
208
234
|
if (!cli)
|
|
209
|
-
return false;
|
|
235
|
+
return { ok: false, detail: 'no usable core CLI to run swap-current with' };
|
|
210
236
|
if (readCurrentFrameworkVersion(this.home) === version)
|
|
211
|
-
return true;
|
|
237
|
+
return { ok: true, detail: null };
|
|
212
238
|
const fwDir = frameworkRoot(this.home);
|
|
213
239
|
const res = (0, child_process_1.spawnSync)(nodeInterpreter(), [cli, 'swap-current', '--framework-dir', fwDir, '--version', version], { env: this.childEnv(), encoding: 'utf-8', timeout: 120_000 });
|
|
214
|
-
if (res.error || (res.status ?? 1) !== 0)
|
|
215
|
-
|
|
216
|
-
|
|
240
|
+
if (res.error || (res.status ?? 1) !== 0) {
|
|
241
|
+
const detail = (res.error?.message ?? `${res.stderr || res.stdout || ''}`.trim()) || `exit ${res.status ?? 'unknown'}`;
|
|
242
|
+
return { ok: false, detail };
|
|
243
|
+
}
|
|
244
|
+
if (readCurrentFrameworkVersion(this.home) !== version) {
|
|
245
|
+
return { ok: false, detail: `swap-current exited 0 but current still resolves to ${readCurrentFrameworkVersion(this.home) ?? 'nothing'}` };
|
|
246
|
+
}
|
|
247
|
+
return { ok: true, detail: null };
|
|
217
248
|
}
|
|
218
249
|
/**
|
|
219
250
|
* Compare the bundled core version with `framework/current`. When they differ
|
|
@@ -266,8 +297,8 @@ class FrameworkManager {
|
|
|
266
297
|
// No errors yet nothing materialized (empty/duplicate-only provider list).
|
|
267
298
|
return { swapped: false, version: current };
|
|
268
299
|
}
|
|
269
|
-
const
|
|
270
|
-
if (ok) {
|
|
300
|
+
const swap = this.swapCurrentDetailed(bundled);
|
|
301
|
+
if (swap.ok) {
|
|
271
302
|
try {
|
|
272
303
|
this.broadcast?.({ type: 'framework.updated', version: bundled });
|
|
273
304
|
}
|
|
@@ -281,7 +312,7 @@ class FrameworkManager {
|
|
|
281
312
|
this.broadcast?.({
|
|
282
313
|
type: 'framework.update_failed',
|
|
283
314
|
version: bundled,
|
|
284
|
-
errors: [{ provider: providers[0] ?? 'claude', message:
|
|
315
|
+
errors: [{ provider: providers[0] ?? 'claude', message: `swap-current failed${swap.detail ? `: ${swap.detail}` : ''}` }],
|
|
285
316
|
});
|
|
286
317
|
}
|
|
287
318
|
catch {
|