fix: resolve remaining PR315 Bugbot findings

This commit is contained in:
Harsh 2026-03-06 12:05:33 +05:30
parent 11ea174a81
commit c8f55e712c
7 changed files with 85 additions and 21 deletions

View File

@ -0,0 +1,16 @@
import { describe, expect, it } from "vitest";
import { asValidOpenCodeSessionId } from "../opencode-session-id.js";
describe("asValidOpenCodeSessionId", () => {
it("accepts valid OpenCode session ids", () => {
expect(asValidOpenCodeSessionId("ses_abc123")).toBe("ses_abc123");
expect(asValidOpenCodeSessionId(" ses_ABC-123_xyz ")).toBe("ses_ABC-123_xyz");
});
it("rejects invalid OpenCode session ids", () => {
expect(asValidOpenCodeSessionId("")).toBeUndefined();
expect(asValidOpenCodeSessionId("ses bad")).toBeUndefined();
expect(asValidOpenCodeSessionId("abc123")).toBeUndefined();
expect(asValidOpenCodeSessionId(123)).toBeUndefined();
});
});

View File

@ -1969,6 +1969,34 @@ describe("remap", () => {
const meta = readMetadataRaw(sessionsDir, "app-1");
expect(meta?.["opencodeSessionId"]).toBe("ses_discovered");
});
it("prefers most recently updated title match when remapping", async () => {
const deleteLogPath = join(tmpDir, "opencode-delete-remap-ordering.log");
const mockBin = installMockOpencode(
JSON.stringify([
{ id: "ses_old", title: "AO:app-1", updated: "2025-01-01T00:00:00.000Z" },
{ id: "ses_new", title: "AO:app-1", updated: 1_772_777_328_000 },
]),
deleteLogPath,
);
process.env.PATH = `${mockBin}:${originalPath ?? ""}`;
writeMetadata(sessionsDir, "app-1", {
worktree: "/tmp",
branch: "main",
status: "working",
project: "my-app",
agent: "opencode",
runtimeHandle: JSON.stringify(makeHandle("rt-1")),
});
const sm = createSessionManager({ config, registry: mockRegistry });
const mapped = await sm.remap("app-1");
expect(mapped).toBe("ses_new");
const meta = readMetadataRaw(sessionsDir, "app-1");
expect(meta?.["opencodeSessionId"]).toBe("ses_new");
});
});
describe("spawnOrchestrator", () => {

View File

@ -61,6 +61,7 @@ export type { OrchestratorPromptConfig } from "./orchestrator-prompt.js";
// Shared utilities
export { shellEscape, escapeAppleScript, validateUrl, readLastJsonlEntry } from "./utils.js";
export { asValidOpenCodeSessionId } from "./opencode-session-id.js";
// Path utilities — hash-based directory structure
export {

View File

@ -0,0 +1,8 @@
const OPENCODE_SESSION_ID_RE = /^ses_[A-Za-z0-9_-]+$/;
export function asValidOpenCodeSessionId(value: unknown): string | undefined {
if (typeof value !== "string") return undefined;
const trimmed = value.trim();
if (trimmed.length === 0) return undefined;
return OPENCODE_SESSION_ID_RE.test(trimmed) ? trimmed : undefined;
}

View File

@ -60,17 +60,19 @@ import {
generateConfigHash,
validateAndStoreOrigin,
} from "./paths.js";
import { asValidOpenCodeSessionId } from "./opencode-session-id.js";
const execFileAsync = promisify(execFile);
const OPENCODE_DISCOVERY_TIMEOUT_MS = 2_000;
const OPENCODE_INTERACTIVE_DISCOVERY_TIMEOUT_MS = 10_000;
const OPENCODE_SESSION_ID_RE = /^ses_[A-Za-z0-9_-]+$/;
function asValidOpenCodeSessionId(value: unknown): string | undefined {
if (typeof value !== "string") return undefined;
const trimmed = value.trim();
if (trimmed.length === 0) return undefined;
return OPENCODE_SESSION_ID_RE.test(trimmed) ? trimmed : undefined;
function scoreOpenCodeUpdated(value: unknown): number {
if (typeof value === "number" && Number.isFinite(value)) return value;
if (typeof value === "string") {
const parsed = Date.parse(value);
if (!Number.isNaN(parsed)) return parsed;
}
return -1;
}
function errorIncludesSessionNotFound(err: unknown): boolean {
@ -115,11 +117,13 @@ async function discoverOpenCodeSessionIdsByTitle(
const parsed = safeJsonParse<Array<Record<string, unknown>>>(stdout);
if (!parsed) return [];
const title = `AO:${sessionId}`;
const candidates = parsed.filter((entry) => {
const candidateTitle = typeof entry["title"] === "string" ? entry["title"] : "";
const candidateId = typeof entry["id"] === "string" ? entry["id"] : "";
return candidateTitle === title && candidateId.length > 0;
});
const candidates = parsed
.filter((entry) => {
const candidateTitle = typeof entry["title"] === "string" ? entry["title"] : "";
const candidateId = typeof entry["id"] === "string" ? entry["id"] : "";
return candidateTitle === title && candidateId.length > 0;
})
.sort((a, b) => scoreOpenCodeUpdated(b["updated"]) - scoreOpenCodeUpdated(a["updated"]));
return candidates
.map((entry) => asValidOpenCodeSessionId(entry["id"]))

View File

@ -1,5 +1,6 @@
import {
shellEscape,
asValidOpenCodeSessionId,
type Agent,
type AgentSessionInfo,
type AgentLaunchConfig,
@ -20,15 +21,6 @@ interface OpenCodeSessionListEntry {
updated?: string;
}
const OPENCODE_SESSION_ID_RE = /^ses_[A-Za-z0-9_-]+$/;
function asValidOpenCodeSessionId(value: unknown): string | undefined {
if (typeof value !== "string") return undefined;
const trimmed = value.trim();
if (trimmed.length === 0) return undefined;
return OPENCODE_SESSION_ID_RE.test(trimmed) ? trimmed : undefined;
}
function parseSessionList(raw: string): OpenCodeSessionListEntry[] {
let parsed: unknown;
try {

View File

@ -56,6 +56,7 @@ export function DirectTerminal({
const [status, setStatus] = useState<"connecting" | "connected" | "error">("connecting");
const [error, setError] = useState<string | null>(null);
const [reloading, setReloading] = useState(false);
const [reloadError, setReloadError] = useState<string | null>(null);
const [resolvedReloadCommand, setResolvedReloadCommand] = useState<string | null>(null);
// Update URL when fullscreen changes
@ -74,6 +75,7 @@ export function DirectTerminal({
async function handleReload(): Promise<void> {
if (!isOpenCodeSession || reloading) return;
setReloadError(null);
setReloading(true);
try {
let commandToSend = resolvedReloadCommand ?? reloadCommand;
@ -96,11 +98,16 @@ export function DirectTerminal({
setResolvedReloadCommand(commandToSend);
}
await fetch(`/api/sessions/${encodeURIComponent(sessionId)}/send`, {
const sendRes = await fetch(`/api/sessions/${encodeURIComponent(sessionId)}/send`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: commandToSend }),
});
if (!sendRes.ok) {
throw new Error(`Failed to send reload command: ${sendRes.status}`);
}
} catch (err) {
setReloadError(err instanceof Error ? err.message : "Failed to reload OpenCode session");
} finally {
setReloading(false);
}
@ -590,6 +597,14 @@ export function DirectTerminal({
)}
</button>
) : null}
{reloadError ? (
<span
className="max-w-[40ch] truncate text-[10px] font-medium text-[var(--color-status-error)]"
title={reloadError}
>
{reloadError}
</span>
) : null}
<button
onClick={() => setFullscreen(!fullscreen)}
className={cn(