easyeda-mcp-pro 0.17.0 → 0.18.0

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.
package/README.md CHANGED
@@ -1,19 +1,51 @@
1
- # easyeda-mcp-pro
1
+ <div align="center">
2
2
 
3
- **Production-grade MCP server for EasyEDA Pro: safe PCB design inspection, BOM sourcing, manufacturing export, and AI-assisted hardware review.**
3
+ # easyeda-mcp-pro
4
4
 
5
- [![CI](https://github.com/oaslananka/easyeda-mcp-pro/actions/workflows/ci.yml/badge.svg)](https://github.com/oaslananka/easyeda-mcp-pro/actions/workflows/ci.yml)
6
- [![npm version](https://img.shields.io/npm/v/easyeda-mcp-pro.svg)](https://www.npmjs.com/package/easyeda-mcp-pro)
7
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
- [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/13406/badge)](https://www.bestpractices.dev/projects/13406)
9
- [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/oaslananka/easyeda-mcp-pro)
5
+ <p>
6
+ Production-grade MCP server for EasyEDA Pro: safe PCB design inspection, BOM sourcing, manufacturing export, and AI-assisted hardware review.
7
+ </p>
10
8
 
11
- <p align="center">
12
- <a href="https://www.buymeacoffee.com/oaslananka">
13
- <img src="https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20coffee&emoji=%E2%98%95&slug=oaslananka&button_colour=FFDD00&font_colour=000000&font_family=Arial&outline_colour=000000&coffee_colour=ffffff" alt="Buy me a coffee" />
9
+ <p>
10
+ <a href="https://github.com/oaslananka/easyeda-mcp-pro/actions/workflows/ci.yml">
11
+ <img src="https://github.com/oaslananka/easyeda-mcp-pro/actions/workflows/ci.yml/badge.svg" alt="CI status" />
12
+ </a>
13
+ <a href="https://www.npmjs.com/package/easyeda-mcp-pro">
14
+ <img src="https://img.shields.io/npm/v/easyeda-mcp-pro.svg?logo=npm" alt="npm version" />
15
+ </a>
16
+ <a href="https://www.npmjs.com/package/easyeda-mcp-pro">
17
+ <img src="https://img.shields.io/npm/dt/easyeda-mcp-pro?logo=npm&label=total%20downloads" alt="npm total downloads" />
18
+ </a>
19
+ <a href="https://www.npmjs.com/package/easyeda-mcp-pro">
20
+ <img src="https://img.shields.io/node/v/easyeda-mcp-pro" alt="supported Node.js version" />
21
+ </a>
22
+ <a href="https://opensource.org/licenses/MIT">
23
+ <img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT" />
24
+ </a>
25
+ <a href="https://www.bestpractices.dev/projects/13406">
26
+ <img src="https://www.bestpractices.dev/projects/13406/badge" alt="OpenSSF Best Practices" />
27
+ </a>
28
+ <a href="https://github.com/oaslananka/easyeda-mcp-pro/security/policy">
29
+ <img src="https://img.shields.io/badge/security-policy-blue" alt="Security policy" />
30
+ </a>
31
+ <a href="https://deepwiki.com/oaslananka/easyeda-mcp-pro">
32
+ <img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki" />
14
33
  </a>
15
34
  </p>
16
35
 
36
+ <p>
37
+ <strong>Compliance docs:</strong>
38
+ <a href="THIRD_PARTY_NOTICES.md">Third-Party Notices</a>
39
+ ·
40
+ <a href="docs/vendor-terms.md">Vendor Terms and Unsupported Workflows</a>
41
+ ·
42
+ <a href="docs/OPENSSF_BEST_PRACTICES.md">OpenSSF Best Practices</a>
43
+ ·
44
+ <a href="docs/REMOTE_MCP_MODES.md">Remote MCP Modes</a>
45
+ </p>
46
+
47
+ </div>
48
+
17
49
  ---
18
50
 
19
51
  ## Quick Start
@@ -46,7 +78,7 @@ For advanced configurations, manual instructions, and specific clients, see [Ins
46
78
 
47
79
  ## Overview
48
80
 
49
- easyeda-mcp-pro is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that bridges AI assistants with hardware design workflows in EasyEDA Pro. It exposes 52 profile-gated MCP tools for schematic inspection and editing, controlled EasyEDA Pro API calls, BOM management, design rule checks, PCB board analysis, fabrication exports, and supplier integration.
81
+ easyeda-mcp-pro is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that bridges AI assistants with hardware design workflows in EasyEDA Pro. It exposes up to 60 profile-gated MCP tools for schematic inspection and editing, controlled EasyEDA Pro API calls, BOM management, design rule checks, PCB board analysis, fabrication exports, diagnostics, and supplier integration.
50
82
 
51
83
  The server connects to EasyEDA Pro via a WebSocket bridge extension, enabling real-time access to open project data. It integrates with JLCPCB, LCSC, Mouser, and DigiKey for BOM sourcing and pricing.
52
84
 
@@ -456,6 +488,24 @@ When `OAUTH_ENABLED=true`, every request to `/mcp` must include an `Authorizatio
456
488
 
457
489
  The server enforces startup safety checks: **non-loopback `HTTP_HOST` without OAuth is rejected**, and `HTTP_AUTH_DISABLED=true` is rejected outside non-production loopback deployments.
458
490
 
491
+ ### Docker defaults
492
+
493
+ The Docker image starts in HTTP mode with `HTTP_HOST=127.0.0.1` so the default container boot path is safe and passes the same startup safety checks as local HTTP mode. For an externally reachable container, override the bind address and configure OAuth plus an explicit origin allowlist:
494
+
495
+ ```bash
496
+ docker run --rm \
497
+ -e HTTP_HOST=0.0.0.0 \
498
+ -e ALLOWED_ORIGINS=https://your-client.example.com \
499
+ -e OAUTH_ENABLED=true \
500
+ -e OAUTH_ISSUER=https://issuer.example.com/ \
501
+ -e OAUTH_JWKS_URI=https://issuer.example.com/.well-known/jwks.json \
502
+ -e OAUTH_AUDIENCE=easyeda-mcp-pro \
503
+ -p 127.0.0.1:3000:3000 \
504
+ ghcr.io/oaslananka/easyeda-mcp-pro:latest
505
+ ```
506
+
507
+ Do not expose non-loopback HTTP without OAuth. Use a reverse proxy or platform gateway for TLS termination and external access.
508
+
459
509
  #### HTTP Security Features
460
510
 
461
511
  - **Rate limiting**: Per-IP sliding window (configurable via `HTTP_RATE_LIMIT_MAX`), returns `429 Too Many Requests` with retry-after header
@@ -468,7 +518,14 @@ See `.env.example` for the complete list of configuration variables.
468
518
 
469
519
  ## MCP Tools
470
520
 
471
- The server currently registers 50 default profile-gated tools. Tools are filtered by the active `TOOL_PROFILE`: `core` exposes the normal workflow tools, `pro` adds manufacturing exports, `full` adds controlled documented EasyEDA API calls, and `dev` adds runtime probes for debugging.
521
+ The server currently registers up to 60 profile-gated tools. Tools are filtered by the active `TOOL_PROFILE`:
522
+
523
+ - `core`: 42 tools
524
+ - `pro`: 47 tools
525
+ - `full`: 56 tools
526
+ - `dev`: 60 tools
527
+
528
+ `core` exposes the standard workflow tools, `pro` adds manufacturing exports, `full` adds controlled documented EasyEDA API calls, and `dev` adds runtime probes for debugging.
472
529
 
473
530
  Capability scopes add a second authorization layer when `TOOL_SCOPES` is set. Leave it empty for the default local all-capabilities mode, or restrict it with comma/space separated scopes such as `diagnostics:read`, `schematic:read`, `schematic:write`, `bom:read`, `bom:source`, `checks:read`, `pcb:read`, `pcb:write`, `export:write`, `api:read`, `api:write`, and `bridge:execute`.
474
531
 
@@ -568,7 +625,7 @@ The schematic write APIs use EasyEDA Pro extension APIs that EasyEDA currently m
568
625
  │ (via Plugin) │ Protocol │ └───────────────┘ │
569
626
  └─────────────────┘ │ ┌───────────────┐ │
570
627
  │ │ ToolRegistry │ │
571
- │ │ (41 tools) │ │
628
+ │ │ (up to 60 tools) │ │
572
629
  │ └───────────────┘ │
573
630
  │ ┌───────────────┐ │
574
631
  │ │ Storage │──┼──► SQLite
@@ -712,6 +769,18 @@ This repository uses automated workflows to manage dependencies and releases:
712
769
 
713
770
  ---
714
771
 
772
+ ## Support the project
773
+
774
+ If this project helps you save time while working with EasyEDA Pro, BOM workflows, or MCP integrations, you can support ongoing development here:
775
+
776
+ <p align="left">
777
+ <a href="https://www.buymeacoffee.com/oaslananka">
778
+ <img src="https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20coffee&emoji=%E2%98%95&slug=oaslananka&button_colour=FFDD00&font_colour=000000&font_family=Inter&outline_colour=000000&coffee_colour=ffffff" alt="Buy me a coffee" />
779
+ </a>
780
+ </p>
781
+
782
+ ---
783
+
715
784
  ## License
716
785
 
717
786
  [MIT](LICENSE)
@@ -1 +1 @@
1
- export declare const SERVER_VERSION = "0.17.0";
1
+ export declare const SERVER_VERSION = "0.18.0";
@@ -1,2 +1,2 @@
1
- export const SERVER_VERSION = '0.17.0'; // x-release-please-version
1
+ export const SERVER_VERSION = '0.18.0'; // x-release-please-version
2
2
  //# sourceMappingURL=version.js.map
@@ -0,0 +1,33 @@
1
+ import type { ActiveProject, RemoteRiskLevel } from './protocol.js';
2
+ export interface ApprovalRequestInput {
3
+ approvalId: string;
4
+ userId: string;
5
+ sessionId: string;
6
+ toolName: string;
7
+ riskLevel: RemoteRiskLevel;
8
+ inputHash: string;
9
+ actionSummary: string;
10
+ activeProject?: ActiveProject;
11
+ expiresAt: Date;
12
+ }
13
+ export type ApprovalDecision = 'approved' | 'rejected' | 'timeout';
14
+ export interface ApprovalRecord extends ApprovalRequestInput {
15
+ decision?: ApprovalDecision;
16
+ decidedAt?: Date;
17
+ }
18
+ export declare function requiresApproval(riskLevel: RemoteRiskLevel): boolean;
19
+ export declare function isDestructiveRisk(riskLevel: RemoteRiskLevel): boolean;
20
+ export declare class ApprovalStore {
21
+ private readonly approvals;
22
+ request(input: ApprovalRequestInput): ApprovalRecord;
23
+ resolve(approvalId: string, decision: ApprovalDecision, now?: Date): ApprovalRecord | undefined;
24
+ consumeApproved(input: {
25
+ approvalId: string;
26
+ userId: string;
27
+ sessionId: string;
28
+ toolName: string;
29
+ inputHash: string;
30
+ now?: Date;
31
+ }): boolean;
32
+ get(approvalId: string): ApprovalRecord | undefined;
33
+ }
@@ -0,0 +1,41 @@
1
+ export function requiresApproval(riskLevel) {
2
+ return riskLevel !== 'read';
3
+ }
4
+ export function isDestructiveRisk(riskLevel) {
5
+ return riskLevel === 'destructive';
6
+ }
7
+ export class ApprovalStore {
8
+ approvals = new Map();
9
+ request(input) {
10
+ const record = { ...input };
11
+ this.approvals.set(input.approvalId, record);
12
+ return record;
13
+ }
14
+ resolve(approvalId, decision, now = new Date()) {
15
+ const record = this.approvals.get(approvalId);
16
+ if (!record)
17
+ return undefined;
18
+ record.decision = now.getTime() > record.expiresAt.getTime() ? 'timeout' : decision;
19
+ record.decidedAt = now;
20
+ return record;
21
+ }
22
+ consumeApproved(input) {
23
+ const record = this.approvals.get(input.approvalId);
24
+ if (!record)
25
+ return false;
26
+ const now = input.now ?? new Date();
27
+ const matches = record.userId === input.userId &&
28
+ record.sessionId === input.sessionId &&
29
+ record.toolName === input.toolName &&
30
+ record.inputHash === input.inputHash &&
31
+ record.decision === 'approved' &&
32
+ record.expiresAt.getTime() > now.getTime();
33
+ if (matches)
34
+ this.approvals.delete(input.approvalId);
35
+ return matches;
36
+ }
37
+ get(approvalId) {
38
+ return this.approvals.get(approvalId);
39
+ }
40
+ }
41
+ //# sourceMappingURL=approval-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-policy.js","sourceRoot":"","sources":["../../src/remote/approval-policy.ts"],"names":[],"mappings":"AAqBA,MAAM,UAAU,gBAAgB,CAAC,SAA0B;IACzD,OAAO,SAAS,KAAK,MAAM,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,SAA0B;IAC1D,OAAO,SAAS,KAAK,aAAa,CAAC;AACrC,CAAC;AAED,MAAM,OAAO,aAAa;IACP,SAAS,GAAG,IAAI,GAAG,EAA0B,CAAC;IAE/D,OAAO,CAAC,KAA2B;QACjC,MAAM,MAAM,GAAmB,EAAE,GAAG,KAAK,EAAE,CAAC;QAC5C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC7C,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,CACL,UAAkB,EAClB,QAA0B,EAC1B,GAAG,GAAG,IAAI,IAAI,EAAE;QAEhB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC9B,MAAM,CAAC,QAAQ,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;QACpF,MAAM,CAAC,SAAS,GAAG,GAAG,CAAC;QACvB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,eAAe,CAAC,KAOf;QACC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;QACpC,MAAM,OAAO,GACX,MAAM,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;YAC9B,MAAM,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS;YACpC,MAAM,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ;YAClC,MAAM,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS;YACpC,MAAM,CAAC,QAAQ,KAAK,UAAU;YAC9B,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;QAC7C,IAAI,OAAO;YAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACrD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,GAAG,CAAC,UAAkB;QACpB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC;CACF"}
@@ -0,0 +1,5 @@
1
+ export * from './approval-policy.js';
2
+ export * from './observability.js';
3
+ export * from './protocol.js';
4
+ export * from './scope.js';
5
+ export * from './session-router.js';
@@ -0,0 +1,6 @@
1
+ export * from './approval-policy.js';
2
+ export * from './observability.js';
3
+ export * from './protocol.js';
4
+ export * from './scope.js';
5
+ export * from './session-router.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/remote/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { RemoteDeploymentMode, RemoteRiskLevel } from './protocol.js';
2
+ export type RemoteAuditEventName = 'remote.session.registered' | 'remote.session.paired' | 'remote.session.disconnected' | 'remote.tool.requested' | 'remote.tool.dispatched' | 'remote.tool.completed' | 'remote.tool.failed' | 'remote.approval.requested' | 'remote.approval.resolved' | 'remote.identity.rejected';
3
+ export interface RemoteAuditEvent {
4
+ event: RemoteAuditEventName;
5
+ observedAt: string;
6
+ mode: RemoteDeploymentMode;
7
+ userId?: string;
8
+ sessionId?: string;
9
+ connectionId?: string;
10
+ toolName?: string;
11
+ riskLevel?: RemoteRiskLevel;
12
+ inputHash?: string;
13
+ status?: 'ok' | 'error' | 'rejected' | 'timeout';
14
+ durationMs?: number;
15
+ errorCode?: string;
16
+ }
17
+ export declare function redactRemotePayload(value: unknown): unknown;
18
+ export declare class RemoteAuditLog {
19
+ private readonly maxEvents;
20
+ private events;
21
+ constructor(maxEvents?: number);
22
+ record(event: Omit<RemoteAuditEvent, 'observedAt'> & {
23
+ observedAt?: string;
24
+ }): RemoteAuditEvent;
25
+ recent(limit?: number): RemoteAuditEvent[];
26
+ clear(): void;
27
+ }
@@ -0,0 +1,37 @@
1
+ const REDACTED = '[redacted]';
2
+ const SECRET_KEY_PATTERN = /(token|secret|password|credential|apiKey|api_key|pairingCode)/i;
3
+ export function redactRemotePayload(value) {
4
+ if (Array.isArray(value))
5
+ return value.map((item) => redactRemotePayload(item));
6
+ if (!value || typeof value !== 'object')
7
+ return value;
8
+ const out = {};
9
+ for (const [key, child] of Object.entries(value)) {
10
+ out[key] = SECRET_KEY_PATTERN.test(key) ? REDACTED : redactRemotePayload(child);
11
+ }
12
+ return out;
13
+ }
14
+ export class RemoteAuditLog {
15
+ maxEvents;
16
+ events = [];
17
+ constructor(maxEvents = 200) {
18
+ this.maxEvents = maxEvents;
19
+ }
20
+ record(event) {
21
+ const stored = {
22
+ ...event,
23
+ observedAt: event.observedAt ?? new Date().toISOString(),
24
+ };
25
+ this.events.push(stored);
26
+ if (this.events.length > this.maxEvents)
27
+ this.events = this.events.slice(-this.maxEvents);
28
+ return stored;
29
+ }
30
+ recent(limit = 25) {
31
+ return this.events.slice(-limit);
32
+ }
33
+ clear() {
34
+ this.events = [];
35
+ }
36
+ }
37
+ //# sourceMappingURL=observability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observability.js","sourceRoot":"","sources":["../../src/remote/observability.ts"],"names":[],"mappings":"AA6BA,MAAM,QAAQ,GAAG,YAAY,CAAC;AAC9B,MAAM,kBAAkB,GAAG,gEAAgE,CAAC;AAE5F,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC;IAChF,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,GAAG,CAAC,GAAG,CAAC,GAAG,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,OAAO,cAAc;IAGI;IAFrB,MAAM,GAAuB,EAAE,CAAC;IAExC,YAA6B,YAAY,GAAG;QAAf,cAAS,GAAT,SAAS,CAAM;IAAG,CAAC;IAEhD,MAAM,CAAC,KAAqE;QAC1E,MAAM,MAAM,GAAqB;YAC/B,GAAG,KAAK;YACR,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACzD,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS;YAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC1F,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,CAAC,KAAK,GAAG,EAAE;QACf,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,321 @@
1
+ import { z } from 'zod';
2
+ export declare const REMOTE_RELAY_PROTOCOL_VERSION: "2026-07-remote-relay-v1";
3
+ export declare const RemoteDeploymentModeSchema: z.ZodEnum<{
4
+ local: "local";
5
+ hosted: "hosted";
6
+ self_hosted: "self_hosted";
7
+ }>;
8
+ export declare const RemoteRiskLevelSchema: z.ZodEnum<{
9
+ export: "export";
10
+ write: "write";
11
+ read: "read";
12
+ destructive: "destructive";
13
+ }>;
14
+ export declare const RemoteScopeSchema: z.ZodEnum<{
15
+ "easyeda.read": "easyeda.read";
16
+ "easyeda.write": "easyeda.write";
17
+ "easyeda.export": "easyeda.export";
18
+ "easyeda.project_admin": "easyeda.project_admin";
19
+ }>;
20
+ export type RemoteDeploymentMode = z.infer<typeof RemoteDeploymentModeSchema>;
21
+ export type RemoteRiskLevel = z.infer<typeof RemoteRiskLevelSchema>;
22
+ export type RemoteScope = z.infer<typeof RemoteScopeSchema>;
23
+ export declare const ActiveProjectSchema: z.ZodObject<{
24
+ projectId: z.ZodOptional<z.ZodString>;
25
+ projectName: z.ZodOptional<z.ZodString>;
26
+ documentType: z.ZodDefault<z.ZodEnum<{
27
+ unknown: "unknown";
28
+ schematic: "schematic";
29
+ pcb: "pcb";
30
+ }>>;
31
+ url: z.ZodOptional<z.ZodString>;
32
+ }, z.core.$strip>;
33
+ export type ActiveProject = z.infer<typeof ActiveProjectSchema>;
34
+ export declare const RegisterSessionMessageSchema: z.ZodObject<{
35
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
36
+ messageId: z.ZodString;
37
+ sessionId: z.ZodOptional<z.ZodString>;
38
+ timestamp: z.ZodString;
39
+ type: z.ZodLiteral<"register_session">;
40
+ extensionVersion: z.ZodString;
41
+ mode: z.ZodEnum<{
42
+ hosted: "hosted";
43
+ self_hosted: "self_hosted";
44
+ }>;
45
+ activeProject: z.ZodOptional<z.ZodObject<{
46
+ projectId: z.ZodOptional<z.ZodString>;
47
+ projectName: z.ZodOptional<z.ZodString>;
48
+ documentType: z.ZodDefault<z.ZodEnum<{
49
+ unknown: "unknown";
50
+ schematic: "schematic";
51
+ pcb: "pcb";
52
+ }>>;
53
+ url: z.ZodOptional<z.ZodString>;
54
+ }, z.core.$strip>>;
55
+ capabilities: z.ZodDefault<z.ZodArray<z.ZodString>>;
56
+ }, z.core.$strip>;
57
+ export declare const SessionRegisteredMessageSchema: z.ZodObject<{
58
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
59
+ messageId: z.ZodString;
60
+ timestamp: z.ZodString;
61
+ type: z.ZodLiteral<"session_registered">;
62
+ sessionId: z.ZodString;
63
+ paired: z.ZodBoolean;
64
+ expiresAt: z.ZodString;
65
+ }, z.core.$strip>;
66
+ export declare const HeartbeatMessageSchema: z.ZodObject<{
67
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
68
+ messageId: z.ZodString;
69
+ sessionId: z.ZodOptional<z.ZodString>;
70
+ timestamp: z.ZodString;
71
+ type: z.ZodLiteral<"heartbeat">;
72
+ }, z.core.$strip>;
73
+ export declare const ToolRequestMessageSchema: z.ZodObject<{
74
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
75
+ messageId: z.ZodString;
76
+ timestamp: z.ZodString;
77
+ type: z.ZodLiteral<"tool_request">;
78
+ sessionId: z.ZodString;
79
+ toolName: z.ZodString;
80
+ riskLevel: z.ZodEnum<{
81
+ export: "export";
82
+ write: "write";
83
+ read: "read";
84
+ destructive: "destructive";
85
+ }>;
86
+ requiresApproval: z.ZodBoolean;
87
+ input: z.ZodOptional<z.ZodUnknown>;
88
+ inputHash: z.ZodString;
89
+ activeProjectHint: z.ZodOptional<z.ZodObject<{
90
+ projectId: z.ZodOptional<z.ZodString>;
91
+ projectName: z.ZodOptional<z.ZodString>;
92
+ documentType: z.ZodDefault<z.ZodEnum<{
93
+ unknown: "unknown";
94
+ schematic: "schematic";
95
+ pcb: "pcb";
96
+ }>>;
97
+ url: z.ZodOptional<z.ZodString>;
98
+ }, z.core.$strip>>;
99
+ deadlineMs: z.ZodOptional<z.ZodNumber>;
100
+ }, z.core.$strip>;
101
+ export declare const ToolResponseMessageSchema: z.ZodObject<{
102
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
103
+ messageId: z.ZodString;
104
+ timestamp: z.ZodString;
105
+ type: z.ZodLiteral<"tool_response">;
106
+ sessionId: z.ZodString;
107
+ requestMessageId: z.ZodString;
108
+ ok: z.ZodBoolean;
109
+ result: z.ZodOptional<z.ZodUnknown>;
110
+ error: z.ZodOptional<z.ZodObject<{
111
+ code: z.ZodString;
112
+ message: z.ZodString;
113
+ suggestion: z.ZodOptional<z.ZodString>;
114
+ }, z.core.$strip>>;
115
+ durationMs: z.ZodNumber;
116
+ }, z.core.$strip>;
117
+ export declare const ApprovalRequestMessageSchema: z.ZodObject<{
118
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
119
+ messageId: z.ZodString;
120
+ timestamp: z.ZodString;
121
+ type: z.ZodLiteral<"approval_request">;
122
+ sessionId: z.ZodString;
123
+ approvalId: z.ZodString;
124
+ toolName: z.ZodString;
125
+ riskLevel: z.ZodEnum<{
126
+ export: "export";
127
+ write: "write";
128
+ read: "read";
129
+ destructive: "destructive";
130
+ }>;
131
+ actionSummary: z.ZodString;
132
+ inputHash: z.ZodString;
133
+ activeProject: z.ZodOptional<z.ZodObject<{
134
+ projectId: z.ZodOptional<z.ZodString>;
135
+ projectName: z.ZodOptional<z.ZodString>;
136
+ documentType: z.ZodDefault<z.ZodEnum<{
137
+ unknown: "unknown";
138
+ schematic: "schematic";
139
+ pcb: "pcb";
140
+ }>>;
141
+ url: z.ZodOptional<z.ZodString>;
142
+ }, z.core.$strip>>;
143
+ expiresAt: z.ZodString;
144
+ }, z.core.$strip>;
145
+ export declare const ApprovalResultMessageSchema: z.ZodObject<{
146
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
147
+ messageId: z.ZodString;
148
+ timestamp: z.ZodString;
149
+ type: z.ZodLiteral<"approval_result">;
150
+ sessionId: z.ZodString;
151
+ approvalId: z.ZodString;
152
+ result: z.ZodEnum<{
153
+ timeout: "timeout";
154
+ rejected: "rejected";
155
+ approved: "approved";
156
+ }>;
157
+ }, z.core.$strip>;
158
+ export declare const SessionClosedMessageSchema: z.ZodObject<{
159
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
160
+ messageId: z.ZodString;
161
+ timestamp: z.ZodString;
162
+ type: z.ZodLiteral<"session_closed">;
163
+ sessionId: z.ZodString;
164
+ reason: z.ZodEnum<{
165
+ disconnected: "disconnected";
166
+ user_disabled: "user_disabled";
167
+ expired: "expired";
168
+ replaced: "replaced";
169
+ }>;
170
+ }, z.core.$strip>;
171
+ export declare const RelayErrorMessageSchema: z.ZodObject<{
172
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
173
+ messageId: z.ZodString;
174
+ timestamp: z.ZodString;
175
+ type: z.ZodLiteral<"error">;
176
+ sessionId: z.ZodOptional<z.ZodString>;
177
+ code: z.ZodString;
178
+ message: z.ZodString;
179
+ }, z.core.$strip>;
180
+ export declare const RelayMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
181
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
182
+ messageId: z.ZodString;
183
+ sessionId: z.ZodOptional<z.ZodString>;
184
+ timestamp: z.ZodString;
185
+ type: z.ZodLiteral<"register_session">;
186
+ extensionVersion: z.ZodString;
187
+ mode: z.ZodEnum<{
188
+ hosted: "hosted";
189
+ self_hosted: "self_hosted";
190
+ }>;
191
+ activeProject: z.ZodOptional<z.ZodObject<{
192
+ projectId: z.ZodOptional<z.ZodString>;
193
+ projectName: z.ZodOptional<z.ZodString>;
194
+ documentType: z.ZodDefault<z.ZodEnum<{
195
+ unknown: "unknown";
196
+ schematic: "schematic";
197
+ pcb: "pcb";
198
+ }>>;
199
+ url: z.ZodOptional<z.ZodString>;
200
+ }, z.core.$strip>>;
201
+ capabilities: z.ZodDefault<z.ZodArray<z.ZodString>>;
202
+ }, z.core.$strip>, z.ZodObject<{
203
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
204
+ messageId: z.ZodString;
205
+ timestamp: z.ZodString;
206
+ type: z.ZodLiteral<"session_registered">;
207
+ sessionId: z.ZodString;
208
+ paired: z.ZodBoolean;
209
+ expiresAt: z.ZodString;
210
+ }, z.core.$strip>, z.ZodObject<{
211
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
212
+ messageId: z.ZodString;
213
+ sessionId: z.ZodOptional<z.ZodString>;
214
+ timestamp: z.ZodString;
215
+ type: z.ZodLiteral<"heartbeat">;
216
+ }, z.core.$strip>, z.ZodObject<{
217
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
218
+ messageId: z.ZodString;
219
+ timestamp: z.ZodString;
220
+ type: z.ZodLiteral<"tool_request">;
221
+ sessionId: z.ZodString;
222
+ toolName: z.ZodString;
223
+ riskLevel: z.ZodEnum<{
224
+ export: "export";
225
+ write: "write";
226
+ read: "read";
227
+ destructive: "destructive";
228
+ }>;
229
+ requiresApproval: z.ZodBoolean;
230
+ input: z.ZodOptional<z.ZodUnknown>;
231
+ inputHash: z.ZodString;
232
+ activeProjectHint: z.ZodOptional<z.ZodObject<{
233
+ projectId: z.ZodOptional<z.ZodString>;
234
+ projectName: z.ZodOptional<z.ZodString>;
235
+ documentType: z.ZodDefault<z.ZodEnum<{
236
+ unknown: "unknown";
237
+ schematic: "schematic";
238
+ pcb: "pcb";
239
+ }>>;
240
+ url: z.ZodOptional<z.ZodString>;
241
+ }, z.core.$strip>>;
242
+ deadlineMs: z.ZodOptional<z.ZodNumber>;
243
+ }, z.core.$strip>, z.ZodObject<{
244
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
245
+ messageId: z.ZodString;
246
+ timestamp: z.ZodString;
247
+ type: z.ZodLiteral<"tool_response">;
248
+ sessionId: z.ZodString;
249
+ requestMessageId: z.ZodString;
250
+ ok: z.ZodBoolean;
251
+ result: z.ZodOptional<z.ZodUnknown>;
252
+ error: z.ZodOptional<z.ZodObject<{
253
+ code: z.ZodString;
254
+ message: z.ZodString;
255
+ suggestion: z.ZodOptional<z.ZodString>;
256
+ }, z.core.$strip>>;
257
+ durationMs: z.ZodNumber;
258
+ }, z.core.$strip>, z.ZodObject<{
259
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
260
+ messageId: z.ZodString;
261
+ timestamp: z.ZodString;
262
+ type: z.ZodLiteral<"approval_request">;
263
+ sessionId: z.ZodString;
264
+ approvalId: z.ZodString;
265
+ toolName: z.ZodString;
266
+ riskLevel: z.ZodEnum<{
267
+ export: "export";
268
+ write: "write";
269
+ read: "read";
270
+ destructive: "destructive";
271
+ }>;
272
+ actionSummary: z.ZodString;
273
+ inputHash: z.ZodString;
274
+ activeProject: z.ZodOptional<z.ZodObject<{
275
+ projectId: z.ZodOptional<z.ZodString>;
276
+ projectName: z.ZodOptional<z.ZodString>;
277
+ documentType: z.ZodDefault<z.ZodEnum<{
278
+ unknown: "unknown";
279
+ schematic: "schematic";
280
+ pcb: "pcb";
281
+ }>>;
282
+ url: z.ZodOptional<z.ZodString>;
283
+ }, z.core.$strip>>;
284
+ expiresAt: z.ZodString;
285
+ }, z.core.$strip>, z.ZodObject<{
286
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
287
+ messageId: z.ZodString;
288
+ timestamp: z.ZodString;
289
+ type: z.ZodLiteral<"approval_result">;
290
+ sessionId: z.ZodString;
291
+ approvalId: z.ZodString;
292
+ result: z.ZodEnum<{
293
+ timeout: "timeout";
294
+ rejected: "rejected";
295
+ approved: "approved";
296
+ }>;
297
+ }, z.core.$strip>, z.ZodObject<{
298
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
299
+ messageId: z.ZodString;
300
+ timestamp: z.ZodString;
301
+ type: z.ZodLiteral<"session_closed">;
302
+ sessionId: z.ZodString;
303
+ reason: z.ZodEnum<{
304
+ disconnected: "disconnected";
305
+ user_disabled: "user_disabled";
306
+ expired: "expired";
307
+ replaced: "replaced";
308
+ }>;
309
+ }, z.core.$strip>, z.ZodObject<{
310
+ protocolVersion: z.ZodLiteral<"2026-07-remote-relay-v1">;
311
+ messageId: z.ZodString;
312
+ timestamp: z.ZodString;
313
+ type: z.ZodLiteral<"error">;
314
+ sessionId: z.ZodOptional<z.ZodString>;
315
+ code: z.ZodString;
316
+ message: z.ZodString;
317
+ }, z.core.$strip>], "type">;
318
+ export type RelayMessage = z.infer<typeof RelayMessageSchema>;
319
+ export type RegisterSessionMessage = z.infer<typeof RegisterSessionMessageSchema>;
320
+ export type ToolRequestMessage = z.infer<typeof ToolRequestMessageSchema>;
321
+ export type ApprovalRequestMessage = z.infer<typeof ApprovalRequestMessageSchema>;
@@ -0,0 +1,104 @@
1
+ import { z } from 'zod';
2
+ export const REMOTE_RELAY_PROTOCOL_VERSION = '2026-07-remote-relay-v1';
3
+ export const RemoteDeploymentModeSchema = z.enum(['local', 'hosted', 'self_hosted']);
4
+ export const RemoteRiskLevelSchema = z.enum(['read', 'write', 'export', 'destructive']);
5
+ export const RemoteScopeSchema = z.enum([
6
+ 'easyeda.read',
7
+ 'easyeda.write',
8
+ 'easyeda.export',
9
+ 'easyeda.project_admin',
10
+ ]);
11
+ export const ActiveProjectSchema = z.object({
12
+ projectId: z.string().min(1).optional(),
13
+ projectName: z.string().min(1).optional(),
14
+ documentType: z.enum(['schematic', 'pcb', 'unknown']).default('unknown'),
15
+ url: z.string().url().optional(),
16
+ });
17
+ const BaseRelayMessageSchema = z.object({
18
+ protocolVersion: z.literal(REMOTE_RELAY_PROTOCOL_VERSION),
19
+ messageId: z.string().min(1),
20
+ sessionId: z.string().min(1).optional(),
21
+ timestamp: z.string().datetime(),
22
+ });
23
+ export const RegisterSessionMessageSchema = BaseRelayMessageSchema.extend({
24
+ type: z.literal('register_session'),
25
+ extensionVersion: z.string().min(1),
26
+ mode: RemoteDeploymentModeSchema.exclude(['local']),
27
+ activeProject: ActiveProjectSchema.optional(),
28
+ capabilities: z.array(z.string().min(1)).default([]),
29
+ });
30
+ export const SessionRegisteredMessageSchema = BaseRelayMessageSchema.extend({
31
+ type: z.literal('session_registered'),
32
+ sessionId: z.string().min(1),
33
+ paired: z.boolean(),
34
+ expiresAt: z.string().datetime(),
35
+ });
36
+ export const HeartbeatMessageSchema = BaseRelayMessageSchema.extend({
37
+ type: z.literal('heartbeat'),
38
+ });
39
+ export const ToolRequestMessageSchema = BaseRelayMessageSchema.extend({
40
+ type: z.literal('tool_request'),
41
+ sessionId: z.string().min(1),
42
+ toolName: z.string().min(1),
43
+ riskLevel: RemoteRiskLevelSchema,
44
+ requiresApproval: z.boolean(),
45
+ input: z.unknown().optional(),
46
+ inputHash: z.string().min(1),
47
+ activeProjectHint: ActiveProjectSchema.optional(),
48
+ deadlineMs: z.number().int().positive().optional(),
49
+ });
50
+ export const ToolResponseMessageSchema = BaseRelayMessageSchema.extend({
51
+ type: z.literal('tool_response'),
52
+ sessionId: z.string().min(1),
53
+ requestMessageId: z.string().min(1),
54
+ ok: z.boolean(),
55
+ result: z.unknown().optional(),
56
+ error: z
57
+ .object({
58
+ code: z.string().min(1),
59
+ message: z.string().min(1),
60
+ suggestion: z.string().min(1).optional(),
61
+ })
62
+ .optional(),
63
+ durationMs: z.number().nonnegative(),
64
+ });
65
+ export const ApprovalRequestMessageSchema = BaseRelayMessageSchema.extend({
66
+ type: z.literal('approval_request'),
67
+ sessionId: z.string().min(1),
68
+ approvalId: z.string().min(1),
69
+ toolName: z.string().min(1),
70
+ riskLevel: RemoteRiskLevelSchema,
71
+ actionSummary: z.string().min(1),
72
+ inputHash: z.string().min(1),
73
+ activeProject: ActiveProjectSchema.optional(),
74
+ expiresAt: z.string().datetime(),
75
+ });
76
+ export const ApprovalResultMessageSchema = BaseRelayMessageSchema.extend({
77
+ type: z.literal('approval_result'),
78
+ sessionId: z.string().min(1),
79
+ approvalId: z.string().min(1),
80
+ result: z.enum(['approved', 'rejected', 'timeout']),
81
+ });
82
+ export const SessionClosedMessageSchema = BaseRelayMessageSchema.extend({
83
+ type: z.literal('session_closed'),
84
+ sessionId: z.string().min(1),
85
+ reason: z.enum(['user_disabled', 'expired', 'disconnected', 'replaced']),
86
+ });
87
+ export const RelayErrorMessageSchema = BaseRelayMessageSchema.extend({
88
+ type: z.literal('error'),
89
+ sessionId: z.string().min(1).optional(),
90
+ code: z.string().min(1),
91
+ message: z.string().min(1),
92
+ });
93
+ export const RelayMessageSchema = z.discriminatedUnion('type', [
94
+ RegisterSessionMessageSchema,
95
+ SessionRegisteredMessageSchema,
96
+ HeartbeatMessageSchema,
97
+ ToolRequestMessageSchema,
98
+ ToolResponseMessageSchema,
99
+ ApprovalRequestMessageSchema,
100
+ ApprovalResultMessageSchema,
101
+ SessionClosedMessageSchema,
102
+ RelayErrorMessageSchema,
103
+ ]);
104
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/remote/protocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { RemoteRiskLevel, RemoteScope } from './protocol.js';
2
+ export interface RemoteIdentity {
3
+ userId: string;
4
+ scopes: readonly RemoteScope[];
5
+ expiresAt?: Date;
6
+ }
7
+ export type ScopeCheckResult = {
8
+ ok: true;
9
+ } | {
10
+ ok: false;
11
+ code: 'IDENTITY_MISSING' | 'IDENTITY_EXPIRED' | 'SCOPE_MISSING';
12
+ message: string;
13
+ };
14
+ export declare function requiredScopeForRisk(riskLevel: RemoteRiskLevel): RemoteScope;
15
+ export declare function hasRemoteScope(identity: RemoteIdentity, scope: RemoteScope): boolean;
16
+ export declare function checkRemoteScope(identity: RemoteIdentity | undefined, riskLevel: RemoteRiskLevel, now?: Date): ScopeCheckResult;
@@ -0,0 +1,25 @@
1
+ export function requiredScopeForRisk(riskLevel) {
2
+ if (riskLevel === 'read')
3
+ return 'easyeda.read';
4
+ if (riskLevel === 'write')
5
+ return 'easyeda.write';
6
+ if (riskLevel === 'export')
7
+ return 'easyeda.export';
8
+ return 'easyeda.project_admin';
9
+ }
10
+ export function hasRemoteScope(identity, scope) {
11
+ return identity.scopes.includes(scope) || identity.scopes.includes('easyeda.project_admin');
12
+ }
13
+ export function checkRemoteScope(identity, riskLevel, now = new Date()) {
14
+ if (!identity)
15
+ return { ok: false, code: 'IDENTITY_MISSING', message: 'Remote identity is required.' };
16
+ if (identity.expiresAt && identity.expiresAt.getTime() <= now.getTime()) {
17
+ return { ok: false, code: 'IDENTITY_EXPIRED', message: 'Remote identity has expired.' };
18
+ }
19
+ const required = requiredScopeForRisk(riskLevel);
20
+ if (!hasRemoteScope(identity, required)) {
21
+ return { ok: false, code: 'SCOPE_MISSING', message: `Remote tool requires ${required}.` };
22
+ }
23
+ return { ok: true };
24
+ }
25
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/remote/scope.ts"],"names":[],"mappings":"AAYA,MAAM,UAAU,oBAAoB,CAAC,SAA0B;IAC7D,IAAI,SAAS,KAAK,MAAM;QAAE,OAAO,cAAc,CAAC;IAChD,IAAI,SAAS,KAAK,OAAO;QAAE,OAAO,eAAe,CAAC;IAClD,IAAI,SAAS,KAAK,QAAQ;QAAE,OAAO,gBAAgB,CAAC;IACpD,OAAO,uBAAuB,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAwB,EAAE,KAAkB;IACzE,OAAO,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CAAC;AAC9F,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,QAAoC,EACpC,SAA0B,EAC1B,GAAG,GAAG,IAAI,IAAI,EAAE;IAEhB,IAAI,CAAC,QAAQ;QACX,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,8BAA8B,EAAE,CAAC;IAC1F,IAAI,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;QACxE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,8BAA8B,EAAE,CAAC;IAC1F,CAAC;IACD,MAAM,QAAQ,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IACjD,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;QACxC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,wBAAwB,QAAQ,GAAG,EAAE,CAAC;IAC5F,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC"}
@@ -0,0 +1,54 @@
1
+ import type { ActiveProject, RemoteDeploymentMode, RemoteRiskLevel } from './protocol.js';
2
+ export interface ExtensionSession {
3
+ sessionId: string;
4
+ connectionId: string;
5
+ mode: Exclude<RemoteDeploymentMode, 'local'>;
6
+ extensionVersion: string;
7
+ userId?: string;
8
+ activeProject?: ActiveProject;
9
+ connected: boolean;
10
+ createdAt: Date;
11
+ lastSeenAt: Date;
12
+ expiresAt: Date;
13
+ }
14
+ export type SessionRouteResult = {
15
+ ok: true;
16
+ session: ExtensionSession;
17
+ } | {
18
+ ok: false;
19
+ code: 'SESSION_UNPAIRED' | 'SESSION_DISCONNECTED' | 'SESSION_EXPIRED' | 'SESSION_AMBIGUOUS' | 'PROJECT_INACTIVE';
20
+ message: string;
21
+ };
22
+ export declare class RemoteSessionRouter {
23
+ private readonly now;
24
+ private readonly makeId;
25
+ private readonly sessions;
26
+ private readonly pairings;
27
+ constructor(now?: () => Date, makeId?: () => string);
28
+ registerSession(input: {
29
+ connectionId: string;
30
+ mode: Exclude<RemoteDeploymentMode, 'local'>;
31
+ extensionVersion: string;
32
+ activeProject?: ActiveProject;
33
+ ttlMs?: number;
34
+ }): ExtensionSession;
35
+ createPairingCode(input: {
36
+ userId: string;
37
+ sessionId?: string;
38
+ ttlMs?: number;
39
+ }): string;
40
+ completePairing(input: {
41
+ code: string;
42
+ userId: string;
43
+ sessionId: string;
44
+ }): boolean;
45
+ heartbeat(sessionId: string): boolean;
46
+ updateActiveProject(sessionId: string, activeProject?: ActiveProject): boolean;
47
+ disconnect(sessionId: string): boolean;
48
+ resolve(input: {
49
+ userId: string;
50
+ riskLevel: RemoteRiskLevel;
51
+ sessionId?: string;
52
+ }): SessionRouteResult;
53
+ getSession(sessionId: string): ExtensionSession | undefined;
54
+ }
@@ -0,0 +1,138 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ export class RemoteSessionRouter {
3
+ now;
4
+ makeId;
5
+ sessions = new Map();
6
+ pairings = new Map();
7
+ constructor(now = () => new Date(), makeId = () => randomUUID()) {
8
+ this.now = now;
9
+ this.makeId = makeId;
10
+ }
11
+ registerSession(input) {
12
+ const now = this.now();
13
+ const session = {
14
+ sessionId: `sess_${this.makeId()}`,
15
+ connectionId: input.connectionId,
16
+ mode: input.mode,
17
+ extensionVersion: input.extensionVersion,
18
+ activeProject: input.activeProject,
19
+ connected: true,
20
+ createdAt: now,
21
+ lastSeenAt: now,
22
+ expiresAt: new Date(now.getTime() + (input.ttlMs ?? 2 * 60 * 60 * 1000)),
23
+ };
24
+ this.sessions.set(session.sessionId, session);
25
+ return session;
26
+ }
27
+ createPairingCode(input) {
28
+ const now = this.now();
29
+ const code = `EDA-${this.makeId().replace(/-/g, '').slice(0, 8).toUpperCase()}`;
30
+ this.pairings.set(code, {
31
+ code,
32
+ userId: input.userId,
33
+ sessionId: input.sessionId,
34
+ createdAt: now,
35
+ expiresAt: new Date(now.getTime() + (input.ttlMs ?? 10 * 60 * 1000)),
36
+ });
37
+ return code;
38
+ }
39
+ completePairing(input) {
40
+ const pairing = this.pairings.get(input.code);
41
+ const session = this.sessions.get(input.sessionId);
42
+ const now = this.now();
43
+ if (!pairing || !session)
44
+ return false;
45
+ if (pairing.usedAt)
46
+ return false;
47
+ if (pairing.userId !== input.userId)
48
+ return false;
49
+ if (pairing.sessionId && pairing.sessionId !== input.sessionId)
50
+ return false;
51
+ if (pairing.expiresAt.getTime() <= now.getTime())
52
+ return false;
53
+ if (!session.connected || session.expiresAt.getTime() <= now.getTime())
54
+ return false;
55
+ pairing.usedAt = now;
56
+ session.userId = input.userId;
57
+ session.lastSeenAt = now;
58
+ return true;
59
+ }
60
+ heartbeat(sessionId) {
61
+ const session = this.sessions.get(sessionId);
62
+ if (!session || !session.connected)
63
+ return false;
64
+ session.lastSeenAt = this.now();
65
+ return true;
66
+ }
67
+ updateActiveProject(sessionId, activeProject) {
68
+ const session = this.sessions.get(sessionId);
69
+ if (!session)
70
+ return false;
71
+ session.activeProject = activeProject;
72
+ session.lastSeenAt = this.now();
73
+ return true;
74
+ }
75
+ disconnect(sessionId) {
76
+ const session = this.sessions.get(sessionId);
77
+ if (!session)
78
+ return false;
79
+ session.connected = false;
80
+ session.lastSeenAt = this.now();
81
+ return true;
82
+ }
83
+ resolve(input) {
84
+ const now = this.now();
85
+ const candidates = [...this.sessions.values()].filter((session) => session.userId === input.userId &&
86
+ (!input.sessionId || session.sessionId === input.sessionId));
87
+ if (candidates.length === 0) {
88
+ return {
89
+ ok: false,
90
+ code: 'SESSION_UNPAIRED',
91
+ message: 'No paired EasyEDA extension session.',
92
+ };
93
+ }
94
+ const active = candidates.filter((session) => session.connected);
95
+ if (active.length === 0) {
96
+ return {
97
+ ok: false,
98
+ code: 'SESSION_DISCONNECTED',
99
+ message: 'Paired EasyEDA extension is disconnected.',
100
+ };
101
+ }
102
+ const unexpired = active.filter((session) => session.expiresAt.getTime() > now.getTime());
103
+ if (unexpired.length === 0) {
104
+ return {
105
+ ok: false,
106
+ code: 'SESSION_EXPIRED',
107
+ message: 'Paired EasyEDA extension session expired.',
108
+ };
109
+ }
110
+ if (!input.sessionId && unexpired.length > 1) {
111
+ return {
112
+ ok: false,
113
+ code: 'SESSION_AMBIGUOUS',
114
+ message: 'Multiple active EasyEDA sessions require explicit selection.',
115
+ };
116
+ }
117
+ const session = unexpired[0];
118
+ if (!session) {
119
+ return {
120
+ ok: false,
121
+ code: 'SESSION_UNPAIRED',
122
+ message: 'No paired EasyEDA extension session.',
123
+ };
124
+ }
125
+ if (input.riskLevel !== 'read' && !session.activeProject) {
126
+ return {
127
+ ok: false,
128
+ code: 'PROJECT_INACTIVE',
129
+ message: 'No active EasyEDA project is visible.',
130
+ };
131
+ }
132
+ return { ok: true, session };
133
+ }
134
+ getSession(sessionId) {
135
+ return this.sessions.get(sessionId);
136
+ }
137
+ }
138
+ //# sourceMappingURL=session-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-router.js","sourceRoot":"","sources":["../../src/remote/session-router.ts"],"names":[],"mappings":"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"}
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easyeda-mcp-pro",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "mcpName": "io.github.oaslananka/easyeda-mcp-pro",
5
5
  "description": "MCP server for EasyEDA Pro: PCB inspection, BOM, exports, and hardware review.",
6
6
  "keywords": [
@@ -72,7 +72,8 @@
72
72
  "check:metadata": "node scripts/check-metadata.mjs",
73
73
  "inventory:capture": "tsx scripts/capture-runtime-inventory.mts",
74
74
  "inventory:diff": "tsx scripts/diff-runtime-inventory.mts",
75
- "eval:golden": "tsx scripts/run-evals.mts"
75
+ "eval:golden": "tsx scripts/run-evals.mts",
76
+ "eval:golden:update": "tsx scripts/run-evals.mts --update-baseline"
76
77
  },
77
78
  "dependencies": {
78
79
  "@modelcontextprotocol/sdk": "^1.29.0",