mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-20 22:02:38 +00:00
new gateway
This commit is contained in:
parent
01958298e0
commit
9a0b848789
34 changed files with 1632 additions and 290 deletions
|
|
@ -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