@loicngr/kobo 1.11.36 → 1.11.37
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/AGENTS.md +15 -0
- package/CHANGELOG.md +4 -0
- package/dist/server/index.js +11 -24
- package/dist/server/routes/dev-server.js +2 -1
- package/dist/server/routes/pull-requests.js +5 -4
- package/dist/server/routes/workspaces.js +122 -91
- package/dist/server/services/agent/engines/codex/client.js +1 -0
- package/dist/server/services/agent/engines/codex/engine.js +47 -10
- package/dist/server/services/agent/engines/codex/event-mapper.js +5 -1
- package/dist/server/services/agent/engines/codex/jsonrpc/peer.js +21 -8
- package/dist/server/services/agent/engines/codex/jsonrpc/transport.js +30 -3
- package/dist/server/services/agent/orchestrator.js +146 -68
- package/dist/server/services/agent/session-controller.js +12 -2
- package/dist/server/services/auto-loop-service.js +57 -3
- package/dist/server/services/change-source-branch-service.js +36 -4
- package/dist/server/services/cron-service.js +19 -0
- package/dist/server/services/db-backup-service.js +30 -0
- package/dist/server/services/dev-server-service.js +167 -38
- package/dist/server/services/image-service.js +25 -28
- package/dist/server/services/pr-checkout-service.js +34 -5
- package/dist/server/services/quota-backoff-service.js +25 -8
- package/dist/server/services/wakeup-service.js +23 -3
- package/dist/server/services/worktree-purge-service.js +3 -7
- package/dist/server/utils/agent-stop-result.js +12 -0
- package/dist/server/utils/git-ops.js +8 -1
- package/dist/server/utils/git-worktree-fingerprint.js +86 -0
- package/dist/server/utils/safe-path.js +21 -0
- package/dist/server/utils/script-runner.js +32 -9
- package/dist/server/utils/workspace-lifecycle-guard.js +27 -0
- package/package.json +1 -1
- package/src/client/dist/pwa/assets/{ActivityFeed-Bakgvay6.css → ActivityFeed-BDyFBoxd.css} +1 -1
- package/src/client/dist/pwa/assets/ActivityFeed-C_-hxIo8.js +8 -0
- package/src/client/dist/pwa/assets/{ChangelogPage-BFGYQ6eY.js → ChangelogPage-CBlGJ4zT.js} +1 -1
- package/src/client/dist/pwa/assets/{ComparisonPanel-C6DfjpZZ.js → ComparisonPanel-DZAE2X9j.js} +1 -1
- package/src/client/dist/pwa/assets/CreatePage-A3pVradx.css +1 -0
- package/src/client/dist/pwa/assets/CreatePage-ArmtcF4d.js +2 -0
- package/src/client/dist/pwa/assets/{DashboardPage-DsVRjzzy.js → DashboardPage-eQTCXcdz.js} +1 -1
- package/src/client/dist/pwa/assets/{DiffViewer-DSlSAIiW.js → DiffViewer-CaT7vuSr.js} +3 -3
- package/src/client/dist/pwa/assets/{DocumentsPanel-C1eXTw_Q.js → DocumentsPanel-A0isCTaI.js} +1 -1
- package/src/client/dist/pwa/assets/{DrawerToggleButton-BxYpUb4y.js → DrawerToggleButton-T70fh8dp.js} +1 -1
- package/src/client/dist/pwa/assets/{GitPanel-DnBWSdVp.js → GitPanel-CAvk0G7h.js} +3 -3
- package/src/client/dist/pwa/assets/{HealthPage-rX1TiaP9.js → HealthPage-D8Mr0V6w.js} +1 -1
- package/src/client/dist/pwa/assets/{MainLayout-Ceuh5uJj.js → MainLayout-x6Es4Da-.js} +2 -2
- package/src/client/dist/pwa/assets/{QBtnDropdown-K7syiyyH.js → QBtnDropdown-CQqzzlFI.js} +1 -1
- package/src/client/dist/pwa/assets/{QDrawer-DapzE8DH.js → QDrawer-Cwj2qh0r.js} +1 -1
- package/src/client/dist/pwa/assets/{QSelect-Bodml89k.js → QSelect-BSPtuXJg.js} +1 -1
- package/src/client/dist/pwa/assets/{QTooltip-K-9OdDfq.js → QTooltip-DC_kDKku.js} +1 -1
- package/src/client/dist/pwa/assets/{SchedulePanel-CfumTzAa.js → SchedulePanel-9aJSoz6Y.js} +1 -1
- package/src/client/dist/pwa/assets/{SearchPage-AhZYS-dS.js → SearchPage-BjNvtE_U.js} +1 -1
- package/src/client/dist/pwa/assets/{SettingsPage-BoHXWvKn.js → SettingsPage-CvLnT73V.js} +1 -1
- package/src/client/dist/pwa/assets/{SplitWorkspacePage-3wK11mcu.js → SplitWorkspacePage-DTao0DHr.js} +1 -1
- package/src/client/dist/pwa/assets/{TerminalPanel-CKg0BeNK.js → TerminalPanel-BrKIW-bJ.js} +1 -1
- package/src/client/dist/pwa/assets/{TouchPan-D6ctyPYf.js → TouchPan-_n1AQgoo.js} +1 -1
- package/src/client/dist/pwa/assets/{TourReplayButton-Dn4Dy_kH.js → TourReplayButton-CJF3nVvn.js} +1 -1
- package/src/client/dist/pwa/assets/{WorkspaceAttentionLabels-DnoabsfK.js → WorkspaceAttentionLabels-CxesUiIm.js} +1 -1
- package/src/client/dist/pwa/assets/{WorkspacePage-DYQrfeYw.css → WorkspacePage-DoXtwpjV.css} +1 -1
- package/src/client/dist/pwa/assets/WorkspacePage-MxBE0mr3.js +4 -0
- package/src/client/dist/pwa/assets/{browser-DHpSUK8-.js → browser-DGWpkvN3.js} +1 -1
- package/src/client/dist/pwa/assets/{build-path-tree-DtEjsZlh.js → build-path-tree-Ba1dSLgi.js} +1 -1
- package/src/client/dist/pwa/assets/{de-Dt_m-H83.js → de-DVK21RB6.js} +1 -1
- package/src/client/dist/pwa/assets/{documents-B2dg1wRf.js → documents-BMHzjW_g.js} +1 -1
- package/src/client/dist/pwa/assets/{editor.api-Cd1D35oj.js → editor.api-CYTuNwqu.js} +1 -1
- package/src/client/dist/pwa/assets/{efforts-IB1vPjJQ.js → efforts-BPlRSHXp.js} +1 -1
- package/src/client/dist/pwa/assets/{engineFeatures-DYSyDEJe.js → engineFeatures-DVB2rklO.js} +1 -1
- package/src/client/dist/pwa/assets/{es-DFg25DCS.js → es-C8cQwDZM.js} +1 -1
- package/src/client/dist/pwa/assets/{expand-template-CNAHiesL.js → expand-template-Cj82YE-O.js} +1 -1
- package/src/client/dist/pwa/assets/{format-DidwgbbX.js → format-Dk7O3LhA.js} +1 -1
- package/src/client/dist/pwa/assets/{formatters-BjzyUHlu.js → formatters-TGjvs7Ca.js} +1 -1
- package/src/client/dist/pwa/assets/{fr-CQ1slpol.js → fr-BVOMZ2Bl.js} +1 -1
- package/src/client/dist/pwa/assets/{freemarker2-jNGWawVw.js → freemarker2-39lO_dWH.js} +1 -1
- package/src/client/dist/pwa/assets/{handlebars-DfSqClrW.js → handlebars-M_0V82cD.js} +1 -1
- package/src/client/dist/pwa/assets/{html-ByLX-n8T.js → html-yILqYDsl.js} +1 -1
- package/src/client/dist/pwa/assets/i18n-2UeMfd5m.js +1 -0
- package/src/client/dist/pwa/assets/{index-DcwcFDrZ.js → index-BJU5dnkF.js} +2 -2
- package/src/client/dist/pwa/assets/{it-w09rfaP7.js → it-CxkAoOj4.js} +1 -1
- package/src/client/dist/pwa/assets/{javascript-Dja1fHfv.js → javascript-DrW-amDc.js} +1 -1
- package/src/client/dist/pwa/assets/{liquid-D315hW6C.js → liquid-D_mXmUoL.js} +1 -1
- package/src/client/dist/pwa/assets/{mdx-B5yUwpzv.js → mdx-E8O3lvR5.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco-contributions-NfxrU0r3.js → monaco-contributions-C2K3sl_V.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco.contribution-D1hXPkDZ.js → monaco.contribution-DLhHQIJn.js} +2 -2
- package/src/client/dist/pwa/assets/{network-auth-CARVtbNe.js → network-auth-BUwWtnui.js} +1 -1
- package/src/client/dist/pwa/assets/{notify-theme-CKC3-EAZ.js → notify-theme-1mOpY2vN.js} +1 -1
- package/src/client/dist/pwa/assets/{pending-diff-open-BlLrQaW7.js → pending-diff-open-BL7dKRqF.js} +1 -1
- package/src/client/dist/pwa/assets/{permissionModes-DMzFEQhr.js → permissionModes-BtC3_P-P.js} +1 -1
- package/src/client/dist/pwa/assets/{project-color-CFjdIj6W.js → project-color-BFBFZWeG.js} +1 -1
- package/src/client/dist/pwa/assets/{python-Dc6mnEip.js → python-DoZAYim6.js} +1 -1
- package/src/client/dist/pwa/assets/{razor-DArVFBGO.js → razor-9bZ30g2y.js} +1 -1
- package/src/client/dist/pwa/assets/{render-chat-markdown-BTPAjU2g.js → render-chat-markdown-7EtWNjG0.js} +1 -1
- package/src/client/dist/pwa/assets/{typescript-COE1vIyB.js → typescript-C1CBfL3L.js} +1 -1
- package/src/client/dist/pwa/assets/{use-tours-C8x2V1w5.js → use-tours-BJ81clv6.js} +1 -1
- package/src/client/dist/pwa/assets/{workspace-templates-DHyA_GEW.js → workspace-templates-r1-bipjM.js} +1 -1
- package/src/client/dist/pwa/assets/{xml-eYjr4cGR.js → xml-COuu5isK.js} +1 -1
- package/src/client/dist/pwa/assets/{yaml-UooOcssF.js → yaml-ChZ7WqaJ.js} +1 -1
- package/src/client/dist/pwa/index.html +1 -1
- package/src/client/dist/pwa/sw.js +1 -1
- package/src/client/dist/pwa/assets/ActivityFeed-BPx-xhFY.js +0 -8
- package/src/client/dist/pwa/assets/CreatePage-DxOyY65u.css +0 -1
- package/src/client/dist/pwa/assets/CreatePage-ypEzkAKv.js +0 -2
- package/src/client/dist/pwa/assets/WorkspacePage-BI7CW2yT.js +0 -4
- package/src/client/dist/pwa/assets/i18n-CnjXQjxy.js +0 -1
package/AGENTS.md
CHANGED
|
@@ -239,6 +239,8 @@ Each event maps to one effective-settings key (`sessionEndedScript` / `prMergedS
|
|
|
239
239
|
|
|
240
240
|
### Engine comparison
|
|
241
241
|
|
|
242
|
+
Successful paired creation opens the existing `split` route with `left` and `right` workspace ids in creation order. A single successful creation keeps the normal workspace route, including when the second creation fails.
|
|
243
|
+
|
|
242
244
|
One task, two engines, two sibling worktrees. `POST /api/workspaces` accepts a `comparisonId`; the client posts twice with the same one (sequentially — parallel creations contend on the git index lock), suffixing the name with the engine's display name and the branch with the engine id and giving the non-configured engine the model picked for it on the form and its own effort / permission defaults. `workspaces.comparison_id` (migration v40) groups them; `listComparisonMembers` reads them back, treating an empty id as no group rather than as a match against the NULL majority. `GET /api/workspaces/:id/comparison` returns each member with its cached git stats (`null`, never zeros, when nothing has been measured). `ComparisonPanel.vue` renders the table at the top of the Git tab, only for workspaces that belong to a comparison. See [CONFIGURATION.md → Comparing two engines on one task](CONFIGURATION.md#comparing-two-engines-on-one-task).
|
|
243
245
|
|
|
244
246
|
### Unanswered question reminder
|
|
@@ -384,3 +386,16 @@ deviate from the documented system without explicit user approval.
|
|
|
384
386
|
- Don't skip `try/catch` swallowing on best-effort cleanup (agent stop, dev-server stop, worktree removal). These must never break the primary operation.
|
|
385
387
|
- Don't hardcode user-visible text in the frontend. Every string must go through `$t()` / `t()` with keys in all 5 locale files. See [Internationalization (i18n)](#internationalization-i18n).
|
|
386
388
|
- Don't hardcode hex colors, spacing literals, or font names in components. Use the CSS variables from `src/client/src/css/design-tokens.scss` and the patterns in `DESIGN.md`. See [Design System](#design-system).
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
## Audit remediation contracts (September 2026)
|
|
392
|
+
|
|
393
|
+
- `stopAgentAndWait` returns `stopped | not-running | timeout | failed`. Destructive callers must pass its result through `assertAgentStopped`. A timeout or failure retains the same controller in `stopping` until actual stop completion; it never authorizes a replacement or worktree removal. Pending replacements remain cancellable and are finalized as `killed` when stopped before starting. Dev-server shutdown retains ownership of the entire process group until no live member remains, including when the shell exits first; process inspection failures propagate to destructive callers. A successful custom stop can override a failed generic Docker shutdown only after an independent successful query confirms no running containers with the exact project label.
|
|
394
|
+
- Acquire `withWorkspaceLifecycleGuard` before `withGitRepoLock`. Agent/dev-server starts check lifecycle ownership centrally. Wakeups and cron defer while busy; auto-loop waiters are reconsidered on guard release and confirmed capacity release. A user stop persistently disables auto-loop, including while waiting for capacity without a controller. Internal stops explicitly pass `replacement`, `setup`, `archive` or `shutdown` to preserve that intent; delete and purge retain their dedicated causes.
|
|
395
|
+
- Quota-backoff `arm` receives the authoritative next `retryCount` from the orchestrator. Persist that value directly; consuming the preceding timer row must not reset the retry sequence.
|
|
396
|
+
- A quota retry with a residual controller awaits a confirmed technical stop before starting another iteration. Persist recovery before that await and retain its attempt count if the stop remains unconfirmed. Shutdown suspends quota, wakeup and cron timers without consuming their rows; late arms stay persisted but do not deliver until boot restores the schedulers. Automatic continuations also check shutdown when deferred lifecycle callbacks run, preserving auto-loop intent instead of treating a refused start as an error.
|
|
397
|
+
- Codex uses a 120-second stream-idle deadline and a bounded 30-minute deadline while normalized foreground tool calls remain unresolved. Synthetic `TodoWrite` calls emitted for completed plans must have matching results so they do not keep the tool deadline active. Human-input pauses remain independent of the tool timeout.
|
|
398
|
+
- Codex explicit stop confirms `exit` after each escalation, including `SIGKILL`; an unconfirmed exit rejects the stop instead of authorizing destructive follow-up. Exit listeners and deadlines are removed when that wait settles.
|
|
399
|
+
- Conversation loading indicators follow outstanding session/history/sync requests only while no content is available. Background refreshes must preserve the displayed conversation, component state and reading position, including feeds containing only user messages.
|
|
400
|
+
- PR diagnosis fingerprints are asynchronous and include binary staged/unstaged diffs and untracked file contents (symlink targets, not their destinations). Untracked nested Git repositories are fingerprinted recursively, including HEAD and index changes; ignored files and Git internals are excluded. Await them at every callsite. Workspace matching always includes normalized project identity.
|
|
401
|
+
- Daily DB backups are checked at startup and hourly, with the existing 24-hour minimum age and retention. Stop the scheduler and await its active backup before closing SQLite.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,10 @@ All notable changes to Kōbō are documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/). Each release is an `## <version>`
|
|
5
5
|
section — the in-app "What's new" dialog reads this file.
|
|
6
6
|
|
|
7
|
+
## 1.11.37
|
|
8
|
+
|
|
9
|
+
- fix(kobo): harden workspace lifecycle and comparison navigation
|
|
10
|
+
|
|
7
11
|
## 1.11.36
|
|
8
12
|
|
|
9
13
|
- fix(agent): resume quota waits and scheduled background checks
|
package/dist/server/index.js
CHANGED
|
@@ -29,12 +29,12 @@ import usageRoutes from './routes/usage.js';
|
|
|
29
29
|
import voiceRouter from './routes/voice.js';
|
|
30
30
|
import workspaceTemplatesRouter from './routes/workspace-templates.js';
|
|
31
31
|
import workspacesRouter from './routes/workspaces.js';
|
|
32
|
-
import { getAvailableSkills, isAgentUnavailableError, reconcileOrphanSessions, restoreRetryCountsFromDb, sendMessage, setBackendPort, startAgent, startWatchdog,
|
|
32
|
+
import { getAvailableSkills, isAgentUnavailableError, reconcileOrphanSessions, restoreRetryCountsFromDb, sendMessage, setBackendPort, startAgent, startWatchdog, stopAgentAndWait, stopAllAgents, stopWatchdog, } from './services/agent/orchestrator.js';
|
|
33
33
|
import * as autoLoopService from './services/auto-loop-service.js';
|
|
34
34
|
import { startAwaitingUserReminder, stopAwaitingUserReminder } from './services/awaiting-user-reminder-service.js';
|
|
35
35
|
import { runContentMigrationIfNeeded } from './services/content-migration-service.js';
|
|
36
36
|
import * as cronService from './services/cron-service.js';
|
|
37
|
-
import { createDailyDbBackupIfNeeded, createPreMigrationBackup } from './services/db-backup-service.js';
|
|
37
|
+
import { createDailyDbBackupIfNeeded, createPreMigrationBackup, startDailyDbBackupScheduler, } from './services/db-backup-service.js';
|
|
38
38
|
import { startDevServer, stopAllDevServers, stopDevServer } from './services/dev-server-service.js';
|
|
39
39
|
import { authorizeWsUpgrade, generateToken, getLanHostnames, getLanUrls, isAllowedOrigin, isAllowedRequestHost, resolveBindHost, resolveDevClientOrigin, resolveNetworkAccessEnvOverrides, resolveProxyHostname, } from './services/network-access-service.js';
|
|
40
40
|
import { startPrWatcher, stopPrWatcher } from './services/pr-watcher-service.js';
|
|
@@ -101,23 +101,11 @@ runRetentionPass('at boot');
|
|
|
101
101
|
const RETENTION_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
102
102
|
const retentionTimer = setInterval(() => runRetentionPass('daily'), RETENTION_INTERVAL_MS);
|
|
103
103
|
retentionTimer.unref?.();
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (r.created) {
|
|
110
|
-
startupDebug(`[kobo] Daily DB backup: ${r.created}`);
|
|
111
|
-
if (r.deleted.length > 0) {
|
|
112
|
-
startupDebug(`[kobo] Rotated ${r.deleted.length} old DB backup(s)`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
})
|
|
116
|
-
.catch((err) => {
|
|
117
|
-
// A full disk or an unwritable backup directory must not stop the server
|
|
118
|
-
// from booting: without this catch the rejection reaches the last-resort
|
|
119
|
-
// handler below and Kōbō exits before it ever listens.
|
|
120
|
-
console.error('[kobo] Daily DB backup failed (continuing):', err);
|
|
104
|
+
// Check on boot and hourly so servers running longer than a day keep backing up.
|
|
105
|
+
const dailyBackupScheduler = startDailyDbBackupScheduler(async () => {
|
|
106
|
+
const result = await createDailyDbBackupIfNeeded(db, getDbPath());
|
|
107
|
+
if (result.created)
|
|
108
|
+
startupDebug(`[kobo] Daily DB backup: ${result.created}`);
|
|
121
109
|
});
|
|
122
110
|
// Initialize process cleanup, agent watchdog, PR watcher, and wakeup rehydration
|
|
123
111
|
reconcileOrphanSessions();
|
|
@@ -128,7 +116,7 @@ autoLoopService.rehydrate();
|
|
|
128
116
|
// otherwise the next arm() after restart would compute the next ladder rung
|
|
129
117
|
// from retryCount=0 and undo the progression.
|
|
130
118
|
restoreRetryCountsFromDb();
|
|
131
|
-
quotaBackoffService.restoreOnBoot((workspaceId) => autoLoopService.onQuotaBackoffExpired(workspaceId));
|
|
119
|
+
quotaBackoffService.restoreOnBoot((workspaceId, pending) => autoLoopService.onQuotaBackoffExpired(workspaceId, pending));
|
|
132
120
|
cronService.restoreOnBoot();
|
|
133
121
|
// Deliver any new default prompt templates to existing installs (seed-once via the
|
|
134
122
|
// seededDefaultSlugs watermark; never overwrites or re-adds deleted defaults).
|
|
@@ -453,10 +441,8 @@ setMessageHandler(async (type, payload) => {
|
|
|
453
441
|
}
|
|
454
442
|
if (type === 'workspace:stop' && p?.workspaceId) {
|
|
455
443
|
try {
|
|
456
|
-
//
|
|
457
|
-
|
|
458
|
-
autoLoopService.disable(p.workspaceId, 'user-action');
|
|
459
|
-
stopAgent(p.workspaceId);
|
|
444
|
+
// Share the HTTP stop contract, including loops waiting without an agent.
|
|
445
|
+
await stopAgentAndWait(p.workspaceId);
|
|
460
446
|
}
|
|
461
447
|
catch (err) {
|
|
462
448
|
console.error('[ws] Failed to stop agent:', err);
|
|
@@ -613,6 +599,7 @@ async function gracefulShutdown(signal, exitCode = 0) {
|
|
|
613
599
|
}
|
|
614
600
|
finally {
|
|
615
601
|
try {
|
|
602
|
+
await dailyBackupScheduler.stop();
|
|
616
603
|
closeDb();
|
|
617
604
|
console.log('[kobo] Database closed');
|
|
618
605
|
}
|
|
@@ -3,6 +3,7 @@ import { migrationGuard } from '../middleware/migration-guard.js';
|
|
|
3
3
|
import { getDevServerLogs, getStatus, startDevServer, stopDevServer } from '../services/dev-server-service.js';
|
|
4
4
|
import { getProjectSettings } from '../services/settings-service.js';
|
|
5
5
|
import { getWorkspace } from '../services/workspace-service.js';
|
|
6
|
+
import { WorkspaceLifecycleBusyError } from '../utils/workspace-lifecycle-guard.js';
|
|
6
7
|
/** Hono sub-router for per-workspace dev server lifecycle (start, stop, status, logs). */
|
|
7
8
|
const app = new Hono();
|
|
8
9
|
// GET /api/dev-server/:workspaceId/status
|
|
@@ -51,7 +52,7 @@ app.post('/:workspaceId/start', migrationGuard, (c) => {
|
|
|
51
52
|
}
|
|
52
53
|
catch (err) {
|
|
53
54
|
const message = err instanceof Error ? err.message : String(err);
|
|
54
|
-
const status =
|
|
55
|
+
const status = err instanceof WorkspaceLifecycleBusyError || /already starting|is stopping/.test(message) ? 409 : 500;
|
|
55
56
|
return c.json({ error: message }, status);
|
|
56
57
|
}
|
|
57
58
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
1
2
|
import { Hono } from 'hono';
|
|
2
3
|
import { getForgeProvider } from '../services/forge/registry.js';
|
|
3
4
|
import { resolveForge } from '../services/forge/resolve.js';
|
|
@@ -61,7 +62,7 @@ app.post('/diagnose', async (c) => {
|
|
|
61
62
|
ongoingOperation: null,
|
|
62
63
|
branch: { state: 'absent' },
|
|
63
64
|
};
|
|
64
|
-
return c.json({ report, pr: null, fingerprint: prCheckout.computeFingerprint(report) });
|
|
65
|
+
return c.json({ report, pr: null, fingerprint: await prCheckout.computeFingerprint(report) });
|
|
65
66
|
}
|
|
66
67
|
// Page through the full list — a PR the user explicitly diagnosed by
|
|
67
68
|
// number must never come back "not found" just for sitting past the
|
|
@@ -75,11 +76,11 @@ app.post('/diagnose', async (c) => {
|
|
|
75
76
|
} while (!pr && cursor !== null);
|
|
76
77
|
if (!pr)
|
|
77
78
|
return c.json({ error: `Pull request #${prNumber} not found` }, 404);
|
|
78
|
-
const workspaces = listWorkspaces(true);
|
|
79
|
+
const workspaces = listWorkspaces(true).filter((workspace) => path.resolve(workspace.projectPath) === path.resolve(projectPath));
|
|
79
80
|
const report = prCheckout.diagnoseLocalState(projectPath, pr.headBranch, worktreesPath ?? null, attachedWorktreePaths(workspaces), workspaces);
|
|
80
81
|
if (pr.isFork)
|
|
81
82
|
report.blockers.push({ kind: 'fork-pr' });
|
|
82
|
-
return c.json({ report, pr, fingerprint: prCheckout.computeFingerprint(report) });
|
|
83
|
+
return c.json({ report, pr, fingerprint: await prCheckout.computeFingerprint(report) });
|
|
83
84
|
}
|
|
84
85
|
catch (err) {
|
|
85
86
|
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
@@ -93,7 +94,7 @@ app.post('/resolve', async (c) => {
|
|
|
93
94
|
if (!body.projectPath || !body.fingerprint) {
|
|
94
95
|
return c.json({ error: 'Missing required fields: projectPath, fingerprint' }, 400);
|
|
95
96
|
}
|
|
96
|
-
const workspaces = listWorkspaces(true);
|
|
97
|
+
const workspaces = listWorkspaces(true).filter((workspace) => path.resolve(workspace.projectPath) === path.resolve(body.projectPath));
|
|
97
98
|
const result = await prCheckout.resolvePrCheckout({
|
|
98
99
|
projectPath: body.projectPath,
|
|
99
100
|
headBranch: body.headBranch,
|