easyeda-mcp-pro 0.17.1 → 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
@@ -40,6 +40,8 @@
40
40
  <a href="docs/vendor-terms.md">Vendor Terms and Unsupported Workflows</a>
41
41
  ·
42
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>
43
45
  </p>
44
46
 
45
47
  </div>
@@ -1 +1 @@
1
- export declare const SERVER_VERSION = "0.17.1";
1
+ export declare const SERVER_VERSION = "0.18.0";
@@ -1,2 +1,2 @@
1
- export const SERVER_VERSION = '0.17.1'; // 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.1",
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": [