118 lines
4.7 KiB
TypeScript
118 lines
4.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
const {
|
|
DEFAULT_AI_PROVIDER,
|
|
AI_RUNTIME_EXIT_OSC_IDENT,
|
|
CODEX_BOOTSTRAP_TOKEN_CODEX_MISSING,
|
|
CODEX_BOOTSTRAP_TOKEN_DIR_MISSING,
|
|
CODEX_BOOTSTRAP_TOKEN_READY,
|
|
DEFAULT_CODEX_SANDBOX_MODE,
|
|
DEFAULT_COPILOT_PERMISSION_MODE,
|
|
buildAiRuntimeExitMarker,
|
|
buildCopilotLaunchCommand,
|
|
buildCopilotCommand,
|
|
buildCdCommand,
|
|
buildCodexBootstrapCommand,
|
|
buildCodexResumeCommand,
|
|
consumeAiRuntimeExitMarkers,
|
|
isAiSessionReady,
|
|
normalizeAiProvider,
|
|
normalizeCodexSandboxMode,
|
|
normalizeCopilotPermissionMode
|
|
} = require("./aiLaunch.js");
|
|
|
|
describe("miniprogram aiLaunch", () => {
|
|
it("保留 HOME 展开语义", () => {
|
|
expect(buildCdCommand("~")).toBe('cd "$HOME"');
|
|
expect(buildCdCommand("~/workspace/remoteconn")).toBe("cd \"$HOME\"/'workspace/remoteconn'");
|
|
});
|
|
|
|
it("对普通路径做单引号转义", () => {
|
|
expect(buildCdCommand("/var/www/my app")).toBe("cd '/var/www/my app'");
|
|
expect(buildCdCommand("/tmp/it's here")).toBe("cd '/tmp/it'\\''s here'");
|
|
});
|
|
|
|
it("生成与 Web 对齐的 Codex bootstrap 命令", () => {
|
|
const plan = buildCodexBootstrapCommand("~/workspace/demo", "danger-full-access");
|
|
|
|
expect(plan.projectPath).toBe("~/workspace/demo");
|
|
expect(plan.command.startsWith('sh -lc "')).toBe(true);
|
|
expect(plan.command).toContain("codex --sandbox danger-full-access");
|
|
expect(plan.command).toContain(CODEX_BOOTSTRAP_TOKEN_DIR_MISSING);
|
|
expect(plan.command).toContain(CODEX_BOOTSTRAP_TOKEN_CODEX_MISSING);
|
|
expect(plan.command).toContain(CODEX_BOOTSTRAP_TOKEN_READY);
|
|
expect(plan.command).toContain(
|
|
`printf '\\\\033]${AI_RUNTIME_EXIT_OSC_IDENT};RemoteConn;ai-exit=codex\\\\a'`
|
|
);
|
|
});
|
|
|
|
it("生成 Codex 恢复命令时应使用 resume --last 并保留退出标记", () => {
|
|
const plan = buildCodexResumeCommand("~/workspace/demo", "danger-full-access");
|
|
|
|
expect(plan.projectPath).toBe("~/workspace/demo");
|
|
expect(plan.command.startsWith('sh -lc "')).toBe(true);
|
|
expect(plan.command).toContain("workspace/demo");
|
|
expect(plan.command).toContain("codex resume --last --sandbox danger-full-access;");
|
|
expect(plan.command).toContain(
|
|
`printf '\\\\033]${AI_RUNTIME_EXIT_OSC_IDENT};RemoteConn;ai-exit=codex\\\\a'`
|
|
);
|
|
});
|
|
|
|
it("AI 模式脏值会回退到稳定默认值", () => {
|
|
expect(normalizeAiProvider("")).toBe(DEFAULT_AI_PROVIDER);
|
|
expect(normalizeAiProvider("invalid")).toBe(DEFAULT_AI_PROVIDER);
|
|
expect(normalizeCodexSandboxMode("")).toBe(DEFAULT_CODEX_SANDBOX_MODE);
|
|
expect(normalizeCodexSandboxMode("invalid")).toBe(DEFAULT_CODEX_SANDBOX_MODE);
|
|
expect(normalizeCopilotPermissionMode("")).toBe(DEFAULT_COPILOT_PERMISSION_MODE);
|
|
expect(normalizeCopilotPermissionMode("invalid")).toBe(DEFAULT_COPILOT_PERMISSION_MODE);
|
|
});
|
|
|
|
it("会按 Copilot 权限模式构造启动命令", () => {
|
|
expect(buildCopilotCommand("default")).toBe("copilot");
|
|
expect(buildCopilotCommand("experimental")).toBe("copilot --experimental");
|
|
expect(buildCopilotCommand("allow-all")).toBe("copilot --allow-all");
|
|
expect(buildCopilotCommand("invalid")).toBe("copilot");
|
|
});
|
|
|
|
it("Copilot 启动命令也会携带退出标记,供前端解除前台 AI 保护", () => {
|
|
const plan = buildCopilotLaunchCommand("~/workspace/demo", "allow-all");
|
|
|
|
expect(plan.projectPath).toBe("~/workspace/demo");
|
|
expect(plan.command.startsWith('sh -lc "')).toBe(true);
|
|
expect(plan.command).toContain("copilot --allow-all");
|
|
expect(plan.command).toContain(
|
|
`printf '\\\\033]${AI_RUNTIME_EXIT_OSC_IDENT};RemoteConn;ai-exit=copilot\\\\a'`
|
|
);
|
|
});
|
|
|
|
it("能消费完整或跨 chunk 的 AI 退出标记,而不污染可见文本", () => {
|
|
const codexExitMarker = buildAiRuntimeExitMarker("codex");
|
|
|
|
expect(consumeAiRuntimeExitMarkers(`prefix${codexExitMarker}suffix`, "")).toEqual({
|
|
text: "prefixsuffix",
|
|
carry: "",
|
|
exitedProviders: ["codex"]
|
|
});
|
|
|
|
const first = consumeAiRuntimeExitMarkers(`before${codexExitMarker.slice(0, 8)}`, "");
|
|
expect(first).toEqual({
|
|
text: "before",
|
|
carry: codexExitMarker.slice(0, 8),
|
|
exitedProviders: []
|
|
});
|
|
|
|
expect(consumeAiRuntimeExitMarkers(`${codexExitMarker.slice(8)}after`, first.carry)).toEqual({
|
|
text: "after",
|
|
carry: "",
|
|
exitedProviders: ["codex"]
|
|
});
|
|
});
|
|
|
|
it("只有收到显式 shell-ready 信号后,才认为 AI 会话可发命令", () => {
|
|
expect(isAiSessionReady("connected", true, false)).toBe(false);
|
|
expect(isAiSessionReady("connected", true, true)).toBe(true);
|
|
expect(isAiSessionReady("connecting", true, true)).toBe(false);
|
|
expect(isAiSessionReady("connected", false, true)).toBe(false);
|
|
});
|
|
});
|