easyeda-mcp-pro 1.1.0 → 1.2.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "packageVersion": "1.1.0",
4
- "sourceSha256": "689104fb72f62a94653365108e3c1227035216354b597d056927fa9821761857",
3
+ "packageVersion": "1.2.0",
4
+ "sourceSha256": "e1f24bbf698c647e27b5dd11a8413e0e84c35a51f36a3fd18bd2ae15d9749460",
5
5
  "artifacts": [
6
6
  {
7
7
  "path": "dist/bom-quality/adapter.d.ts",
@@ -110,33 +110,48 @@
110
110
  },
111
111
  {
112
112
  "path": "dist/bridge/listener-ownership.d.ts",
113
- "size": 1035,
114
- "sha256": "a304ea66ee7948d3882b969b5b7ede2fa780c21e2a76feaad9147cd189e40bf2"
113
+ "size": 1359,
114
+ "sha256": "bdd46f40c2f9362fa8f820454f63dbd25799721f33fce7a9dd144af8f7dad8d9"
115
115
  },
116
116
  {
117
117
  "path": "dist/bridge/listener-ownership.js",
118
- "size": 9073,
119
- "sha256": "d567cc43c8dd920d4625c6e2e55c353670c2121a85e0263c2b0130674a1aef6f"
118
+ "size": 9951,
119
+ "sha256": "3e7f3ea0d7038049529e61ffddcdab8c01d814e206b29bc16d9aa01646b08189"
120
120
  },
121
121
  {
122
122
  "path": "dist/bridge/listener-ownership.js.map",
123
- "size": 8236,
124
- "sha256": "faa5a592f7ca9cbba8dbd303179f1e251969c8d3cec95b32420735d0f1bb7f88"
123
+ "size": 8968,
124
+ "sha256": "743ee3958e5b8a8f7b5717b52203ff646702451920d6a42c561a421f894a398d"
125
+ },
126
+ {
127
+ "path": "dist/bridge/local-bridge-relay.d.ts",
128
+ "size": 1757,
129
+ "sha256": "35c80560e756c476ff72e7eb089e292499d5cfbd1ec536cd3f5e0c3849ed77e2"
130
+ },
131
+ {
132
+ "path": "dist/bridge/local-bridge-relay.js",
133
+ "size": 11920,
134
+ "sha256": "09cdce358590e8c23b44043a6a029e51adefa4517e17dbb1c650d81bc41e6093"
135
+ },
136
+ {
137
+ "path": "dist/bridge/local-bridge-relay.js.map",
138
+ "size": 11212,
139
+ "sha256": "ebafc53ffc33d13e2ea5c1ddf73909e01d9228569dc2e453e50c223c4e3c944e"
125
140
  },
126
141
  {
127
142
  "path": "dist/bridge/manager.d.ts",
128
- "size": 4432,
129
- "sha256": "f1cb9de5ee96405860ebc51c8374cec20c43660d93c0e39ce9cd64b90b58c935"
143
+ "size": 4855,
144
+ "sha256": "d39b5e82f6cc604f54967c562e0013f61fdb8749c603bf416282f64b8e4227e5"
130
145
  },
131
146
  {
132
147
  "path": "dist/bridge/manager.js",
133
- "size": 32104,
134
- "sha256": "dfd31c6d89e71f80c000c1ec5fb27edb30b1226afd998e5901150bb4b2d42f3f"
148
+ "size": 39621,
149
+ "sha256": "4a28d1292cbf2573fdc96a9182abf4b1e97d41a8b4dab95936042afe6e684ff8"
135
150
  },
136
151
  {
137
152
  "path": "dist/bridge/manager.js.map",
138
- "size": 26073,
139
- "sha256": "e131a8da5fd43f51f3fde42e5b5b6271c4ebdadd8bce5d97ed6d55d2bc26bea4"
153
+ "size": 32517,
154
+ "sha256": "39cafb104101496c8afa75bcdf2e5bb4d1eadc8b30ea5e40f95a1d5ffa6f2740"
140
155
  },
141
156
  {
142
157
  "path": "dist/bridge/protocol.d.ts",
@@ -155,18 +170,18 @@
155
170
  },
156
171
  {
157
172
  "path": "dist/bridge/types.d.ts",
158
- "size": 3494,
159
- "sha256": "a45720d96962ae33f8f3b54f19781379dd4b30ba86ee51db5c82dca9b912d0f8"
173
+ "size": 3540,
174
+ "sha256": "419f2b49f9c9b4d1f4739322c9054ebca5fda91c09768f01d306338b53303087"
160
175
  },
161
176
  {
162
177
  "path": "dist/bridge/types.js",
163
- "size": 1951,
164
- "sha256": "e00bd4a16c3c02cd488664088494e88d05fc8e1ea260a45a9bc408ccb520c9e9"
178
+ "size": 1976,
179
+ "sha256": "536bbac55b573a16d89fff17ca06eb841d24c9375b5167338fdc3af828eefb8d"
165
180
  },
166
181
  {
167
182
  "path": "dist/bridge/types.js.map",
168
- "size": 1069,
169
- "sha256": "42df270e3ad264456adfa4db1da137178e9c73f006f6a6c345340b605fc2981e"
183
+ "size": 1082,
184
+ "sha256": "5ae20c8f60ce271fbe70d3911f1846328c4dfe735a005df0617a5d25f62b2f04"
170
185
  },
171
186
  {
172
187
  "path": "dist/catalog/errors.d.ts",
@@ -476,7 +491,7 @@
476
491
  {
477
492
  "path": "dist/config/profiles.js",
478
493
  "size": 1615,
479
- "sha256": "fc2eb5e9308216896c9e38490db61ba7e91ae606043e38bb1f47e8fee6a5b9fe"
494
+ "sha256": "db125176c23629fda56a16fcc0ad80a2bcba6f5b79f4597fa04a54e464d0466a"
480
495
  },
481
496
  {
482
497
  "path": "dist/config/profiles.js.map",
@@ -486,12 +501,12 @@
486
501
  {
487
502
  "path": "dist/config/version.d.ts",
488
503
  "size": 47,
489
- "sha256": "4d66e92f315948eb0a7bede2a814e4b5bf37aa09873d36845e5b3e170d16b833"
504
+ "sha256": "b96ca94b9d291e107668a616a75dc59541e4b7e89ad77e43c93f1424b909f371"
490
505
  },
491
506
  {
492
507
  "path": "dist/config/version.js",
493
508
  "size": 102,
494
- "sha256": "abddecccadf9dd45dcdffd33056193a1745c165607677c18c68af7d4b2ed3fe4"
509
+ "sha256": "7b5f35cc2678361240d142ae48dbf1fa28cb3ca9f611538630c7971b3523a8f3"
495
510
  },
496
511
  {
497
512
  "path": "dist/config/version.js.map",
@@ -2275,13 +2290,13 @@
2275
2290
  },
2276
2291
  {
2277
2292
  "path": "dist/tools/L1_pcb_write.js",
2278
- "size": 37972,
2279
- "sha256": "f7a618492a3c201c32f294c3338c53d60c2095329d1b509947664adbe31562bd"
2293
+ "size": 51305,
2294
+ "sha256": "c3da35b0d9169636e70afb92d1e298f445bead5abd0e8d64f9db7045e71158c2"
2280
2295
  },
2281
2296
  {
2282
2297
  "path": "dist/tools/L1_pcb_write.js.map",
2283
- "size": 31163,
2284
- "sha256": "149540ff8d58591cd94dd9fce57fde900beaa4252485c31e7f58bc5132b2fbc1"
2298
+ "size": 42696,
2299
+ "sha256": "4370110aad70d2ed2a5b6939be39fc45d31bd9ef5601130b6698f6bf080d1da4"
2285
2300
  },
2286
2301
  {
2287
2302
  "path": "dist/tools/L1_schematic_batch.d.ts",
@@ -2991,12 +3006,12 @@
2991
3006
  {
2992
3007
  "path": "easyeda-bridge-extension.checksums.json",
2993
3008
  "size": 1004,
2994
- "sha256": "ff0d255cc4f9e9fc0d821b26979317f93984699f6459d56b7935f79735b3e9a9"
3009
+ "sha256": "a15f5bddd08f4f2c712eb8e3fdc56585010a2b2fc7f2c218b683e835ef2b7fb7"
2995
3010
  },
2996
3011
  {
2997
3012
  "path": "easyeda-bridge-extension.eext",
2998
- "size": 177756,
2999
- "sha256": "e3676b31a632e30a4ef2a556d6549bb0d8a8130e1e2f8e0fb96d182d0bb33746"
3013
+ "size": 183089,
3014
+ "sha256": "0b9932e7616ddd3f8e09d927ba33fb1181df842fe6d30e6065583b45a87cfaa1"
3000
3015
  }
3001
3016
  ]
3002
3017
  }
@@ -67,6 +67,50 @@ const failClosedPcbWriteMetadata = {
67
67
  destructiveHint: false,
68
68
  },
69
69
  };
70
+ const pcbRebuildCopperInputSchema = z
71
+ .object({
72
+ pourIds: z
73
+ .array(z.string().trim().min(1))
74
+ .min(1)
75
+ .max(100)
76
+ .refine((ids) => new Set(ids).size === ids.length, {
77
+ message: 'pourIds must not contain duplicates.',
78
+ })
79
+ .optional()
80
+ .describe('Optional existing PCB_PrimitivePour primitive ids to rebuild.'),
81
+ net: z
82
+ .string()
83
+ .trim()
84
+ .min(1)
85
+ .optional()
86
+ .describe('Optional exact net-name filter. Combined with pourIds/layer when provided.'),
87
+ layer: z
88
+ .union([z.literal(1), z.literal(2), z.number().int().min(15).max(44)])
89
+ .optional()
90
+ .describe('Optional copper-layer filter: TOP=1, BOTTOM=2, INNER_1..INNER_30=15..44.'),
91
+ confirmWrite: z
92
+ .literal(true)
93
+ .describe('Must be the literal boolean true (not the string "true") to allow this write.'),
94
+ })
95
+ .strict();
96
+ const pcbRebuildCopperItemSchema = z.object({
97
+ pourId: z.string(),
98
+ pouredId: z.string().nullable(),
99
+ status: z.enum(['rebuilt', 'no-copper', 'error', 'readback-missing', 'readback-mismatch']),
100
+ verified: z.boolean(),
101
+ error: z.string().optional(),
102
+ });
103
+ const pcbRebuildCopperOutputSchema = z.object({
104
+ success: z.boolean(),
105
+ matchedCount: z.number().int().nonnegative(),
106
+ attemptedCount: z.number().int().nonnegative(),
107
+ rebuiltCount: z.number().int().nonnegative(),
108
+ noCopperCount: z.number().int().nonnegative(),
109
+ transactionCovered: z.literal(false),
110
+ planeZonesSupported: z.literal(false),
111
+ results: z.array(pcbRebuildCopperItemSchema),
112
+ error: z.string().optional(),
113
+ });
70
114
  const failClosedPcbZoneInputSchema = z.object({
71
115
  points: z.array(z.object({ x: z.number(), y: z.number() })),
72
116
  layer: z.number(),
@@ -82,12 +126,217 @@ const failClosedPcbZoneOutputSchema = z.object({
82
126
  error: z.string().optional(),
83
127
  remediation: z.string().optional(),
84
128
  });
129
+ const pcbPolygonTokenSchema = z.union([z.number().finite(), z.string().min(1)]);
130
+ const pcbPolygonSourceSchema = z
131
+ .array(pcbPolygonTokenSchema)
132
+ .min(3)
133
+ .describe('EasyEDA TPCB_PolygonSourceArray, for example ["R", x, y, width, height, 0, 0] or a coordinate/L source array.');
134
+ const pcbRegionPolygonSchema = z
135
+ .union([pcbPolygonSourceSchema, z.array(pcbPolygonSourceSchema).min(1)])
136
+ .describe('One EasyEDA polygon source array, or an array of source arrays for a complex polygon with multiple contours.');
137
+ const pcbRegionRuleTypeSchema = z.enum([
138
+ 'NO_COMPONENTS',
139
+ 'NO_WIRES',
140
+ 'NO_FILLS',
141
+ 'NO_POURS',
142
+ 'NO_INNER_ELECTRICAL_LAYERS',
143
+ ]);
144
+ function pcbRegionRuleTypeValue(rule) {
145
+ switch (rule) {
146
+ case 'NO_COMPONENTS':
147
+ return 2;
148
+ case 'NO_WIRES':
149
+ return 5;
150
+ case 'NO_FILLS':
151
+ return 6;
152
+ case 'NO_POURS':
153
+ return 7;
154
+ case 'NO_INNER_ELECTRICAL_LAYERS':
155
+ return 8;
156
+ }
157
+ }
158
+ const pcbRegionLayerSchema = z
159
+ .union([z.literal(1), z.literal(2), z.literal(12), z.number().int().min(15).max(44)])
160
+ .describe('EasyEDA TPCB_LayersOfRegion layer: TOP=1, BOTTOM=2, MULTI=12, or INNER_1..INNER_30=15..44.');
161
+ const pcbAddRegionInputSchema = z
162
+ .object({
163
+ layer: pcbRegionLayerSchema,
164
+ polygon: pcbRegionPolygonSchema,
165
+ ruleType: z
166
+ .array(pcbRegionRuleTypeSchema)
167
+ .min(1)
168
+ .max(5)
169
+ .describe('EPCB_PrimitiveRegionRuleType values to enforce in this region.'),
170
+ name: z
171
+ .string()
172
+ .min(1)
173
+ .max(200)
174
+ .optional()
175
+ .describe('Optional native Region name. EasyEDA Pro 3.2.149 may omit this field on persisted read-back; when requested but not confirmed, the tool fails closed and rolls the Region back.'),
176
+ lineWidth: z
177
+ .number()
178
+ .positive()
179
+ .optional()
180
+ .describe('Optional native EasyEDA PCB line width. EasyEDA Pro 3.2.149 may normalize this field on persisted read-back; when requested but not confirmed, the tool fails closed and rolls the Region back.'),
181
+ locked: z.boolean().default(false),
182
+ confirmWrite: z
183
+ .literal(true)
184
+ .describe('Must be the literal boolean true (not the string "true") to allow this write.'),
185
+ })
186
+ .strict();
187
+ const pcbRegionReadBackSchema = z.object({
188
+ primitiveId: z.string(),
189
+ layer: z.number().optional(),
190
+ ruleTypes: z.array(z.number().int()),
191
+ regionName: z.string().optional(),
192
+ lineWidth: z.number().optional(),
193
+ locked: z.boolean(),
194
+ polygonSource: z.unknown().optional(),
195
+ });
196
+ const pcbAddRegionOutputSchema = z.object({
197
+ success: z.boolean(),
198
+ primitiveId: z.string().optional(),
199
+ ruleType: z.array(pcbRegionRuleTypeSchema).optional(),
200
+ ruleTypeValues: z.array(z.number().int()).optional(),
201
+ readBack: pcbRegionReadBackSchema.optional(),
202
+ rolledBack: z.boolean().optional(),
203
+ error: z.string().optional(),
204
+ });
205
+ function normalizedRuleTypes(value) {
206
+ return [...value].sort((a, b) => a - b);
207
+ }
208
+ function regionReadBackMatches(item, expected, ruleTypeValues) {
209
+ if (item.layer !== expected.layer)
210
+ return false;
211
+ if (JSON.stringify(normalizedRuleTypes(item.ruleTypes)) !==
212
+ JSON.stringify([...ruleTypeValues].sort((a, b) => a - b))) {
213
+ return false;
214
+ }
215
+ if (expected.name !== undefined && item.regionName !== expected.name)
216
+ return false;
217
+ if (expected.lineWidth !== undefined && item.lineWidth !== expected.lineWidth)
218
+ return false;
219
+ if (item.locked !== expected.locked)
220
+ return false;
221
+ return JSON.stringify(item.polygonSource) === JSON.stringify(expected.polygon);
222
+ }
223
+ const pcbRegionCreateResultSchema = z.object({
224
+ result: z.object({
225
+ state: z.object({ PrimitiveId: z.string().min(1) }),
226
+ }),
227
+ });
228
+ function primitiveIdFromApiCall(value) {
229
+ const parsed = pcbRegionCreateResultSchema.safeParse(value);
230
+ return parsed.success ? parsed.data.result.state.PrimitiveId : undefined;
231
+ }
232
+ async function listRegionsForWrite(ctx) {
233
+ const result = await ctx.bridge.call('pcb.listRegions', { limit: 200, offset: 0 });
234
+ const parsed = z.array(pcbRegionReadBackSchema).safeParse(result.items ?? []);
235
+ if (!parsed.success) {
236
+ throw new Error('PCB region read-back returned an unexpected shape.');
237
+ }
238
+ return parsed.data;
239
+ }
240
+ async function rollbackCreatedRegion(ctx, primitiveId) {
241
+ const deletion = await ctx.bridge.call('pcb.deleteComponent', { primitiveIds: [primitiveId] });
242
+ if (!deletion.deleted?.includes(primitiveId))
243
+ return false;
244
+ const remaining = await listRegionsForWrite(ctx);
245
+ return !remaining.some((item) => item.primitiveId === primitiveId);
246
+ }
247
+ function taggedRegionPolygon(polygon) {
248
+ return Array.isArray(polygon[0]) ? { $complexPolygon: polygon } : { $polygon: polygon };
249
+ }
250
+ function findCreatedRegion(after, beforeIds, returnedPrimitiveId) {
251
+ if (returnedPrimitiveId) {
252
+ const exact = after.find((item) => item.primitiveId === returnedPrimitiveId);
253
+ if (exact)
254
+ return exact;
255
+ }
256
+ const added = after.filter((item) => !beforeIds.has(item.primitiveId));
257
+ return added.length === 1 ? added[0] : undefined;
258
+ }
259
+ async function failedRegionWrite(ctx, parsed, ruleTypeValues, message, primitiveId, readBack) {
260
+ let rolledBack;
261
+ if (primitiveId) {
262
+ try {
263
+ rolledBack = await rollbackCreatedRegion(ctx, primitiveId);
264
+ }
265
+ catch {
266
+ rolledBack = false;
267
+ }
268
+ }
269
+ return {
270
+ success: false,
271
+ ...(primitiveId ? { primitiveId } : {}),
272
+ ruleType: parsed.ruleType,
273
+ ruleTypeValues,
274
+ ...(readBack ? { readBack } : {}),
275
+ ...(rolledBack !== undefined ? { rolledBack } : {}),
276
+ error: rolledBack === false
277
+ ? `${message}; rollback could not be verified.`
278
+ : rolledBack === true
279
+ ? `${message}; the created region was rolled back.`
280
+ : message,
281
+ };
282
+ }
283
+ async function handlePcbAddRegion(ctx, params) {
284
+ const result = pcbAddRegionInputSchema.safeParse(params);
285
+ if (!result.success)
286
+ return { success: false, error: result.error.message };
287
+ const parsed = result.data;
288
+ const ruleTypeValues = parsed.ruleType.map(pcbRegionRuleTypeValue);
289
+ try {
290
+ const before = await listRegionsForWrite(ctx);
291
+ const beforeIds = new Set(before.map((item) => item.primitiveId));
292
+ const created = await ctx.bridge.call('api.call', {
293
+ path: 'PCB_PrimitiveRegion.create',
294
+ args: [
295
+ parsed.layer,
296
+ taggedRegionPolygon(parsed.polygon),
297
+ ruleTypeValues,
298
+ parsed.name,
299
+ parsed.lineWidth,
300
+ parsed.locked,
301
+ ],
302
+ });
303
+ const returnedPrimitiveId = primitiveIdFromApiCall(created);
304
+ let after;
305
+ try {
306
+ after = await listRegionsForWrite(ctx);
307
+ }
308
+ catch (error) {
309
+ return failedRegionWrite(ctx, parsed, ruleTypeValues, `PCB_PrimitiveRegion.create returned but persisted read-back failed: ${error instanceof Error ? error.message : String(error)}`, returnedPrimitiveId);
310
+ }
311
+ const readBack = findCreatedRegion(after, beforeIds, returnedPrimitiveId);
312
+ if (!readBack) {
313
+ return failedRegionWrite(ctx, parsed, ruleTypeValues, 'PCB_PrimitiveRegion.create returned without a unique matching PCB_PrimitiveRegion.getAll read-back', returnedPrimitiveId);
314
+ }
315
+ if (!regionReadBackMatches(readBack, parsed, ruleTypeValues)) {
316
+ return failedRegionWrite(ctx, parsed, ruleTypeValues, 'PCB region read-back did not match the requested layer/rules/name/width/lock state', readBack.primitiveId, readBack);
317
+ }
318
+ return {
319
+ success: true,
320
+ primitiveId: readBack.primitiveId,
321
+ ruleType: parsed.ruleType,
322
+ ruleTypeValues,
323
+ readBack,
324
+ };
325
+ }
326
+ catch (error) {
327
+ return {
328
+ success: false,
329
+ error: error instanceof Error ? error.message : String(error),
330
+ };
331
+ }
332
+ }
85
333
  const failClosedPcbZoneTool = {
86
334
  name: 'easyeda_pcb_add_zone',
87
335
  title: 'Add PCB copper zone/pour (unavailable)',
88
- description: 'PCB copper-zone creation is unavailable because the verified EasyEDA Pro runtime requires ' +
89
- 'a complete native argument contract that this integration has not yet recovered. This tool ' +
90
- 'fails closed and does not call the bridge.',
336
+ description: 'PCB copper-zone creation is unavailable because the verified EasyEDA Pro 3.2.149 runtime ' +
337
+ 'does not provide a trustworthy create/rebuild/associated-Poured read-back contract. The ' +
338
+ 'native create signature is known, but rebuild association and persistence remain unsafe. ' +
339
+ 'This tool fails closed and does not call the bridge.',
91
340
  ...failClosedPcbWriteMetadata,
92
341
  version: '2.0.0',
93
342
  inputSchema: failClosedPcbZoneInputSchema,
@@ -96,7 +345,7 @@ const failClosedPcbZoneTool = {
96
345
  success: false,
97
346
  not_available: true,
98
347
  error: 'PCB copper-zone creation is not supported by the verified EasyEDA Pro runtime.',
99
- remediation: 'Create or edit the copper zone in EasyEDA Pro manually until the complete native zone-creation contract is live-verified.',
348
+ remediation: 'Create or edit the copper zone in EasyEDA Pro manually until a supported runtime has a live-verified deterministic create/rebuild/associated-Poured read-back contract.',
100
349
  }),
101
350
  };
102
351
  export async function applyLayoutOperations(ctx, operations) {
@@ -559,6 +808,27 @@ function registerPcbWriteTools(registry, _config) {
559
808
  'easyeda_pcb_modify_component.',
560
809
  }),
561
810
  });
