mirror of
https://github.com/blamouche/obsidian-any-ai-code.git
synced 2026-07-22 06:53:38 +00:00
Refactor the sidebar panel from a single-process view into a tabbed multi-session view: each tab is an independent CliSession with its own PTY process and xterm terminal. A New session / + control opens a session from any configured runtime; multiple sessions of the same runtime are allowed and labels are disambiguated. Stop/Restart/Clear and the @file/ @folder buttons act on the active tab; a single ResizeObserver refits only the visible terminal and re-fits on tab activation. Automations now spawn their own session tab for the declared runtime (or the default when none is declared), await CLI readiness, then send the prompt — replacing the old skip-if-not-running/runtime-mismatch logic. Add settings autoCloseAutomationSessions and maxConcurrentSessions; the Automations modal "Run now" button is always enabled. Extract pure helpers to session-utils.ts (resolveRuntimeForAutomation, runtimeMatches, nextSessionLabel, canOpenSession) with unit tests. Update README and the generated example automation. Bump to 0.2.9 (manifest, versions.json, package.json, prompt-hub version). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
86 lines
2.7 KiB
TypeScript
86 lines
2.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
canOpenSession,
|
|
nextSessionLabel,
|
|
resolveRuntimeForAutomation,
|
|
runtimeMatches
|
|
} from "../session-utils";
|
|
import type { CliRuntimeConfig } from "../runtime-utils";
|
|
|
|
const runtimes: CliRuntimeConfig[] = [
|
|
{ id: "claude", name: "Claude", command: "claude" },
|
|
{ id: "codex", name: "Codex", command: "codex" },
|
|
{ id: "abc123", name: "My Gemini", command: "gemini" }
|
|
];
|
|
|
|
describe("runtimeMatches", () => {
|
|
it("matches by id", () => {
|
|
expect(runtimeMatches(runtimes[0], "claude")).toBe(true);
|
|
});
|
|
|
|
it("matches by name case-insensitively", () => {
|
|
expect(runtimeMatches(runtimes[2], "my gemini")).toBe(true);
|
|
});
|
|
|
|
it("returns false on no match or empty target", () => {
|
|
expect(runtimeMatches(runtimes[0], "codex")).toBe(false);
|
|
expect(runtimeMatches(runtimes[0], " ")).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("resolveRuntimeForAutomation", () => {
|
|
it("resolves a declared runtime by name", () => {
|
|
expect(resolveRuntimeForAutomation(runtimes, "Codex", "claude")?.id).toBe("codex");
|
|
});
|
|
|
|
it("resolves a declared runtime by id", () => {
|
|
expect(resolveRuntimeForAutomation(runtimes, "abc123", "claude")?.id).toBe("abc123");
|
|
});
|
|
|
|
it("returns null when a declared runtime is unknown", () => {
|
|
expect(resolveRuntimeForAutomation(runtimes, "Nope", "claude")).toBeNull();
|
|
});
|
|
|
|
it("falls back to the default runtime when none is declared", () => {
|
|
expect(resolveRuntimeForAutomation(runtimes, null, "codex")?.id).toBe("codex");
|
|
expect(resolveRuntimeForAutomation(runtimes, "", "abc123")?.id).toBe("abc123");
|
|
});
|
|
|
|
it("falls back to the first runtime when the default id is invalid", () => {
|
|
expect(resolveRuntimeForAutomation(runtimes, null, "missing")?.id).toBe("claude");
|
|
});
|
|
|
|
it("returns null when there are no runtimes", () => {
|
|
expect(resolveRuntimeForAutomation([], null, "claude")).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("nextSessionLabel", () => {
|
|
it("uses the runtime name when free", () => {
|
|
expect(nextSessionLabel([], "Claude")).toBe("Claude");
|
|
});
|
|
|
|
it("disambiguates duplicates", () => {
|
|
expect(nextSessionLabel(["Claude"], "Claude")).toBe("Claude (2)");
|
|
expect(nextSessionLabel(["Claude", "Claude (2)"], "Claude")).toBe("Claude (3)");
|
|
});
|
|
|
|
it("falls back to (Unnamed) for blank names", () => {
|
|
expect(nextSessionLabel([], " ")).toBe("(Unnamed)");
|
|
});
|
|
});
|
|
|
|
describe("canOpenSession", () => {
|
|
it("allows when under the cap", () => {
|
|
expect(canOpenSession(2, 8)).toBe(true);
|
|
});
|
|
|
|
it("blocks at the cap", () => {
|
|
expect(canOpenSession(8, 8)).toBe(false);
|
|
});
|
|
|
|
it("treats 0 or negative as unlimited", () => {
|
|
expect(canOpenSession(100, 0)).toBe(true);
|
|
expect(canOpenSession(100, -1)).toBe(true);
|
|
});
|
|
});
|