prism-mcp-server 20.0.7 → 20.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,220 +0,0 @@
1
- /**
2
- * v12.4: GitHub Actions CI/CD Pipeline Generator
3
- *
4
- * Generates GitHub Actions YAML workflows from Prism project configuration.
5
- * Supports test suite integration, automated deployments, and custom triggers.
6
- */
7
- import { debugLog } from "../utils/logger.js";
8
- // ─── Preset Templates ───────────────────────────────────────
9
- const PRESETS = {
10
- "node-test": {
11
- project: "",
12
- triggers: [
13
- { type: "push", branches: ["main"] },
14
- { type: "pull_request", branches: ["main"] },
15
- ],
16
- jobs: [
17
- {
18
- name: "test",
19
- runsOn: "ubuntu-latest",
20
- steps: [
21
- { name: "Checkout", uses: "actions/checkout@v4" },
22
- { name: "Setup Node", uses: "actions/setup-node@v4", with: { "node-version": "20" } },
23
- { name: "Install", run: "npm ci" },
24
- { name: "Lint", run: "npm run lint --if-present" },
25
- { name: "Type Check", run: "npx tsc --noEmit" },
26
- { name: "Test", run: "npm test --if-present" },
27
- ],
28
- },
29
- ],
30
- },
31
- "npm-publish": {
32
- project: "",
33
- triggers: [
34
- { type: "release", tags: ["v*"] },
35
- ],
36
- jobs: [
37
- {
38
- name: "publish",
39
- runsOn: "ubuntu-latest",
40
- steps: [
41
- { name: "Checkout", uses: "actions/checkout@v4" },
42
- { name: "Setup Node", uses: "actions/setup-node@v4", with: { "node-version": "20", "registry-url": "https://registry.npmjs.org" } },
43
- { name: "Install", run: "npm ci" },
44
- { name: "Build", run: "npm run build" },
45
- { name: "Publish", run: "npm publish", env: { NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}" } },
46
- ],
47
- },
48
- ],
49
- },
50
- "python-test": {
51
- project: "",
52
- triggers: [
53
- { type: "push", branches: ["main"] },
54
- { type: "pull_request", branches: ["main"] },
55
- ],
56
- jobs: [
57
- {
58
- name: "test",
59
- runsOn: "ubuntu-latest",
60
- steps: [
61
- { name: "Checkout", uses: "actions/checkout@v4" },
62
- { name: "Setup Python", uses: "actions/setup-python@v5", with: { "python-version": "3.12" } },
63
- { name: "Install", run: "pip install -e '.[dev]'" },
64
- { name: "Lint", run: "ruff check ." },
65
- { name: "Test", run: "pytest -v" },
66
- ],
67
- },
68
- ],
69
- },
70
- };
71
- // ─── YAML Generator ──────────────────────────────────────────
72
- function indent(level) {
73
- return " ".repeat(level);
74
- }
75
- function renderTriggers(triggers) {
76
- const lines = ["on:"];
77
- for (const trigger of triggers) {
78
- switch (trigger.type) {
79
- case "push":
80
- lines.push(`${indent(1)}push:`);
81
- if (trigger.branches?.length) {
82
- lines.push(`${indent(2)}branches: [${trigger.branches.map(b => `"${b}"`).join(", ")}]`);
83
- }
84
- if (trigger.paths?.length) {
85
- lines.push(`${indent(2)}paths:`);
86
- for (const p of trigger.paths)
87
- lines.push(`${indent(3)}- "${p}"`);
88
- }
89
- break;
90
- case "pull_request":
91
- lines.push(`${indent(1)}pull_request:`);
92
- if (trigger.branches?.length) {
93
- lines.push(`${indent(2)}branches: [${trigger.branches.map(b => `"${b}"`).join(", ")}]`);
94
- }
95
- break;
96
- case "schedule":
97
- lines.push(`${indent(1)}schedule:`);
98
- lines.push(`${indent(2)}- cron: "${trigger.cron || "0 0 * * *"}"`);
99
- break;
100
- case "workflow_dispatch":
101
- lines.push(`${indent(1)}workflow_dispatch:`);
102
- break;
103
- case "release":
104
- lines.push(`${indent(1)}release:`);
105
- lines.push(`${indent(2)}types: [published]`);
106
- break;
107
- }
108
- }
109
- return lines.join("\n");
110
- }
111
- function renderStep(step, level) {
112
- const lines = [];
113
- lines.push(`${indent(level)}- name: "${step.name}"`);
114
- if (step.condition) {
115
- lines.push(`${indent(level + 1)}if: ${step.condition}`);
116
- }
117
- if (step.uses) {
118
- lines.push(`${indent(level + 1)}uses: ${step.uses}`);
119
- }
120
- if (step.run) {
121
- if (step.run.includes("\n")) {
122
- lines.push(`${indent(level + 1)}run: |`);
123
- for (const line of step.run.split("\n")) {
124
- lines.push(`${indent(level + 2)}${line}`);
125
- }
126
- }
127
- else {
128
- lines.push(`${indent(level + 1)}run: ${step.run}`);
129
- }
130
- }
131
- if (step.with && Object.keys(step.with).length > 0) {
132
- lines.push(`${indent(level + 1)}with:`);
133
- for (const [k, v] of Object.entries(step.with)) {
134
- lines.push(`${indent(level + 2)}${k}: "${v}"`);
135
- }
136
- }
137
- if (step.env && Object.keys(step.env).length > 0) {
138
- lines.push(`${indent(level + 1)}env:`);
139
- for (const [k, v] of Object.entries(step.env)) {
140
- lines.push(`${indent(level + 2)}${k}: ${v}`);
141
- }
142
- }
143
- return lines.join("\n");
144
- }
145
- /**
146
- * Generate a GitHub Actions YAML workflow from a pipeline config.
147
- */
148
- export function generateWorkflow(config) {
149
- const lines = [];
150
- lines.push(`name: ${config.project || "CI"}`);
151
- lines.push("");
152
- lines.push(renderTriggers(config.triggers));
153
- lines.push("");
154
- if (config.env && Object.keys(config.env).length > 0) {
155
- lines.push("env:");
156
- for (const [k, v] of Object.entries(config.env)) {
157
- lines.push(`${indent(1)}${k}: "${v}"`);
158
- }
159
- lines.push("");
160
- }
161
- if (config.concurrency) {
162
- lines.push("concurrency:");
163
- lines.push(`${indent(1)}group: ${config.concurrency.group}`);
164
- lines.push(`${indent(1)}cancel-in-progress: ${config.concurrency.cancelInProgress}`);
165
- lines.push("");
166
- }
167
- lines.push("jobs:");
168
- for (const job of config.jobs) {
169
- lines.push(`${indent(1)}${job.name.replace(/\s+/g, "-").toLowerCase()}:`);
170
- lines.push(`${indent(2)}runs-on: ${job.runsOn}`);
171
- if (job.timeout) {
172
- lines.push(`${indent(2)}timeout-minutes: ${job.timeout}`);
173
- }
174
- if (job.needs?.length) {
175
- lines.push(`${indent(2)}needs: [${job.needs.join(", ")}]`);
176
- }
177
- if (job.condition) {
178
- lines.push(`${indent(2)}if: ${job.condition}`);
179
- }
180
- if (job.env && Object.keys(job.env).length > 0) {
181
- lines.push(`${indent(2)}env:`);
182
- for (const [k, v] of Object.entries(job.env)) {
183
- lines.push(`${indent(3)}${k}: "${v}"`);
184
- }
185
- }
186
- lines.push(`${indent(2)}steps:`);
187
- for (const step of job.steps) {
188
- lines.push(renderStep(step, 3));
189
- }
190
- lines.push("");
191
- }
192
- const yaml = lines.join("\n");
193
- return {
194
- filename: `.github/workflows/${config.project || "ci"}.yml`,
195
- yaml,
196
- description: `CI/CD workflow for ${config.project || "project"}`,
197
- };
198
- }
199
- /**
200
- * Generate a workflow from a preset template.
201
- */
202
- export function generateFromPreset(preset, project) {
203
- const config = PRESETS[preset];
204
- if (!config) {
205
- debugLog(`CI Pipeline: Unknown preset '${preset}'. Available: ${Object.keys(PRESETS).join(", ")}`);
206
- return null;
207
- }
208
- return generateWorkflow({ ...config, project });
209
- }
210
- /**
211
- * List available preset templates.
212
- */
213
- export function listPresets() {
214
- return [
215
- { name: "node-test", description: "Node.js CI: lint, type-check, test" },
216
- { name: "npm-publish", description: "npm publish on release tag" },
217
- { name: "python-test", description: "Python CI: ruff lint, pytest" },
218
- ];
219
- }
220
- debugLog("v12.4: CI pipeline generator loaded");
@@ -1,172 +0,0 @@
1
- /**
2
- * v12.3: Encrypted Peer-to-Peer Session Syncing
3
- *
4
- * AES-256-GCM encryption for session data transmission between
5
- * Prism instances. Supports both stdio pipe and WebSocket transports.
6
- *
7
- * Architecture:
8
- * 1. Generate per-sync ephemeral key (ECDH key exchange)
9
- * 2. Encrypt session payloads with AES-256-GCM
10
- * 3. Transfer via stdio pipe (local) or WebSocket (remote)
11
- * 4. Verify integrity with HMAC-SHA256
12
- */
13
- import { debugLog } from "../utils/logger.js";
14
- import crypto from "node:crypto";
15
- const { createHash, randomBytes, createCipheriv, createDecipheriv } = crypto;
16
- // ─── Encryption / Decryption ─────────────────────────────────
17
- const ALGORITHM = "aes-256-gcm";
18
- const IV_LENGTH = 12;
19
- const KEY_LENGTH = 32;
20
- /**
21
- * Derive a symmetric key from a shared secret using SHA-256.
22
- */
23
- export function deriveKey(sharedSecret) {
24
- return createHash("sha256").update(sharedSecret).digest();
25
- }
26
- /**
27
- * Generate a random encryption key.
28
- */
29
- export function generateKey() {
30
- return randomBytes(KEY_LENGTH);
31
- }
32
- /**
33
- * Encrypt a payload with AES-256-GCM.
34
- */
35
- export function encrypt(plaintext, key) {
36
- const iv = randomBytes(IV_LENGTH);
37
- const cipher = createCipheriv(ALGORITHM, key, iv);
38
- let encrypted = cipher.update(plaintext, "utf8", "hex");
39
- encrypted += cipher.final("hex");
40
- return {
41
- iv: iv.toString("hex"),
42
- data: encrypted,
43
- tag: cipher.getAuthTag().toString("hex"),
44
- algorithm: ALGORITHM,
45
- };
46
- }
47
- /**
48
- * Decrypt an AES-256-GCM encrypted packet.
49
- */
50
- export function decrypt(packet, key) {
51
- const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(packet.iv, "hex"));
52
- decipher.setAuthTag(Buffer.from(packet.tag, "hex"));
53
- let decrypted = decipher.update(packet.data, "hex", "utf8");
54
- decrypted += decipher.final("utf8");
55
- return decrypted;
56
- }
57
- // ─── Sync Payload Construction ───────────────────────────────
58
- /**
59
- * Create a checksum for a sync payload (integrity verification).
60
- */
61
- export function computeChecksum(entries) {
62
- const hash = createHash("sha256");
63
- for (const entry of entries) {
64
- hash.update(entry.id);
65
- hash.update(entry.data);
66
- }
67
- return hash.digest("hex");
68
- }
69
- /**
70
- * Build a sync payload from session entries.
71
- */
72
- export function buildSyncPayload(sourceId, targetId, entries) {
73
- return {
74
- version: 1,
75
- sourceId,
76
- targetId,
77
- timestamp: new Date().toISOString(),
78
- entries,
79
- checksum: computeChecksum(entries),
80
- };
81
- }
82
- /**
83
- * Verify a received sync payload's integrity.
84
- */
85
- export function verifySyncPayload(payload) {
86
- const computed = computeChecksum(payload.entries);
87
- if (computed.length !== payload.checksum.length)
88
- return false;
89
- return crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(payload.checksum));
90
- }
91
- // ─── Peer Management ─────────────────────────────────────────
92
- const peers = new Map();
93
- export function registerPeer(peer) {
94
- peers.set(peer.id, peer);
95
- debugLog(`Sync: Registered peer '${peer.name}' (${peer.transport})`);
96
- }
97
- export function removePeer(peerId) {
98
- return peers.delete(peerId);
99
- }
100
- export function listPeers() {
101
- return Array.from(peers.values());
102
- }
103
- export function getPeer(peerId) {
104
- return peers.get(peerId);
105
- }
106
- // ─── Sync Execution ──────────────────────────────────────────
107
- /**
108
- * Execute an encrypted sync with a peer.
109
- * In this implementation, we prepare the encrypted payload — the actual
110
- * transport (stdio/WebSocket) is pluggable.
111
- */
112
- export async function prepareSyncPacket(sourceId, targetPeerId, entries, sharedSecret) {
113
- const payload = buildSyncPayload(sourceId, targetPeerId, entries);
114
- const key = deriveKey(sharedSecret);
115
- const encrypted = encrypt(JSON.stringify(payload), key);
116
- debugLog(`Sync: Prepared encrypted packet with ${entries.length} entries for peer '${targetPeerId}'`);
117
- return {
118
- encrypted,
119
- metadata: {
120
- entryCount: entries.length,
121
- checksum: payload.checksum,
122
- },
123
- };
124
- }
125
- /**
126
- * Receive and decrypt a sync packet.
127
- */
128
- export function receiveSyncPacket(encrypted, sharedSecret) {
129
- const key = deriveKey(sharedSecret);
130
- const decrypted = decrypt(encrypted, key);
131
- // Wrap JSON.parse so a malformed payload from a misbehaving peer doesn't
132
- // crash the receiver with an unhandled SyntaxError. We re-throw with a
133
- // typed message so callers can distinguish corruption from auth failures.
134
- let payload;
135
- try {
136
- payload = JSON.parse(decrypted);
137
- }
138
- catch (e) {
139
- const msg = e instanceof Error ? e.message : String(e);
140
- throw new Error(`Sync payload corrupted: invalid JSON (${msg})`);
141
- }
142
- if (!verifySyncPayload(payload)) {
143
- throw new Error("Sync payload integrity check failed — checksum mismatch");
144
- }
145
- debugLog(`Sync: Received and verified packet with ${payload.entries.length} entries from '${payload.sourceId}'`);
146
- return payload;
147
- }
148
- /**
149
- * Resolve conflicts between local and remote entries (last-writer-wins).
150
- */
151
- export function resolveConflicts(local, remote) {
152
- const localMap = new Map(local.map(e => [e.id, e]));
153
- let conflictsResolved = 0;
154
- for (const remoteEntry of remote) {
155
- const localEntry = localMap.get(remoteEntry.id);
156
- if (localEntry) {
157
- // Last-writer-wins
158
- if (new Date(remoteEntry.createdAt) > new Date(localEntry.createdAt)) {
159
- localMap.set(remoteEntry.id, remoteEntry);
160
- conflictsResolved++;
161
- }
162
- }
163
- else {
164
- localMap.set(remoteEntry.id, remoteEntry);
165
- }
166
- }
167
- return {
168
- merged: Array.from(localMap.values()),
169
- conflictsResolved,
170
- };
171
- }
172
- debugLog("v12.3: Encrypted sync module loaded");
@@ -1,177 +0,0 @@
1
- /**
2
- * v12.5: Synalux Thin-Client Proxy
3
- *
4
- * HTTP relay to Synalux Cloud API for tier-gated features.
5
- * Routes requests through the Synalux Cloud Gateway, enforcing
6
- * subscription tier limits and authentication.
7
- */
8
- import { debugLog } from "../utils/logger.js";
9
- // ─── Tier Limits ─────────────────────────────────────────────
10
- const TIER_LIMITS = {
11
- free: {
12
- maxRequestsPerMinute: 10,
13
- maxMemoryMb: 50,
14
- maxProjects: 3,
15
- cloudFeatures: ["search", "load_context"],
16
- },
17
- standard: {
18
- maxRequestsPerMinute: 60,
19
- maxMemoryMb: 500,
20
- maxProjects: 20,
21
- cloudFeatures: ["search", "load_context", "save_ledger", "save_handoff", "analytics"],
22
- },
23
- advanced: {
24
- maxRequestsPerMinute: 300,
25
- maxMemoryMb: 5000,
26
- maxProjects: -1, // unlimited
27
- cloudFeatures: ["search", "load_context", "save_ledger", "save_handoff", "analytics", "backup", "sync", "plugins"],
28
- },
29
- enterprise: {
30
- maxRequestsPerMinute: -1, // unlimited
31
- maxMemoryMb: -1,
32
- maxProjects: -1,
33
- cloudFeatures: ["*"], // all features
34
- },
35
- };
36
- // ─── Rate Limiter ────────────────────────────────────────────
37
- const requestLog = [];
38
- function checkRateLimit(tier) {
39
- const limit = TIER_LIMITS[tier].maxRequestsPerMinute;
40
- if (limit === -1)
41
- return true;
42
- const now = Date.now();
43
- const windowStart = now - 60_000;
44
- // Clean old entries
45
- while (requestLog.length > 0 && requestLog[0] < windowStart) {
46
- requestLog.shift();
47
- }
48
- if (requestLog.length >= limit)
49
- return false;
50
- requestLog.push(now);
51
- return true;
52
- }
53
- // ─── Proxy State ─────────────────────────────────────────────
54
- let config = {
55
- baseUrl: "https://cloud.synalux.ai/api/v1/prism",
56
- apiKey: "",
57
- tier: "free",
58
- timeout: 30_000,
59
- retries: 2,
60
- enabled: false,
61
- };
62
- export function configureProxy(updates) {
63
- config = { ...config, ...updates };
64
- debugLog(`Proxy: Configured for ${config.tier} tier → ${config.baseUrl}`);
65
- }
66
- export function getProxyConfig() {
67
- return { ...config, apiKey: config.apiKey ? "***" : "" };
68
- }
69
- export function getTierLimits(tier) {
70
- return TIER_LIMITS[tier || config.tier];
71
- }
72
- // ─── Feature Gating ──────────────────────────────────────────
73
- /**
74
- * Check if a cloud feature is available for the current tier.
75
- */
76
- export function isFeatureAvailable(feature) {
77
- const limits = TIER_LIMITS[config.tier];
78
- return limits.cloudFeatures.includes("*") || limits.cloudFeatures.includes(feature);
79
- }
80
- /**
81
- * List all features available for the current tier.
82
- */
83
- export function listAvailableFeatures() {
84
- return [...TIER_LIMITS[config.tier].cloudFeatures];
85
- }
86
- // ─── HTTP Proxy ──────────────────────────────────────────────
87
- /**
88
- * Send a request through the Synalux Cloud proxy.
89
- */
90
- export async function proxyRequest(req) {
91
- if (!config.enabled) {
92
- return {
93
- status: 503,
94
- data: { error: "Synalux Cloud proxy is not enabled. Set PRISM_CLOUD_PROXY=true." },
95
- headers: {},
96
- latencyMs: 0,
97
- cached: false,
98
- };
99
- }
100
- if (!config.apiKey) {
101
- return {
102
- status: 401,
103
- data: { error: "No API key configured. Set PRISM_SYNALUX_API_KEY." },
104
- headers: {},
105
- latencyMs: 0,
106
- cached: false,
107
- };
108
- }
109
- if (!checkRateLimit(config.tier)) {
110
- return {
111
- status: 429,
112
- data: { error: `Rate limit exceeded for ${config.tier} tier (${TIER_LIMITS[config.tier].maxRequestsPerMinute}/min)` },
113
- headers: {},
114
- latencyMs: 0,
115
- cached: false,
116
- };
117
- }
118
- const start = Date.now();
119
- const url = `${config.baseUrl}${req.path}`;
120
- let lastError;
121
- for (let attempt = 0; attempt <= config.retries; attempt++) {
122
- try {
123
- const response = await fetch(url, {
124
- method: req.method,
125
- headers: {
126
- "Authorization": `Bearer ${config.apiKey}`,
127
- "Content-Type": "application/json",
128
- "X-Prism-Tier": config.tier,
129
- "X-Prism-Version": "12.5.0",
130
- ...(req.headers || {}),
131
- },
132
- body: req.body ? JSON.stringify(req.body) : undefined,
133
- signal: AbortSignal.timeout(config.timeout),
134
- });
135
- const data = await response.json().catch(() => null);
136
- const latencyMs = Date.now() - start;
137
- debugLog(`Proxy: ${req.method} ${req.path} → ${response.status} (${latencyMs}ms)`);
138
- const responseHeaders = {};
139
- response.headers.forEach((value, key) => { responseHeaders[key] = value; });
140
- return {
141
- status: response.status,
142
- data,
143
- headers: responseHeaders,
144
- latencyMs,
145
- cached: response.headers.get("x-cache") === "HIT",
146
- };
147
- }
148
- catch (err) {
149
- lastError = err;
150
- if (attempt < config.retries) {
151
- const delay = Math.min(1000 * Math.pow(2, attempt), 5000);
152
- await new Promise(r => setTimeout(r, delay));
153
- debugLog(`Proxy: Retry ${attempt + 1}/${config.retries} after error: ${err}`);
154
- }
155
- }
156
- }
157
- return {
158
- status: 502,
159
- data: { error: `Proxy error after ${config.retries + 1} attempts: ${lastError}` },
160
- headers: {},
161
- latencyMs: Date.now() - start,
162
- cached: false,
163
- };
164
- }
165
- /**
166
- * Check cloud connectivity and tier status.
167
- */
168
- export async function healthCheck() {
169
- const result = await proxyRequest({ method: "GET", path: "/health" });
170
- return {
171
- connected: result.status === 200,
172
- tier: config.tier,
173
- latencyMs: result.latencyMs,
174
- features: listAvailableFeatures(),
175
- };
176
- }
177
- debugLog("v12.5: Synalux proxy loaded");