811
+ registry.register({
812
+ name: 'easyeda_pcb_add_region',
813
+ title: 'Add PCB constraint region',
814
+ description: 'Create a native EasyEDA PCB Region/keepout from a typed polygon and documented rule values. ' +
815
+ 'This is not a copper pour/zone. Success requires getAll-backed read-back; unconfirmed ' +
816
+ 'optional name/lineWidth values cause rollback.',
817
+ profile: 'full',
818
+ evidence: ['official-docs', 'runtime-probe'],
819
+ risk: 'high',
820
+ confirmWrite: true,
821
+ group: 'pcb-write',
822
+ version: '1.0.0',
823
+ annotations: {
824
+ readOnlyHint: false,
825
+ destructiveHint: false,
826
+ idempotentHint: false,
827
+ },
828
+ inputSchema: pcbAddRegionInputSchema,
829
+ outputSchema: pcbAddRegionOutputSchema,
830
+ handler: async (ctx, params) => handlePcbAddRegion(ctx, params),
831
+ });
562
832
  registry.register({
563
833
  name: 'easyeda_pcb_add_track',
564
834
  title: 'Add PCB track',
@@ -669,6 +939,64 @@ function registerPcbWriteTools(registry, _config) {
669
939
  }
670
940
  },
671
941
  });
942
+ registry.register({
943
+ name: 'easyeda_pcb_rebuild_copper',
944
+ title: 'Rebuild existing PCB copper pours',
945
+ description: 'Rebuild existing PCB_PrimitivePour copper via the beta rebuildCopperRegion() API and verify each ' +
946
+ 'result through PCB_PrimitivePoured read-back. Requires confirmWrite. PCB rebuilds are not ' +
947
+ 'transaction-covered; after timeout inspect before retry. PlaneZone objects are unsupported.',
948
+ profile: 'full',
949
+ evidence: ['official-docs', 'pro-api-types', 'official-skill'],
950
+ risk: 'high',
951
+ confirmWrite: true,
952
+ sideEffect: 'design-mutation',
953
+ group: 'pcb-write',
954
+ version: '1.0.0',
955
+ annotations: {
956
+ readOnlyHint: false,
957
+ destructiveHint: false,
958
+ idempotentHint: false,
959
+ },
960
+ inputSchema: pcbRebuildCopperInputSchema,
961
+ outputSchema: pcbRebuildCopperOutputSchema,
962
+ handler: async (ctx, params) => {
963
+ const parsed = pcbRebuildCopperInputSchema.safeParse(params);
964
+ if (!parsed.success) {
965
+ return {
966
+ success: false,
967
+ matchedCount: 0,
968
+ attemptedCount: 0,
969
+ rebuiltCount: 0,
970
+ noCopperCount: 0,
971
+ transactionCovered: false,
972
+ planeZonesSupported: false,
973
+ results: [],
974
+ error: parsed.error.message,
975
+ };
976
+ }
977
+ const { confirmWrite: _confirmWrite, ...bridgeParams } = parsed.data;
978
+ try {
979
+ return await ctx.bridge.call('pcb.rebuildCopper', bridgeParams);
980
+ }
981
+ catch (error) {
982
+ const message = error instanceof Error ? error.message : String(error);
983
+ const timedOut = /timed?\s*out|timeout/i.test(message);
984
+ return {
985
+ success: false,
986
+ matchedCount: 0,
987
+ attemptedCount: 0,
988
+ rebuiltCount: 0,
989
+ noCopperCount: 0,
990
+ transactionCovered: false,
991
+ planeZonesSupported: false,
992
+ results: [],
993
+ error: timedOut
994
+ ? `${message}. PCB copper rebuilds are not transaction-covered; the write outcome is unknown. Inspect the active PCB before retrying.`
995
+ : message,
996
+ };
997
+ }
998
+ },
999
+ });
672
1000
  registry.register(failClosedPcbZoneTool);
673
1001
  registry.register({
674
1002
  name: 'easyeda_pcb_add_text',