Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion extensions/agent-mesh/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@apilium/mayros-agent-mesh",
"version": "0.1.7",
"version": "0.1.8",
"private": true,
"description": "Mayros multi-agent coordination mesh with shared namespaces, delegation, and knowledge fusion",
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion extensions/analytics/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@apilium/mayros-analytics",
"version": "0.1.7",
"version": "0.1.8",
"private": true,
"type": "module",
"main": "index.ts",
Expand Down
2 changes: 1 addition & 1 deletion extensions/bash-sandbox/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@apilium/mayros-bash-sandbox",
"version": "0.1.7",
"version": "0.1.8",
"private": true,
"description": "Bash command sandbox with domain allowlist, command blocklist, and dangerous pattern detection",
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion extensions/bluebubbles/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@apilium/mayros-bluebubbles",
"version": "0.1.7",
"version": "0.1.8",
"description": "Mayros BlueBubbles channel plugin",
"license": "MIT",
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion extensions/ci-plugin/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@apilium/mayros-ci-plugin",
"version": "0.1.7",
"version": "0.1.8",
"description": "CI/CD pipeline integration for Mayros — GitHub Actions and GitLab CI providers",
"type": "module",
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion extensions/code-indexer/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@apilium/mayros-code-indexer",
"version": "0.1.7",
"version": "0.1.8",
"private": true,
"description": "Mayros code indexer plugin — regex-based codebase scanning with RDF triple storage in Cortex",
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion extensions/code-tools/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@apilium/mayros-code-tools",
"version": "0.1.7",
"version": "0.1.8",
"private": true,
"type": "module",
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion extensions/copilot-proxy/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@apilium/mayros-copilot-proxy",
"version": "0.1.7",
"version": "0.1.8",
"private": true,
"description": "Mayros Copilot Proxy provider plugin",
"type": "module",
Expand Down
105 changes: 105 additions & 0 deletions extensions/cortex-sync/config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* Tests for cortex-sync configuration parsing, including P2P bridge (B3).
*/

import { describe, expect, it } from "vitest";
import { parseCortexSyncConfig, type CortexSyncConfig } from "./config.js";

describe("parseCortexSyncConfig", () => {
it("returns defaults for empty input", () => {
const cfg = parseCortexSyncConfig({});
expect(cfg.namespace).toBe("mayros");
expect(cfg.sync.intervalSeconds).toBe(300);
expect(cfg.sync.autoSync).toBe(false);
expect(cfg.sync.conflictStrategy).toBe("last-writer-wins");
expect(cfg.sync.maxTriplesPerSync).toBe(5000);
expect(cfg.sync.syncTimeoutMs).toBe(30000);
expect(cfg.sync.nativeP2pPreferred).toBe(true);
expect(cfg.discovery.bonjourEnabled).toBe(false);
expect(cfg.discovery.manualPeers).toEqual([]);
});

it("nativeP2pPreferred defaults to true", () => {
const cfg = parseCortexSyncConfig({});
expect(cfg.sync.nativeP2pPreferred).toBe(true);
});

it("nativeP2pPreferred can be set to false", () => {
const cfg = parseCortexSyncConfig({
sync: { nativeP2pPreferred: false },
});
expect(cfg.sync.nativeP2pPreferred).toBe(false);
});

it("nativeP2pPreferred true is respected", () => {
const cfg = parseCortexSyncConfig({
sync: { nativeP2pPreferred: true },
});
expect(cfg.sync.nativeP2pPreferred).toBe(true);
});

it("clamps intervalSeconds to range", () => {
const cfg1 = parseCortexSyncConfig({ sync: { intervalSeconds: 1 } });
expect(cfg1.sync.intervalSeconds).toBe(10);

const cfg2 = parseCortexSyncConfig({ sync: { intervalSeconds: 100000 } });
expect(cfg2.sync.intervalSeconds).toBe(86400);
});

it("clamps maxTriplesPerSync to range", () => {
const cfg1 = parseCortexSyncConfig({ sync: { maxTriplesPerSync: 1 } });
expect(cfg1.sync.maxTriplesPerSync).toBe(100);

const cfg2 = parseCortexSyncConfig({ sync: { maxTriplesPerSync: 100000 } });
expect(cfg2.sync.maxTriplesPerSync).toBe(50000);
});

it("parses valid conflictStrategy", () => {
const cfg = parseCortexSyncConfig({
sync: { conflictStrategy: "keep-both" },
});
expect(cfg.sync.conflictStrategy).toBe("keep-both");
});

it("falls back to default for invalid conflictStrategy", () => {
const cfg = parseCortexSyncConfig({
sync: { conflictStrategy: "invalid" },
});
expect(cfg.sync.conflictStrategy).toBe("last-writer-wins");
});

it("parses manual peers", () => {
const cfg = parseCortexSyncConfig({
discovery: {
manualPeers: [
{
nodeId: "node1",
endpoint: "http://localhost:8080",
namespaces: ["ns1"],
enabled: true,
},
],
},
});
expect(cfg.discovery.manualPeers).toHaveLength(1);
expect(cfg.discovery.manualPeers[0].nodeId).toBe("node1");
});

it("filters invalid manual peers (missing nodeId or endpoint)", () => {
const cfg = parseCortexSyncConfig({
discovery: {
manualPeers: [
{ nodeId: "", endpoint: "http://localhost:8080" },
{ nodeId: "valid", endpoint: "" },
{ nodeId: "ok", endpoint: "http://valid" },
],
},
});
expect(cfg.discovery.manualPeers).toHaveLength(1);
expect(cfg.discovery.manualPeers[0].nodeId).toBe("ok");
});

it("rejects unknown sync keys", () => {
expect(() => parseCortexSyncConfig({ sync: { bogus: true } })).toThrow("unknown keys");
});
});
27 changes: 25 additions & 2 deletions extensions/cortex-sync/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export type SyncConfig = {
conflictStrategy: ConflictStrategy;
maxTriplesPerSync: number;
syncTimeoutMs: number;
nativeP2pPreferred: boolean;
};

export type DiscoveryConfig = {
Expand Down Expand Up @@ -80,7 +81,14 @@ function parseSyncConfig(raw: unknown): SyncConfig {
if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
assertAllowedKeys(
sync,
["intervalSeconds", "autoSync", "conflictStrategy", "maxTriplesPerSync", "syncTimeoutMs"],
[
"intervalSeconds",
"autoSync",
"conflictStrategy",
"maxTriplesPerSync",
"syncTimeoutMs",
"nativeP2pPreferred",
],
"sync config",
);
}
Expand Down Expand Up @@ -110,7 +118,17 @@ function parseSyncConfig(raw: unknown): SyncConfig {
? Math.max(5000, Math.min(120000, Math.floor(sync.syncTimeoutMs)))
: DEFAULT_SYNC_TIMEOUT_MS;

return { intervalSeconds, autoSync, conflictStrategy, maxTriplesPerSync, syncTimeoutMs };
const nativeP2pPreferred =
typeof sync.nativeP2pPreferred === "boolean" ? sync.nativeP2pPreferred : true;

return {
intervalSeconds,
autoSync,
conflictStrategy,
maxTriplesPerSync,
syncTimeoutMs,
nativeP2pPreferred,
};
}

function parsePeerConfig(raw: unknown): SyncPeerConfig {
Expand Down Expand Up @@ -195,5 +213,10 @@ export const cortexSyncConfigUiHints = {
default: DEFAULT_BONJOUR_ENABLED,
description: "Enable local network peer discovery",
},
"sync.nativeP2pPreferred": {
type: "boolean",
default: true,
description: "Prefer native P2P gossip over REST polling when available",
},
"discovery.manualPeers": { type: "array", description: "Manually configured peers" },
} as const;
Loading
Loading