mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-17 05:00:16 +00:00
new gateway
This commit is contained in:
parent
01958298e0
commit
9a0b848789
34 changed files with 1632 additions and 290 deletions
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { execCommand, type SpawnOptions, type TerminalAdapter } from "../utils/terminal-adapter";
|
||||
import type { SpawnOptions, TerminalAdapter } from "../utils/terminal-adapter";
|
||||
|
||||
/**
|
||||
* Context needed for iTerm2 spawning (tracks last pane for layout)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ describe("WezTermAdapter", () => {
|
|||
describe("spawn", () => {
|
||||
it("should spawn first pane to the right with 50%", () => {
|
||||
// Mock getPanes finding only current pane
|
||||
mockExecCommand.mockImplementation((bin, args) => {
|
||||
mockExecCommand.mockImplementation((_bin: string, args: string[]) => {
|
||||
if (args.includes("list")) {
|
||||
return {
|
||||
stdout: JSON.stringify([{ pane_id: 0, tab_id: 0 }]),
|
||||
|
|
@ -69,7 +69,7 @@ describe("WezTermAdapter", () => {
|
|||
|
||||
it("should spawn subsequent panes by splitting the sidebar", () => {
|
||||
// Mock getPanes finding current pane (0) and sidebar pane (1)
|
||||
mockExecCommand.mockImplementation((bin, args) => {
|
||||
mockExecCommand.mockImplementation((_bin: string, args: string[]) => {
|
||||
if (args.includes("list")) {
|
||||
return {
|
||||
stdout: JSON.stringify([
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { withLock } from "./lock";
|
||||
|
||||
describe("withLock race conditions", () => {
|
||||
const testDir = path.join(os.tmpdir(), "pi-lock-race-test-" + Date.now());
|
||||
const testDir = path.join(os.tmpdir(), `pi-lock-race-test-${Date.now()}`);
|
||||
const lockPath = path.join(testDir, "test");
|
||||
const lockFile = `${lockPath}.lock`;
|
||||
|
||||
beforeEach(() => {
|
||||
if (!fs.existsSync(testDir)) fs.mkdirSync(testDir, { recursive: true });
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|||
import { withLock } from "./lock";
|
||||
|
||||
describe("withLock", () => {
|
||||
const testDir = path.join(os.tmpdir(), "pi-lock-test-" + Date.now());
|
||||
const testDir = path.join(os.tmpdir(), `pi-lock-test-${Date.now()}`);
|
||||
const lockPath = path.join(testDir, "test");
|
||||
const lockFile = `${lockPath}.lock`;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
// Project: pi-teams
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const LOCK_TIMEOUT = 5000; // 5 seconds of retrying
|
||||
const STALE_LOCK_TIMEOUT = 30000; // 30 seconds for a lock to be considered stale
|
||||
|
||||
export async function withLock<T>(lockPath: string, fn: () => Promise<T>, retries: number = 50): Promise<T> {
|
||||
|
|
@ -18,7 +16,7 @@ export async function withLock<T>(lockPath: string, fn: () => Promise<T>, retrie
|
|||
// Attempt to remove stale lock
|
||||
try {
|
||||
fs.unlinkSync(lockFile);
|
||||
} catch (e) {
|
||||
} catch (_error) {
|
||||
// ignore, another process might have already removed it
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +24,7 @@ export async function withLock<T>(lockPath: string, fn: () => Promise<T>, retrie
|
|||
|
||||
fs.writeFileSync(lockFile, process.pid.toString(), { flag: "wx" });
|
||||
break;
|
||||
} catch (e) {
|
||||
} catch (_error) {
|
||||
retries--;
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
}
|
||||
|
|
@ -41,7 +39,7 @@ export async function withLock<T>(lockPath: string, fn: () => Promise<T>, retrie
|
|||
} finally {
|
||||
try {
|
||||
fs.unlinkSync(lockFile);
|
||||
} catch (e) {
|
||||
} catch (_error) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { appendMessage, broadcastMessage, readInbox, sendPlainMessage } from "./
|
|||
import * as paths from "./paths";
|
||||
|
||||
// Mock the paths to use a temporary directory
|
||||
const testDir = path.join(os.tmpdir(), "pi-teams-test-" + Date.now());
|
||||
const testDir = path.join(os.tmpdir(), `pi-teams-test-${Date.now()}`);
|
||||
|
||||
describe("Messaging Utilities", () => {
|
||||
beforeEach(() => {
|
||||
|
|
@ -14,11 +14,11 @@ describe("Messaging Utilities", () => {
|
|||
fs.mkdirSync(testDir, { recursive: true });
|
||||
|
||||
// Override paths to use testDir
|
||||
vi.spyOn(paths, "inboxPath").mockImplementation((teamName, agentName) => {
|
||||
vi.spyOn(paths, "inboxPath").mockImplementation((_teamName, agentName) => {
|
||||
return path.join(testDir, "inboxes", `${agentName}.json`);
|
||||
});
|
||||
vi.spyOn(paths, "teamDir").mockReturnValue(testDir);
|
||||
vi.spyOn(paths, "configPath").mockImplementation((teamName) => {
|
||||
vi.spyOn(paths, "configPath").mockImplementation((_teamName) => {
|
||||
return path.join(testDir, "config.json");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -103,6 +103,8 @@ export async function broadcastMessage(
|
|||
if (failures.length > 0) {
|
||||
console.error(`Broadcast partially failed: ${failures.length} messages could not be delivered.`);
|
||||
// Optionally log individual errors
|
||||
failures.forEach((f) => console.error(`- Delivery error:`, f.reason));
|
||||
for (const failure of failures) {
|
||||
console.error("- Delivery error:", failure.reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { inboxPath, sanitizeName, teamDir } from "./paths";
|
||||
|
||||
|
|
@ -17,7 +14,6 @@ describe("Security Audit - Path Traversal (Prevention Check)", () => {
|
|||
});
|
||||
|
||||
it("should throw an error for path traversal via taskId", () => {
|
||||
const teamName = "audit-team";
|
||||
const maliciousTaskId = "../../../etc/passwd";
|
||||
// We need to import readTask/updateTask or just sanitizeName directly if we want to test the logic
|
||||
// But since we already tested sanitizeName via other paths, this is just for completeness.
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import os from "node:os";
|
|||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import * as paths from "./paths";
|
||||
import { createTask, listTasks } from "./tasks";
|
||||
import { createTask } from "./tasks";
|
||||
|
||||
const testDir = path.join(os.tmpdir(), "pi-tasks-race-test-" + Date.now());
|
||||
const testDir = path.join(os.tmpdir(), `pi-tasks-race-test-${Date.now()}`);
|
||||
|
||||
describe("Tasks Race Condition Bug", () => {
|
||||
beforeEach(() => {
|
||||
|
|
|
|||
|
|
@ -6,10 +6,9 @@ import path from "node:path";
|
|||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import * as paths from "./paths";
|
||||
import { createTask, evaluatePlan, listTasks, readTask, submitPlan, updateTask } from "./tasks";
|
||||
import * as teams from "./teams";
|
||||
|
||||
// Mock the paths to use a temporary directory
|
||||
const testDir = path.join(os.tmpdir(), "pi-teams-test-" + Date.now());
|
||||
const testDir = path.join(os.tmpdir(), `pi-teams-test-${Date.now()}`);
|
||||
|
||||
describe("Tasks Utilities", () => {
|
||||
beforeEach(() => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { teamExists } from "./teams";
|
|||
export function getTaskId(teamName: string): string {
|
||||
const dir = taskDir(teamName);
|
||||
const files = fs.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
||||
const ids = files.map((f) => parseInt(path.parse(f).name, 10)).filter((id) => !isNaN(id));
|
||||
const ids = files.map((f) => parseInt(path.parse(f).name, 10)).filter((id) => !Number.isNaN(id));
|
||||
return ids.length > 0 ? (Math.max(...ids) + 1).toString() : "1";
|
||||
}
|
||||
|
||||
|
|
@ -169,7 +169,7 @@ export async function listTasks(teamName: string): Promise<TaskFile[]> {
|
|||
const tasks: TaskFile[] = files
|
||||
.map((f) => {
|
||||
const id = parseInt(path.parse(f).name, 10);
|
||||
if (isNaN(id)) return null;
|
||||
if (Number.isNaN(id)) return null;
|
||||
return JSON.parse(fs.readFileSync(path.join(dir, f), "utf-8"));
|
||||
})
|
||||
.filter((t) => t !== null);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { withLock } from "./lock";
|
||||
import type { Member, TeamConfig } from "./models";
|
||||
import { configPath, taskDir, teamDir } from "./paths";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue