@parity-app/cli 0.4.0 → 0.5.1

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,35 +1,38 @@
1
+ import { c as unknownReference, n as nonBlankString, r as CodedError, s as readErrorDetails, t as jsonText } from "./schema-BpGSCLEu.mjs";
1
2
  import { join } from "node:path";
2
3
  import { randomUUID } from "node:crypto";
3
4
  import { readFileSync, readdirSync, realpathSync, rmSync } from "node:fs";
4
5
  import { homedir } from "node:os";
6
+ import { z } from "zod";
7
+ // ../agent/src/routing.ts
8
+ /**
9
+ * Which running copy of Parity an agent's call goes to (PAR-514). Several copies can run at once, each with its own
10
+ * projects open: the person's app, a dev session, `pnpm perf`. Agents care about the project, not the copy, so every
11
+ * call is sent to the copy that has its project open. The CLI decides, once, with these rules, and sends that copy the
12
+ * exact tab it chose; the app matches a tab reference with the same `matchTab`. Nothing here touches files or the
13
+ * network, so the app's renderer can use it too.
14
+ */
15
+ /** How to reach one running copy: what it writes to its registry entry. A number an entry lacks reads as 0. */
16
+ const copyEntry = z.object({
17
+ version: z.number().catch(0),
18
+ url: z.string(),
19
+ token: z.string(),
20
+ /** The app's main process, which names the copy and its entry. A service the app restarts is the same copy. */
21
+ app: z.number(),
22
+ /** When the copy started, in milliseconds since the epoch. A project open in two copies goes to the older one. */
23
+ startedAt: z.number().catch(0)
24
+ });
25
+ /** An entry's text. The file from before the registry names the app's main process `pid`. */
26
+ const entryText = jsonText(copyEntry.extend({
27
+ app: z.number().optional().catch(void 0),
28
+ pid: z.number().optional().catch(void 0)
29
+ }).transform(({ app, pid, ...entry }) => ({
30
+ ...entry,
31
+ app: app ?? pid ?? 0
32
+ })));
5
33
  /** Reads a registry entry's text, or nothing when it isn't one. A file from before the registry stands for its copy. */
6
34
  function readCopyEntry(text) {
7
- if (text === void 0) return void 0;
8
- let value;
9
- try {
10
- value = JSON.parse(text);
11
- } catch {
12
- return;
13
- }
14
- if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
15
- const fields = new Map(Object.entries(value));
16
- const [version, url, token, app, pid, startedAt] = [
17
- "version",
18
- "url",
19
- "token",
20
- "app",
21
- "pid",
22
- "startedAt"
23
- ].map((key) => fields.get(key));
24
- if (typeof url !== "string" || typeof token !== "string") return void 0;
25
- const number = (field) => typeof field === "number" ? field : void 0;
26
- return {
27
- version: number(version) ?? 0,
28
- url,
29
- token,
30
- app: number(app) ?? number(pid) ?? 0,
31
- startedAt: number(startedAt) ?? 0
32
- };
35
+ return entryText.safeParse(text).data;
33
36
  }
34
37
  /**
35
38
  * A project folder as routing compares it: forward slashes, no trailing slash, and on Windows (a drive letter or a
@@ -160,15 +163,21 @@ function byAge(a, b) {
160
163
  return a.entry.startedAt - b.entry.startedAt || a.entry.app - b.entry.app;
161
164
  }
162
165
  // ../agent/src/registry.ts
166
+ /** The settings that place the registry. One that's blank counts as unset. */
167
+ const registrySettings = z.object({
168
+ PARITY_AGENT_FILE: nonBlankString.optional().catch(void 0),
169
+ PARITY_AGENT_DIR: nonBlankString.optional().catch(void 0)
170
+ });
163
171
  /** The registry these settings name: `PARITY_AGENT_FILE`, else `PARITY_AGENT_DIR`, else `~/.parity/agents`. */
164
172
  function agentRegistry(env, home) {
165
- if (env.PARITY_AGENT_FILE) return {
173
+ const { PARITY_AGENT_FILE: file, PARITY_AGENT_DIR: folder } = registrySettings.parse(env);
174
+ if (file) return {
166
175
  kind: "file",
167
- path: env.PARITY_AGENT_FILE
176
+ path: file
168
177
  };
169
178
  return {
170
179
  kind: "folder",
171
- path: env.PARITY_AGENT_DIR || join(home, ".parity", "agents")
180
+ path: folder ?? join(home, ".parity", "agents")
172
181
  };
173
182
  }
174
183
  /** The file before the registry, where an app from before it says how to reach it. */
@@ -221,105 +230,10 @@ function readText(path) {
221
230
  return;
222
231
  }
223
232
  }
224
- // ../core/src/errors.ts
225
- /**
226
- * Why an agent's call failed, as a stable code it can act on without reading the message. The CLI and MCP publish
227
- * these; the app throws them where it knows the cause.
228
- */
229
- const errorCodes = [
230
- "invalid_input",
231
- "not_running",
232
- "not_answering",
233
- "unknown_tab",
234
- "tab_not_ready",
235
- "unknown_design",
236
- "unknown_node",
237
- "ambiguous_reference",
238
- "unknown_state",
239
- "unknown_mode",
240
- "unknown_viewport",
241
- "unknown_component",
242
- "unknown_icon_set",
243
- "unknown_edit_id",
244
- "conflict",
245
- "execution_failed",
246
- "app_error"
247
- ];
248
- /** An error with a stable code, the field it's about and what to use instead. */
249
- var CodedError = class extends Error {
250
- details;
251
- constructor(message, details) {
252
- super(message);
253
- this.name = "CodedError";
254
- this.details = {
255
- ...details,
256
- retryable: details.retryable ?? false
257
- };
258
- }
259
- };
260
- /** An unknown reference, naming the valid ones closest to what was asked for, up to `alternativeLimit`. */
261
- function unknownReference(code, message, reference, candidates, field) {
262
- return new CodedError(message, {
263
- code,
264
- ...field === void 0 ? {} : { field },
265
- alternatives: closest(reference, candidates)
266
- });
267
- }
268
- /**
269
- * Candidates sharing text with the reference first, ignoring case, then the rest by how few single-character edits
270
- * away they are, so a typo's intended id makes the cut; ties keep their own order. All of them when they fit.
271
- */
272
- function closest(reference, candidates, limit = 20) {
273
- const unique = [...new Set(candidates)];
274
- if (unique.length <= limit) return unique;
275
- const wanted = reference.toLowerCase();
276
- const ranked = unique.map((candidate, index) => {
277
- const lower = candidate.toLowerCase();
278
- const near = wanted.length > 0 && (lower.includes(wanted) || wanted.includes(lower));
279
- return {
280
- candidate,
281
- index,
282
- near: near ? 0 : 1,
283
- distance: near ? 0 : editDistance(wanted, lower)
284
- };
285
- });
286
- ranked.sort((a, b) => a.near - b.near || a.distance - b.distance || a.index - b.index);
287
- return ranked.slice(0, limit).map(({ candidate }) => candidate);
288
- }
289
- /** How many single-character insertions, deletions or substitutions turn one string into the other. */
290
- function editDistance(a, b) {
291
- let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
292
- for (let i = 1; i <= a.length; i++) {
293
- const row = [i];
294
- for (let j = 1; j <= b.length; j++) row[j] = Math.min(previous[j] + 1, row[j - 1] + 1, previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
295
- previous = row;
296
- }
297
- return previous[b.length];
298
- }
299
- /** A thrown value's details, when it carries them. */
300
- function errorDetails(error) {
301
- return error instanceof CodedError ? error.details : void 0;
302
- }
303
- /** Details that came from another process, checked before they're trusted. Anything malformed reads as none. */
304
- function readErrorDetails(value) {
305
- if (!value || typeof value !== "object") return void 0;
306
- const code = "code" in value ? value.code : void 0;
307
- const known = errorCodes.find((candidate) => candidate === code);
308
- if (!known) return void 0;
309
- const retryable = "retryable" in value && value.retryable === true;
310
- const field = "field" in value && typeof value.field === "string" && value.field ? value.field : void 0;
311
- const alternatives = "alternatives" in value && Array.isArray(value.alternatives) ? value.alternatives.filter((item) => typeof item === "string").slice(0, 20) : void 0;
312
- return {
313
- code: known,
314
- retryable,
315
- ...field === void 0 ? {} : { field },
316
- ...alternatives === void 0 ? {} : { alternatives }
317
- };
318
- }
319
233
  // src/caller.ts
320
234
  /** Declared caller names are displayed verbatim apart from surrounding whitespace. */
321
235
  function declaredName(value) {
322
- return typeof value === "string" ? value.trim() || void 0 : void 0;
236
+ return nonBlankString.safeParse(value).data;
323
237
  }
324
238
  /** Reserve the session suffix inside the desktop boundary's 80-character caller-id limit. */
325
239
  function sessionCallerId(name, session) {
@@ -405,10 +319,13 @@ async function runningCopies() {
405
319
  report
406
320
  }] : [];
407
321
  }))).flat();
408
- if (entries.length > 0 && copies.length === 0) throw new CodedError(`Parity isn't answering at ${entries.map((entry) => entry.url).join(", ")}. Is the app still running?`, {
409
- code: "not_answering",
410
- retryable: true
411
- });
322
+ if (entries.length > 0 && copies.length === 0) {
323
+ const where = entries.map((entry) => entry.url).join(", ");
324
+ throw new CodedError(`Parity isn't answering at ${where}. Is the app still running?`, {
325
+ code: "not_answering",
326
+ retryable: true
327
+ });
328
+ }
412
329
  if (copies.length === 0) throw new CodedError(`Parity isn't running (nothing in ${registry.path}). Start the app, then try again.`, {
413
330
  code: "not_running",
414
331
  retryable: true
@@ -468,10 +385,45 @@ function describeExec(result) {
468
385
  if (result.output.length > 0) lines.push("", "Output:", ...result.output);
469
386
  const created = Object.entries(result.created);
470
387
  if (created.length > 0) lines.push("", "Created:", ...created.map(([name, id]) => ` ${name} = ${id}`));
471
- if (result.warnings.length > 0) lines.push("", "Warnings (fix these next):", ...result.warnings.map((warning) => ` ${warning}`));
388
+ lines.push(...describeWarnings(result));
389
+ if (result.discarded?.length) {
390
+ lines.push("", "Discarded by Replace (reapply what you still want to the new node):");
391
+ for (const { design, replaced, replacement, nodes } of result.discarded) {
392
+ lines.push(` ${design}/${replaced}, now ${replacement}:`);
393
+ for (const node of nodes) {
394
+ const where = [
395
+ node.viewports && `viewports ${node.viewports.join(", ")}`,
396
+ node.states && `states ${node.states.join(", ")}`,
397
+ node.variants && `variants ${node.variants.join(", ")}`
398
+ ].filter(Boolean);
399
+ lines.push(` ${node.name ? `${node.name} (${node.id})` : node.id}: ${where.join("; ")}`);
400
+ }
401
+ }
402
+ }
472
403
  if (result.screenshots.length > 0) lines.push("", "Screenshots:", ...result.screenshots.map((shot) => ` ${shot.design} / ${shot.state} / ${shot.mode}: ${shot.path}`));
473
404
  return lines.join("\n");
474
405
  }
406
+ const warningHeadings = {
407
+ error: "Render failures (these frames weren't checked):",
408
+ warning: "Warnings (fix these next):",
409
+ suggestion: "Suggestions:"
410
+ };
411
+ /**
412
+ * The warnings under a heading for each severity, so a failed render doesn't read like a naming tip. The lines are
413
+ * the app's, one per group in the groups' order; an app older than PAR-460 sends no groups, so they're all warnings.
414
+ */
415
+ function describeWarnings(result) {
416
+ const lines = [];
417
+ for (const severity of [
418
+ "error",
419
+ "warning",
420
+ "suggestion"
421
+ ]) {
422
+ const matching = result.warnings.filter((_, index) => (result.warningGroups?.[index]?.severity ?? "warning") === severity);
423
+ if (matching.length > 0) lines.push("", warningHeadings[severity], ...matching.map((warning) => ` ${warning}`));
424
+ }
425
+ return lines;
426
+ }
475
427
  /** Where a tab's edits stand: the revision, what's applied and undoable, what's on disk, and what failed to write. */
476
428
  function describeStatus(result) {
477
429
  const { tab, revision, unsaved, undo, redo, save, errors } = result;
@@ -563,6 +515,7 @@ function describeAccessibility(result) {
563
515
  name: finding.name ?? "The design",
564
516
  nodes: finding.node ? [finding.node] : [],
565
517
  detail: finding.detail,
518
+ ...fixLine(finding) ? { fix: fixLine(finding) } : {},
566
519
  frames: []
567
520
  };
568
521
  layer.frames.push({
@@ -586,6 +539,7 @@ function describeAccessibility(result) {
586
539
  const where = layer.frames.length === frames && frames > 1 ? "every frame" : frameList(layer.frames);
587
540
  const ids = layer.nodes.length > 0 ? ` (${layer.nodes.join(", ")})` : "";
588
541
  lines.push(` ${layer.name}${ids}, in ${where}: ${layer.detail}`);
542
+ if (layer.fix) lines.push(` ${layer.fix}`);
589
543
  }
590
544
  lines.push(` How to fix it: ${helpUrl}`);
591
545
  }
@@ -593,12 +547,19 @@ function describeAccessibility(result) {
593
547
  lines.push("", "Automated checks find some issues, not all. Try the design with a keyboard and a screen reader too.");
594
548
  return lines.join("\n");
595
549
  }
550
+ /** A contrast failure's fix in a sentence, naming the style to set so it can be passed to execute. */
551
+ function fixLine(finding) {
552
+ const fix = finding.contrast?.fix;
553
+ if (fix === "none") return "Fix: none of the project's color tokens pass on this background; change the background.";
554
+ return fix && `Fix: set style.foreground to "${fix.token}", which reaches ${fix.ratio}:1 in every frame checked.`;
555
+ }
596
556
  /** A readable account of the layout problems in a design's frames, each once with the frames it's in. */
597
557
  function describeProblems(result) {
598
558
  const { design, problems, frames } = result;
599
559
  const checked = `${design.name} (${design.id}), ${frames === 1 ? "1 frame" : `${frames} frames`}`;
600
560
  const grouped = /* @__PURE__ */ new Map();
601
- for (const { problem, node, state, mode } of problems) {
561
+ for (const { problem, node: id, name, state, mode } of problems) {
562
+ const node = id && name ? `${name} (${id})` : id;
602
563
  const key = `${node ?? ""}\n${problem}`;
603
564
  const entry = grouped.get(key) ?? {
604
565
  problem,
@@ -671,19 +632,47 @@ function describeComponents({ components, iconLibraries = [], leftOut = [], draw
671
632
  }
672
633
  return lines.join("\n").trimEnd();
673
634
  }
674
- /** A component's props, with each variant's options and default. */
635
+ /** A component's props, with each variant's options and default, then what it needs around it and holds. */
675
636
  function describeProps(result) {
676
- if (!result.known) return `${result.component}'s props haven't been read from its source yet. Try again in a moment.`;
677
- const imported = result.import && result.import !== result.path ? `, imported from "${result.import}"` : "";
678
- const lines = [`${result.component}, from ${result.path}${imported}:`];
679
- for (const prop of result.props) {
680
- const options = prop.options ? `: ${prop.options.map((option) => JSON.stringify(option)).join(", ")}` : "";
681
- const fallback = prop.default === void 0 ? "" : `, default ${JSON.stringify(prop.default)}`;
682
- lines.push(` ${prop.name} (${prop.kind}${prop.required ? ", required" : ""})${options}${fallback}`);
637
+ const lines = [];
638
+ if (!result.known) lines.push(`${result.component}'s props haven't been read from its source yet. Try again in a moment.`);
639
+ else {
640
+ const imported = result.import && result.import !== result.path ? `, imported from "${result.import}"` : "";
641
+ lines.push(`${result.component}, from ${result.path}${imported}:`);
642
+ for (const prop of result.props) {
643
+ const options = prop.options ? `: ${prop.options.map((option) => JSON.stringify(option)).join(", ")}` : "";
644
+ const fallback = prop.default === void 0 ? "" : `, default ${JSON.stringify(prop.default)}`;
645
+ lines.push(` ${prop.name} (${prop.kind}${prop.required ? ", required" : ""})${options}${fallback}`);
646
+ }
647
+ if (result.props.length === 0) lines.push(" No props of its own. --all lists the attributes it passes on.");
683
648
  }
684
- if (result.props.length === 0) lines.push(" No props of its own. --all lists the attributes it passes on.");
649
+ if (result.composition) lines.push("", ...describeComposition(result.composition));
685
650
  return lines.join("\n");
686
651
  }
652
+ const roles = {
653
+ provider: "a provider, which draws nothing and gives the parts inside it their context",
654
+ wrapper: "a wrapper, which other parts go inside",
655
+ component: "a component"
656
+ };
657
+ const exampleStatus = {
658
+ drawn: "drawn on the canvas",
659
+ failing: "failing on the canvas",
660
+ untried: "not drawn yet"
661
+ };
662
+ /** What a component needs around it, what it holds, its slots, and its example as JSON for `Insert`. */
663
+ function describeComposition({ role, parents, holds, slots, example }) {
664
+ const lines = [`It's ${roles[role]}.`];
665
+ if (parents.status === "listed") {
666
+ const chain = parents.chain.map((parent) => parent.optional ? `${parent.component} (optional)` : parent.component);
667
+ lines.push(`Goes inside, outermost first: ${chain.join(" > ")}${parents.from ? ` (from its ${parents.from})` : ""}`);
668
+ } else if (parents.status === "none") lines.push("Needs nothing around it.");
669
+ else lines.push(`What it needs around it is unknown: ${parents.reason}`);
670
+ if (holds.length > 0) lines.push(`Holds: ${holds.join(", ")}`);
671
+ if (slots.length > 0) lines.push(`Slots: ${slots.map((slot) => `${slot.key} (${slot.kind})`).join(", ")}`);
672
+ const reason = example.reason ? `: ${example.reason}` : "";
673
+ lines.push(`Example for Insert, ${exampleStatus[example.status]}${reason}:`, ` ${JSON.stringify(example.node)}`);
674
+ return lines;
675
+ }
687
676
  /** Frames as state and mode ids, naming a mode once when they share it: "default, error in desktop-light". */
688
677
  function frameList(frames) {
689
678
  const modes = [...new Set(frames.map((frame) => frame.mode))];
@@ -721,4 +710,4 @@ function scriptPath(url) {
721
710
  return url || "an unknown script";
722
711
  }
723
712
  }
724
- export { errorDetails as _, describeExec as a, describeProps as c, registry as d, setCaller as f, errorCodes as g, closest as h, describeConsole as i, describeStatus as l, CodedError as m, describeAccessibility as n, describeModes as o, mcpCallerName as p, describeComponents as r, describeProblems as s, call as t, describeTokens as u, readErrorDetails as v, unknownReference as y };
713
+ export { describeExec as a, describeProps as c, registry as d, setCaller as f, describeConsole as i, describeStatus as l, describeAccessibility as n, describeModes as o, mcpCallerName as p, describeComponents as r, describeProblems as s, call as t, describeTokens as u };
package/dist/index.d.mts CHANGED
@@ -1,3 +1,4 @@
1
+ import { z } from "zod";
1
2
  /**
2
3
  * Where running copies of Parity say how to reach them (PAR-514): one entry per copy, named by its main process, in
3
4
  * `~/.parity/agents`. Each copy writes and removes only its own. The app and the CLI both go through here, so the
@@ -63,15 +64,42 @@ interface Screenshot {
63
64
  state: string;
64
65
  mode: string;
65
66
  }
67
+ /** What one `Replace` threw away: the patches keyed to the old node and what was inside it (PAR-455). */
68
+ interface Discarded {
69
+ design: string;
70
+ replaced: string;
71
+ replacement: string;
72
+ nodes: {
73
+ id: string;
74
+ name?: string;
75
+ viewports?: string[];
76
+ states?: string[];
77
+ variants?: string[];
78
+ }[];
79
+ }
80
+ /**
81
+ * Findings with the same category and message, once, with every place they were found (PAR-460). The app's
82
+ * `warnings` are these as lines, in the same order.
83
+ */
84
+ interface WarningGroup {
85
+ category: string;
86
+ severity: "error" | "warning" | "suggestion";
87
+ message: string;
88
+ count: number;
89
+ at: string[];
90
+ }
66
91
  /** The canonical result. Structured output is this without the screenshots' image data. */
67
92
  interface ExecResponse {
68
93
  ok: boolean;
69
94
  applied: boolean;
70
95
  output: string[];
71
96
  warnings: string[];
97
+ warningGroups?: WarningGroup[];
72
98
  /** Set when the call failed, with the editId to retry a snippet by. */
73
99
  error?: OperationError;
74
100
  created: Record<string, string>;
101
+ /** Set when a Replace threw away responsive, state or variant patches. */
102
+ discarded?: Discarded[];
75
103
  screenshots: Screenshot[];
76
104
  }
77
105
  /**
@@ -225,6 +253,13 @@ export interface AccessibilityFinding {
225
253
  node?: string;
226
254
  name?: string;
227
255
  detail: string;
256
+ /** For a contrast failure: the token to color the layer's text with, or `"none"` when none of the project's pass. */
257
+ contrast?: {
258
+ fix?: {
259
+ token: string;
260
+ ratio: number;
261
+ } | "none";
262
+ };
228
263
  }
229
264
  export interface AccessibilityResponse {
230
265
  tab: {
@@ -253,15 +288,41 @@ export interface ProblemsResponse {
253
288
  id: string;
254
289
  name: string;
255
290
  };
256
- problems: {
257
- state: string;
258
- mode: string;
259
- problem: string;
260
- node?: string;
261
- }[];
291
+ problems: Problem[];
262
292
  frames: number;
263
293
  warnings: string[];
264
294
  }
295
+ /** A box in a frame, in CSS pixels from its top left corner. */
296
+ export interface FrameBox {
297
+ x: number;
298
+ y: number;
299
+ width: number;
300
+ height: number;
301
+ }
302
+ /**
303
+ * One problem in one frame. Layout problems (`frame-overflow`, `clipped`) name the node, what it overflows (the node
304
+ * that clips it, or the frame, which has none), both boxes, and how far past each edge it reaches.
305
+ */
306
+ export interface Problem {
307
+ state: string;
308
+ mode: string;
309
+ kind?: "render-error" | "unknown-component" | "content-wider" | "frame-overflow" | "clipped";
310
+ problem: string;
311
+ node?: string;
312
+ name?: string;
313
+ box?: FrameBox;
314
+ container?: {
315
+ node?: string;
316
+ name?: string;
317
+ box: FrameBox;
318
+ };
319
+ overflow?: {
320
+ left?: number;
321
+ right?: number;
322
+ top?: number;
323
+ bottom?: number;
324
+ };
325
+ }
265
326
  /** A readable account of the layout problems in a design's frames, each once with the frames it's in. */
266
327
  export declare function describeProblems(result: ProblemsResponse): string;
267
328
  export interface TokenInfo {
@@ -325,8 +386,38 @@ export interface PropsResponse {
325
386
  default?: string | number | boolean | null;
326
387
  required?: boolean;
327
388
  }[];
389
+ /** What it needs around it and holds inside it, read from its library's metadata (PAR-454). */
390
+ composition?: Composition;
391
+ }
392
+ /** A component's composition, as `@parity-app/editor`'s `componentComposition` reads it. */
393
+ export interface Composition {
394
+ role: "provider" | "wrapper" | "component";
395
+ parents: {
396
+ status: "listed";
397
+ chain: {
398
+ component: string;
399
+ optional?: true;
400
+ }[];
401
+ from?: string;
402
+ } | {
403
+ status: "none";
404
+ } | {
405
+ status: "unknown";
406
+ reason: string;
407
+ };
408
+ holds: string[];
409
+ slots: {
410
+ name: string;
411
+ key: string;
412
+ kind: "children" | "element" | "render";
413
+ }[];
414
+ example: {
415
+ node: unknown;
416
+ status: "drawn" | "failing" | "untried";
417
+ reason?: string;
418
+ };
328
419
  }
329
- /** A component's props, with each variant's options and default. */
420
+ /** A component's props, with each variant's options and default, then what it needs around it and holds. */
330
421
  export declare function describeProps(result: PropsResponse): string;
331
422
  export interface ConsoleEntry {
332
423
  level: "info" | "warning" | "error" | "debug";
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- export { t as call, n as describeAccessibility, r as describeComponents, i as describeConsole, a as describeExec, o as describeModes, s as describeProblems, c as describeProps, l as describeStatus, u as describeTokens, d as registry, f as setCaller } from "./client-Cf66-n3R.mjs";
1
+ export { t as call, n as describeAccessibility, r as describeComponents, i as describeConsole, a as describeExec, o as describeModes, s as describeProblems, c as describeProps, l as describeStatus, u as describeTokens, d as registry, f as setCaller } from "./client-CoW-KcnT.mjs";
@@ -0,0 +1,113 @@
1
+ import { z } from "zod";
2
+ // ../core/src/errors.ts
3
+ /**
4
+ * Why an agent's call failed, as a stable code it can act on without reading the message. The CLI and MCP publish
5
+ * these; the app throws them where it knows the cause.
6
+ */
7
+ const errorCodes = [
8
+ "invalid_input",
9
+ "not_running",
10
+ "not_answering",
11
+ "unknown_tab",
12
+ "tab_not_ready",
13
+ "unknown_design",
14
+ "unknown_node",
15
+ "ambiguous_reference",
16
+ "unknown_state",
17
+ "unknown_mode",
18
+ "unknown_viewport",
19
+ "unknown_component",
20
+ "unknown_icon_set",
21
+ "unknown_edit_id",
22
+ "conflict",
23
+ "execution_failed",
24
+ "app_error"
25
+ ];
26
+ /** Details as another process sends them. Each field that's malformed falls back on its own; only the code must hold. */
27
+ const errorDetailsSchema = z.object({
28
+ code: z.enum(errorCodes),
29
+ /** Whether sending the same request again, unchanged, can succeed. */
30
+ retryable: z.boolean().catch(false),
31
+ /** The request field at fault, as a path: `design`, `state`, `edits/0/find`. */
32
+ field: z.string().min(1).optional().catch(void 0),
33
+ /** Valid references to use instead of an unknown or ambiguous one, at most `alternativeLimit` of them. */
34
+ alternatives: z.array(z.unknown()).transform((items) => items.filter((item) => typeof item === "string").slice(0, 20)).optional().catch(void 0)
35
+ });
36
+ /** An error with a stable code, the field it's about and what to use instead. */
37
+ var CodedError = class extends Error {
38
+ details;
39
+ constructor(message, details) {
40
+ super(message);
41
+ this.name = "CodedError";
42
+ this.details = {
43
+ ...details,
44
+ retryable: details.retryable ?? false
45
+ };
46
+ }
47
+ };
48
+ /** An unknown reference, naming the valid ones closest to what was asked for, up to `alternativeLimit`. */
49
+ function unknownReference(code, message, reference, candidates, field) {
50
+ return new CodedError(message, {
51
+ code,
52
+ ...field === void 0 ? {} : { field },
53
+ alternatives: closest(reference, candidates)
54
+ });
55
+ }
56
+ /**
57
+ * Candidates sharing text with the reference first, ignoring case, then the rest by how few single-character edits
58
+ * away they are, so a typo's intended id makes the cut; ties keep their own order. All of them when they fit.
59
+ */
60
+ function closest(reference, candidates, limit = 20) {
61
+ const unique = [...new Set(candidates)];
62
+ if (unique.length <= limit) return unique;
63
+ const wanted = reference.toLowerCase();
64
+ const ranked = unique.map((candidate, index) => {
65
+ const lower = candidate.toLowerCase();
66
+ const near = wanted.length > 0 && (lower.includes(wanted) || wanted.includes(lower));
67
+ return {
68
+ candidate,
69
+ index,
70
+ near: near ? 0 : 1,
71
+ distance: near ? 0 : editDistance(wanted, lower)
72
+ };
73
+ });
74
+ ranked.sort((a, b) => a.near - b.near || a.distance - b.distance || a.index - b.index);
75
+ return ranked.slice(0, limit).map(({ candidate }) => candidate);
76
+ }
77
+ /** How many single-character insertions, deletions or substitutions turn one string into the other. */
78
+ function editDistance(a, b) {
79
+ let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
80
+ for (let i = 1; i <= a.length; i++) {
81
+ const row = [i];
82
+ for (let j = 1; j <= b.length; j++) row[j] = Math.min(previous[j] + 1, row[j - 1] + 1, previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
83
+ previous = row;
84
+ }
85
+ return previous[b.length];
86
+ }
87
+ /** A thrown value's details, when it carries them. */
88
+ function errorDetails(error) {
89
+ return error instanceof CodedError ? error.details : void 0;
90
+ }
91
+ /** Details that came from another process, checked before they're trusted. Anything malformed reads as none. */
92
+ function readErrorDetails(value) {
93
+ return errorDetailsSchema.safeParse(value).data;
94
+ }
95
+ z.record(z.string(), z.unknown());
96
+ /** Text with something besides whitespace, trimmed. */
97
+ const nonBlankString = z.string().trim().min(1, "can't be blank");
98
+ z.number();
99
+ /** JSON text holding what `schema` checks. Text that isn't JSON fails as `expected JSON`. */
100
+ function jsonText(schema) {
101
+ return z.string().transform((text, context) => {
102
+ try {
103
+ return JSON.parse(text);
104
+ } catch {
105
+ context.addIssue({
106
+ code: "custom",
107
+ message: "expected JSON"
108
+ });
109
+ return z.NEVER;
110
+ }
111
+ }).pipe(schema);
112
+ }
113
+ export { errorCodes as a, unknownReference as c, closest as i, nonBlankString as n, errorDetails as o, CodedError as r, readErrorDetails as s, jsonText as t };
@@ -1,4 +1,4 @@
1
- import { p as parseLibraryManifest, u as libraryManifestExtension } from "./src-CYEKdXm_.mjs";
1
+ import { p as parseLibraryManifest, u as libraryManifestExtension } from "./src-C4faIpux.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import { readFile, readdir } from "node:fs/promises";
4
4
  import { dirname, join } from "node:path";