@xera-ai/core 0.4.4 → 0.8.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.
Files changed (157) hide show
  1. package/dist/bin/internal.js +895 -537
  2. package/dist/src/index.js +109 -4
  3. package/package.json +6 -4
  4. package/src/artifact/status.ts +3 -0
  5. package/src/bin-internal/auth-setup.ts +116 -0
  6. package/src/bin-internal/exec.ts +42 -9
  7. package/src/bin-internal/graph-record.ts +3 -0
  8. package/src/bin-internal/index.ts +2 -0
  9. package/src/bin-internal/normalize.ts +13 -1
  10. package/src/bin-internal/report.ts +94 -2
  11. package/src/bin-internal/verify-prompts.ts +2 -1
  12. package/src/classifier/aggregate.ts +3 -0
  13. package/src/classifier/auth-expired.ts +44 -0
  14. package/src/classifier/contract-drift.ts +111 -0
  15. package/src/classifier/rate-limited.ts +25 -0
  16. package/src/config/schema.ts +51 -8
  17. package/src/graph/schema.ts +3 -0
  18. package/src/graph/types.ts +4 -1
  19. package/src/index.ts +2 -0
  20. package/src/scrub/index.ts +1 -0
  21. package/src/scrub/rules.ts +69 -0
  22. package/dist/adapter/types.d.ts +0 -70
  23. package/dist/adapter/types.d.ts.map +0 -1
  24. package/dist/artifact/hash.d.ts +0 -4
  25. package/dist/artifact/hash.d.ts.map +0 -1
  26. package/dist/artifact/meta.d.ts +0 -20
  27. package/dist/artifact/meta.d.ts.map +0 -1
  28. package/dist/artifact/paths.d.ts +0 -25
  29. package/dist/artifact/paths.d.ts.map +0 -1
  30. package/dist/artifact/status.d.ts +0 -75
  31. package/dist/artifact/status.d.ts.map +0 -1
  32. package/dist/auth/encrypt.d.ts +0 -4
  33. package/dist/auth/encrypt.d.ts.map +0 -1
  34. package/dist/auth/key.d.ts +0 -3
  35. package/dist/auth/key.d.ts.map +0 -1
  36. package/dist/auth/refresh.d.ts +0 -9
  37. package/dist/auth/refresh.d.ts.map +0 -1
  38. package/dist/auth/state.d.ts +0 -15
  39. package/dist/auth/state.d.ts.map +0 -1
  40. package/dist/bin-internal/disputes.d.ts +0 -2
  41. package/dist/bin-internal/disputes.d.ts.map +0 -1
  42. package/dist/bin-internal/doctor.d.ts +0 -5
  43. package/dist/bin-internal/doctor.d.ts.map +0 -1
  44. package/dist/bin-internal/eval-deterministic.d.ts +0 -5
  45. package/dist/bin-internal/eval-deterministic.d.ts.map +0 -1
  46. package/dist/bin-internal/eval-prepare.d.ts +0 -7
  47. package/dist/bin-internal/eval-prepare.d.ts.map +0 -1
  48. package/dist/bin-internal/eval-report.d.ts +0 -5
  49. package/dist/bin-internal/eval-report.d.ts.map +0 -1
  50. package/dist/bin-internal/exec.d.ts +0 -2
  51. package/dist/bin-internal/exec.d.ts.map +0 -1
  52. package/dist/bin-internal/fetch.d.ts +0 -5
  53. package/dist/bin-internal/fetch.d.ts.map +0 -1
  54. package/dist/bin-internal/graph-backfill.d.ts +0 -2
  55. package/dist/bin-internal/graph-backfill.d.ts.map +0 -1
  56. package/dist/bin-internal/graph-enrich.d.ts +0 -2
  57. package/dist/bin-internal/graph-enrich.d.ts.map +0 -1
  58. package/dist/bin-internal/graph-query.d.ts +0 -2
  59. package/dist/bin-internal/graph-query.d.ts.map +0 -1
  60. package/dist/bin-internal/graph-record-script.d.ts +0 -2
  61. package/dist/bin-internal/graph-record-script.d.ts.map +0 -1
  62. package/dist/bin-internal/graph-record.d.ts +0 -3
  63. package/dist/bin-internal/graph-record.d.ts.map +0 -1
  64. package/dist/bin-internal/graph-render.d.ts +0 -2
  65. package/dist/bin-internal/graph-render.d.ts.map +0 -1
  66. package/dist/bin-internal/graph-snapshot.d.ts +0 -2
  67. package/dist/bin-internal/graph-snapshot.d.ts.map +0 -1
  68. package/dist/bin-internal/heal-prepare.d.ts +0 -19
  69. package/dist/bin-internal/heal-prepare.d.ts.map +0 -1
  70. package/dist/bin-internal/impact-prepare.d.ts +0 -2
  71. package/dist/bin-internal/impact-prepare.d.ts.map +0 -1
  72. package/dist/bin-internal/index.d.ts +0 -2
  73. package/dist/bin-internal/index.d.ts.map +0 -1
  74. package/dist/bin-internal/lint.d.ts +0 -2
  75. package/dist/bin-internal/lint.d.ts.map +0 -1
  76. package/dist/bin-internal/normalize.d.ts +0 -2
  77. package/dist/bin-internal/normalize.d.ts.map +0 -1
  78. package/dist/bin-internal/post.d.ts +0 -2
  79. package/dist/bin-internal/post.d.ts.map +0 -1
  80. package/dist/bin-internal/promote.d.ts +0 -2
  81. package/dist/bin-internal/promote.d.ts.map +0 -1
  82. package/dist/bin-internal/report.d.ts +0 -2
  83. package/dist/bin-internal/report.d.ts.map +0 -1
  84. package/dist/bin-internal/status-cmd.d.ts +0 -2
  85. package/dist/bin-internal/status-cmd.d.ts.map +0 -1
  86. package/dist/bin-internal/typecheck.d.ts +0 -2
  87. package/dist/bin-internal/typecheck.d.ts.map +0 -1
  88. package/dist/bin-internal/unlock.d.ts +0 -2
  89. package/dist/bin-internal/unlock.d.ts.map +0 -1
  90. package/dist/bin-internal/validate-feature.d.ts +0 -2
  91. package/dist/bin-internal/validate-feature.d.ts.map +0 -1
  92. package/dist/bin-internal/verify-prompts.d.ts +0 -7
  93. package/dist/bin-internal/verify-prompts.d.ts.map +0 -1
  94. package/dist/classifier/aggregate.d.ts +0 -3
  95. package/dist/classifier/aggregate.d.ts.map +0 -1
  96. package/dist/classifier/history.d.ts +0 -13
  97. package/dist/classifier/history.d.ts.map +0 -1
  98. package/dist/classifier/types.d.ts +0 -26
  99. package/dist/classifier/types.d.ts.map +0 -1
  100. package/dist/config/define.d.ts +0 -3
  101. package/dist/config/define.d.ts.map +0 -1
  102. package/dist/config/load.d.ts +0 -3
  103. package/dist/config/load.d.ts.map +0 -1
  104. package/dist/config/schema.d.ts +0 -72
  105. package/dist/config/schema.d.ts.map +0 -1
  106. package/dist/eval/paths.d.ts +0 -15
  107. package/dist/eval/paths.d.ts.map +0 -1
  108. package/dist/eval/run-id.d.ts +0 -6
  109. package/dist/eval/run-id.d.ts.map +0 -1
  110. package/dist/eval/types.d.ts +0 -203
  111. package/dist/eval/types.d.ts.map +0 -1
  112. package/dist/graph/classify.d.ts +0 -42
  113. package/dist/graph/classify.d.ts.map +0 -1
  114. package/dist/graph/cost.d.ts +0 -21
  115. package/dist/graph/cost.d.ts.map +0 -1
  116. package/dist/graph/enrich.d.ts +0 -10
  117. package/dist/graph/enrich.d.ts.map +0 -1
  118. package/dist/graph/impact.d.ts +0 -31
  119. package/dist/graph/impact.d.ts.map +0 -1
  120. package/dist/graph/index.d.ts +0 -17
  121. package/dist/graph/index.d.ts.map +0 -1
  122. package/dist/graph/paths.d.ts +0 -10
  123. package/dist/graph/paths.d.ts.map +0 -1
  124. package/dist/graph/render.d.ts +0 -50
  125. package/dist/graph/render.d.ts.map +0 -1
  126. package/dist/graph/schema.d.ts +0 -180
  127. package/dist/graph/schema.d.ts.map +0 -1
  128. package/dist/graph/similarity.d.ts +0 -3
  129. package/dist/graph/similarity.d.ts.map +0 -1
  130. package/dist/graph/store.d.ts +0 -14
  131. package/dist/graph/store.d.ts.map +0 -1
  132. package/dist/graph/types.d.ts +0 -152
  133. package/dist/graph/types.d.ts.map +0 -1
  134. package/dist/graph/ulid.d.ts +0 -2
  135. package/dist/graph/ulid.d.ts.map +0 -1
  136. package/dist/index.d.ts +0 -20
  137. package/dist/index.d.ts.map +0 -1
  138. package/dist/jira/client.d.ts +0 -11
  139. package/dist/jira/client.d.ts.map +0 -1
  140. package/dist/jira/fields.d.ts +0 -7
  141. package/dist/jira/fields.d.ts.map +0 -1
  142. package/dist/jira/mcp-backend.d.ts +0 -3
  143. package/dist/jira/mcp-backend.d.ts.map +0 -1
  144. package/dist/jira/rest-backend.d.ts +0 -8
  145. package/dist/jira/rest-backend.d.ts.map +0 -1
  146. package/dist/jira/retry.d.ts +0 -8
  147. package/dist/jira/retry.d.ts.map +0 -1
  148. package/dist/jira/types.d.ts +0 -29
  149. package/dist/jira/types.d.ts.map +0 -1
  150. package/dist/lock/file-lock.d.ts +0 -12
  151. package/dist/lock/file-lock.d.ts.map +0 -1
  152. package/dist/logging/ndjson-logger.d.ts +0 -11
  153. package/dist/logging/ndjson-logger.d.ts.map +0 -1
  154. package/dist/reporter/jira-comment.d.ts +0 -9
  155. package/dist/reporter/jira-comment.d.ts.map +0 -1
  156. package/dist/reporter/status-writer.d.ts +0 -14
  157. package/dist/reporter/status-writer.d.ts.map +0 -1
@@ -19,15 +19,15 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
19
19
  var __require = import.meta.require;
20
20
 
21
21
  // src/graph/paths.ts
22
- import { join } from "path";
22
+ import { join as join3 } from "path";
23
23
  function graphPaths(repoRoot) {
24
- const eventsDir = join(repoRoot, ".xera/graph/events");
24
+ const eventsDir = join3(repoRoot, ".xera/graph/events");
25
25
  return {
26
26
  eventsDir,
27
- snapshotFile: join(repoRoot, ".xera/graph/snapshot.json"),
28
- costLog: join(repoRoot, ".xera/cost-log.jsonl"),
29
- eventsMonthDir: (yyyyMm) => join(eventsDir, yyyyMm),
30
- eventFile: (ulid, skill, ticketId, yyyyMm) => join(eventsDir, yyyyMm, `${ulid}-${skill}-${ticketId}.jsonl`)
27
+ snapshotFile: join3(repoRoot, ".xera/graph/snapshot.json"),
28
+ costLog: join3(repoRoot, ".xera/cost-log.jsonl"),
29
+ eventsMonthDir: (yyyyMm) => join3(eventsDir, yyyyMm),
30
+ eventFile: (ulid, skill, ticketId, yyyyMm) => join3(eventsDir, yyyyMm, `${ulid}-${skill}-${ticketId}.jsonl`)
31
31
  };
32
32
  }
33
33
  function currentYyyyMm(now = new Date) {
@@ -41,7 +41,7 @@ var init_paths = () => {};
41
41
  var SCHEMA_VERSION = 1;
42
42
 
43
43
  // src/graph/schema.ts
44
- import { z } from "zod";
44
+ import { z as z2 } from "zod";
45
45
  function safeParseEvent(value) {
46
46
  const r = EventSchema.safeParse(value);
47
47
  if (r.success)
@@ -50,110 +50,113 @@ function safeParseEvent(value) {
50
50
  }
51
51
  var schemaV, iso, ticketFetched, ticketEnriched, scenarioGenerated, pomGenerated, pomPromoted, runCompleted, classification, runClassified, classificationDisputed, edgeDiscovered, base, EventSchema;
52
52
  var init_schema = __esm(() => {
53
- schemaV = z.literal(SCHEMA_VERSION);
54
- iso = z.string().datetime({ offset: false });
55
- ticketFetched = z.object({
56
- ticketId: z.string().regex(/^[A-Z][A-Z0-9]*-\d+$/),
57
- summary: z.string(),
58
- ac: z.array(z.string()),
59
- jiraLinks: z.array(z.object({
60
- ticketId: z.string().regex(/^[A-Z][A-Z0-9]*-\d+$/),
61
- relation: z.enum(["blocks", "duplicates", "relates", "supersedes"])
53
+ schemaV = z2.literal(SCHEMA_VERSION);
54
+ iso = z2.string().datetime({ offset: false });
55
+ ticketFetched = z2.object({
56
+ ticketId: z2.string().regex(/^[A-Z][A-Z0-9]*-\d+$/),
57
+ summary: z2.string(),
58
+ ac: z2.array(z2.string()),
59
+ jiraLinks: z2.array(z2.object({
60
+ ticketId: z2.string().regex(/^[A-Z][A-Z0-9]*-\d+$/),
61
+ relation: z2.enum(["blocks", "duplicates", "relates", "supersedes"])
62
62
  })),
63
- storyHash: z.string(),
64
- modifiesAreas: z.array(z.string().regex(/^[a-z0-9-]+$/))
63
+ storyHash: z2.string(),
64
+ modifiesAreas: z2.array(z2.string().regex(/^[a-z0-9-]+$/))
65
65
  }).passthrough();
66
- ticketEnriched = z.object({
67
- ticketId: z.string(),
66
+ ticketEnriched = z2.object({
67
+ ticketId: z2.string(),
68
68
  enrichedAt: iso,
69
- similarCount: z.number().int().nonnegative()
69
+ similarCount: z2.number().int().nonnegative()
70
70
  }).passthrough();
71
- scenarioGenerated = z.object({
72
- scenarioId: z.string(),
73
- ticketId: z.string(),
74
- name: z.string(),
75
- gherkin: z.string(),
76
- priority: z.enum(["p0", "p1", "p2"]),
77
- featureHash: z.string(),
71
+ scenarioGenerated = z2.object({
72
+ scenarioId: z2.string(),
73
+ ticketId: z2.string(),
74
+ name: z2.string(),
75
+ gherkin: z2.string(),
76
+ priority: z2.enum(["p0", "p1", "p2"]),
77
+ featureHash: z2.string(),
78
78
  generatedAt: iso
79
79
  }).passthrough();
80
- pomGenerated = z.object({
81
- pomId: z.string(),
82
- ticketId: z.string(),
83
- filePath: z.string(),
84
- route: z.string(),
85
- locators: z.array(z.string()),
86
- scope: z.enum(["local", "shared"])
80
+ pomGenerated = z2.object({
81
+ pomId: z2.string(),
82
+ ticketId: z2.string(),
83
+ filePath: z2.string(),
84
+ route: z2.string(),
85
+ locators: z2.array(z2.string()),
86
+ scope: z2.enum(["local", "shared"])
87
87
  }).passthrough();
88
- pomPromoted = z.object({
89
- pomId: z.string(),
90
- fromPath: z.string(),
91
- toPath: z.string()
88
+ pomPromoted = z2.object({
89
+ pomId: z2.string(),
90
+ fromPath: z2.string(),
91
+ toPath: z2.string()
92
92
  }).passthrough();
93
- runCompleted = z.object({
94
- scenarioId: z.string(),
95
- ticketId: z.string(),
96
- runId: z.string(),
97
- status: z.enum(["pass", "fail"]),
98
- traceId: z.string().optional(),
99
- runtime: z.number().nonnegative()
93
+ runCompleted = z2.object({
94
+ scenarioId: z2.string(),
95
+ ticketId: z2.string(),
96
+ runId: z2.string(),
97
+ status: z2.enum(["pass", "fail"]),
98
+ traceId: z2.string().optional(),
99
+ runtime: z2.number().nonnegative()
100
100
  }).passthrough();
101
- classification = z.enum([
101
+ classification = z2.enum([
102
102
  "REAL_BUG",
103
103
  "TEST_BUG",
104
104
  "SELECTOR_DRIFT",
105
105
  "FLAKY",
106
106
  "PASS",
107
- "TEST_OUTDATED"
107
+ "TEST_OUTDATED",
108
+ "CONTRACT_DRIFT",
109
+ "RATE_LIMITED",
110
+ "AUTH_EXPIRED"
108
111
  ]);
109
- runClassified = z.object({
110
- scenarioId: z.string(),
111
- runId: z.string(),
112
+ runClassified = z2.object({
113
+ scenarioId: z2.string(),
114
+ runId: z2.string(),
112
115
  classification,
113
- confidence: z.enum(["low", "medium", "high"])
116
+ confidence: z2.enum(["low", "medium", "high"])
114
117
  }).passthrough();
115
- classificationDisputed = z.object({
116
- runId: z.string(),
117
- scenarioId: z.string(),
118
+ classificationDisputed = z2.object({
119
+ runId: z2.string(),
120
+ scenarioId: z2.string(),
118
121
  originalClassification: classification,
119
122
  disputedTo: classification,
120
- qaActor: z.string(),
121
- qaReason: z.string().optional()
123
+ qaActor: z2.string(),
124
+ qaReason: z2.string().optional()
122
125
  }).passthrough();
123
- edgeDiscovered = z.object({
124
- kind: z.enum(["tests", "uses", "covers", "modifies", "jira-linked", "similar", "ran"]),
125
- from: z.string(),
126
- to: z.string(),
127
- confidence: z.number().min(0).max(1).optional(),
128
- source: z.string()
126
+ edgeDiscovered = z2.object({
127
+ kind: z2.enum(["tests", "uses", "covers", "modifies", "jira-linked", "similar", "ran"]),
128
+ from: z2.string(),
129
+ to: z2.string(),
130
+ confidence: z2.number().min(0).max(1).optional(),
131
+ source: z2.string()
129
132
  }).passthrough();
130
133
  base = {
131
- event_id: z.string().min(20),
134
+ event_id: z2.string().min(20),
132
135
  schema_version: schemaV,
133
136
  ts: iso,
134
- actor: z.string()
137
+ actor: z2.string()
135
138
  };
136
- EventSchema = z.discriminatedUnion("type", [
137
- z.object({ ...base, type: z.literal("ticket.fetched"), payload: ticketFetched }),
138
- z.object({ ...base, type: z.literal("ticket.enriched"), payload: ticketEnriched }),
139
- z.object({ ...base, type: z.literal("scenario.generated"), payload: scenarioGenerated }),
140
- z.object({ ...base, type: z.literal("pom.generated"), payload: pomGenerated }),
141
- z.object({ ...base, type: z.literal("pom.promoted"), payload: pomPromoted }),
142
- z.object({ ...base, type: z.literal("run.completed"), payload: runCompleted }),
143
- z.object({ ...base, type: z.literal("run.classified"), payload: runClassified }),
144
- z.object({
139
+ EventSchema = z2.discriminatedUnion("type", [
140
+ z2.object({ ...base, type: z2.literal("ticket.fetched"), payload: ticketFetched }),
141
+ z2.object({ ...base, type: z2.literal("ticket.enriched"), payload: ticketEnriched }),
142
+ z2.object({ ...base, type: z2.literal("scenario.generated"), payload: scenarioGenerated }),
143
+ z2.object({ ...base, type: z2.literal("pom.generated"), payload: pomGenerated }),
144
+ z2.object({ ...base, type: z2.literal("pom.promoted"), payload: pomPromoted }),
145
+ z2.object({ ...base, type: z2.literal("run.completed"), payload: runCompleted }),
146
+ z2.object({ ...base, type: z2.literal("run.classified"), payload: runClassified }),
147
+ z2.object({
145
148
  ...base,
146
- type: z.literal("classification.disputed"),
149
+ type: z2.literal("classification.disputed"),
147
150
  payload: classificationDisputed
148
151
  }),
149
- z.object({ ...base, type: z.literal("edge.discovered"), payload: edgeDiscovered })
152
+ z2.object({ ...base, type: z2.literal("edge.discovered"), payload: edgeDiscovered })
150
153
  ]);
151
154
  });
152
155
 
153
156
  // src/graph/store.ts
154
157
  import { createHash } from "crypto";
155
158
  import {
156
- existsSync,
159
+ existsSync as existsSync3,
157
160
  mkdirSync,
158
161
  readdirSync,
159
162
  readFileSync,
@@ -180,7 +183,7 @@ function appendEvents(repoRoot, events, opts) {
180
183
  }
181
184
  function loadAllEvents(repoRoot) {
182
185
  const paths = graphPaths(repoRoot);
183
- if (!existsSync(paths.eventsDir))
186
+ if (!existsSync3(paths.eventsDir))
184
187
  return [];
185
188
  const files = [];
186
189
  for (const monthDir of readdirSync(paths.eventsDir, { withFileTypes: true })) {
@@ -359,7 +362,7 @@ function writeSnapshot(repoRoot, snap) {
359
362
  }
360
363
  function loadSnapshot(repoRoot) {
361
364
  const paths = graphPaths(repoRoot);
362
- if (!existsSync(paths.snapshotFile))
365
+ if (!existsSync3(paths.snapshotFile))
363
366
  return null;
364
367
  try {
365
368
  return JSON.parse(readFileSync(paths.snapshotFile, "utf8"));
@@ -437,8 +440,8 @@ __export(exports_graph_record_script, {
437
440
  recordScriptImpl: () => recordScriptImpl
438
441
  });
439
442
  import { createHash as createHash2 } from "crypto";
440
- import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
441
- import { basename, join as join3 } from "path";
443
+ import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "fs";
444
+ import { basename, join as join5 } from "path";
442
445
  function inferPriority(name, gherkin) {
443
446
  const haystack = `${name} ${gherkin}`.toLowerCase();
444
447
  for (const kw of P0_KEYWORDS) {
@@ -480,9 +483,9 @@ function parseFeature(text) {
480
483
  return scenarios;
481
484
  }
482
485
  function listPomFiles(dir) {
483
- if (!existsSync4(dir))
486
+ if (!existsSync6(dir))
484
487
  return [];
485
- return readdirSync2(dir).filter((f) => f.endsWith(".ts")).map((f) => join3(dir, f));
488
+ return readdirSync2(dir).filter((f) => f.endsWith(".ts")).map((f) => join5(dir, f));
486
489
  }
487
490
  function extractRoute(pomContent) {
488
491
  const m = pomContent.match(/goto\s*\(\s*['"]([^'"]+)['"]/);
@@ -509,11 +512,11 @@ function extractPomUsage(specContent) {
509
512
  return [...names];
510
513
  }
511
514
  async function recordScriptImpl(repoRoot, ticket) {
512
- const ticketDir = join3(repoRoot, ".xera", ticket);
513
- const featurePath = join3(ticketDir, "feature", `${ticket}.feature`);
514
- const specPath = join3(ticketDir, "tests", `${ticket}.spec.ts`);
515
- const pomDir = join3(ticketDir, "poms");
516
- if (!existsSync4(featurePath)) {
515
+ const ticketDir = join5(repoRoot, ".xera", ticket);
516
+ const featurePath = join5(ticketDir, "feature", `${ticket}.feature`);
517
+ const specPath = join5(ticketDir, "tests", `${ticket}.spec.ts`);
518
+ const pomDir = join5(ticketDir, "poms");
519
+ if (!existsSync6(featurePath)) {
517
520
  console.error(`[graph-record script] feature missing`);
518
521
  return 1;
519
522
  }
@@ -551,7 +554,7 @@ async function recordScriptImpl(repoRoot, ticket) {
551
554
  };
552
555
  events.push(mk("xera-script", "pom.generated", pg));
553
556
  }
554
- if (existsSync4(specPath)) {
557
+ if (existsSync6(specPath)) {
555
558
  const specContent = readFileSync4(specPath, "utf8");
556
559
  const usedPoms = extractPomUsage(specContent);
557
560
  for (const scenario of scenarios) {
@@ -619,7 +622,7 @@ var init_graph_record_script = __esm(() => {
619
622
  ];
620
623
  });
621
624
 
622
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js
625
+ // ../../node_modules/yaml/dist/nodes/identity.js
623
626
  var require_identity = __commonJS((exports) => {
624
627
  var ALIAS = Symbol.for("yaml.alias");
625
628
  var DOC = Symbol.for("yaml.document");
@@ -673,7 +676,7 @@ var require_identity = __commonJS((exports) => {
673
676
  exports.isSeq = isSeq;
674
677
  });
675
678
 
676
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/visit.js
679
+ // ../../node_modules/yaml/dist/visit.js
677
680
  var require_visit = __commonJS((exports) => {
678
681
  var identity = require_identity();
679
682
  var BREAK = Symbol("break visit");
@@ -828,7 +831,7 @@ var require_visit = __commonJS((exports) => {
828
831
  exports.visitAsync = visitAsync;
829
832
  });
830
833
 
831
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js
834
+ // ../../node_modules/yaml/dist/doc/directives.js
832
835
  var require_directives = __commonJS((exports) => {
833
836
  var identity = require_identity();
834
837
  var visit = require_visit();
@@ -980,7 +983,7 @@ var require_directives = __commonJS((exports) => {
980
983
  exports.Directives = Directives;
981
984
  });
982
985
 
983
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js
986
+ // ../../node_modules/yaml/dist/doc/anchors.js
984
987
  var require_anchors = __commonJS((exports) => {
985
988
  var identity = require_identity();
986
989
  var visit = require_visit();
@@ -1042,7 +1045,7 @@ var require_anchors = __commonJS((exports) => {
1042
1045
  exports.findNewAnchor = findNewAnchor;
1043
1046
  });
1044
1047
 
1045
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js
1048
+ // ../../node_modules/yaml/dist/doc/applyReviver.js
1046
1049
  var require_applyReviver = __commonJS((exports) => {
1047
1050
  function applyReviver(reviver, obj, key, val) {
1048
1051
  if (val && typeof val === "object") {
@@ -1089,7 +1092,7 @@ var require_applyReviver = __commonJS((exports) => {
1089
1092
  exports.applyReviver = applyReviver;
1090
1093
  });
1091
1094
 
1092
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js
1095
+ // ../../node_modules/yaml/dist/nodes/toJS.js
1093
1096
  var require_toJS = __commonJS((exports) => {
1094
1097
  var identity = require_identity();
1095
1098
  function toJS(value, arg, ctx) {
@@ -1116,7 +1119,7 @@ var require_toJS = __commonJS((exports) => {
1116
1119
  exports.toJS = toJS;
1117
1120
  });
1118
1121
 
1119
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js
1122
+ // ../../node_modules/yaml/dist/nodes/Node.js
1120
1123
  var require_Node = __commonJS((exports) => {
1121
1124
  var applyReviver = require_applyReviver();
1122
1125
  var identity = require_identity();
@@ -1153,7 +1156,7 @@ var require_Node = __commonJS((exports) => {
1153
1156
  exports.NodeBase = NodeBase;
1154
1157
  });
1155
1158
 
1156
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js
1159
+ // ../../node_modules/yaml/dist/nodes/Alias.js
1157
1160
  var require_Alias = __commonJS((exports) => {
1158
1161
  var anchors = require_anchors();
1159
1162
  var visit = require_visit();
@@ -1263,7 +1266,7 @@ var require_Alias = __commonJS((exports) => {
1263
1266
  exports.Alias = Alias;
1264
1267
  });
1265
1268
 
1266
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js
1269
+ // ../../node_modules/yaml/dist/nodes/Scalar.js
1267
1270
  var require_Scalar = __commonJS((exports) => {
1268
1271
  var identity = require_identity();
1269
1272
  var Node = require_Node();
@@ -1291,7 +1294,7 @@ var require_Scalar = __commonJS((exports) => {
1291
1294
  exports.isScalarValue = isScalarValue;
1292
1295
  });
1293
1296
 
1294
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js
1297
+ // ../../node_modules/yaml/dist/doc/createNode.js
1295
1298
  var require_createNode = __commonJS((exports) => {
1296
1299
  var Alias = require_Alias();
1297
1300
  var identity = require_identity();
@@ -1363,7 +1366,7 @@ var require_createNode = __commonJS((exports) => {
1363
1366
  exports.createNode = createNode;
1364
1367
  });
1365
1368
 
1366
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js
1369
+ // ../../node_modules/yaml/dist/nodes/Collection.js
1367
1370
  var require_Collection = __commonJS((exports) => {
1368
1371
  var createNode = require_createNode();
1369
1372
  var identity = require_identity();
@@ -1478,7 +1481,7 @@ var require_Collection = __commonJS((exports) => {
1478
1481
  exports.isEmptyPath = isEmptyPath;
1479
1482
  });
1480
1483
 
1481
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js
1484
+ // ../../node_modules/yaml/dist/stringify/stringifyComment.js
1482
1485
  var require_stringifyComment = __commonJS((exports) => {
1483
1486
  var stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#");
1484
1487
  function indentComment(comment, indent) {
@@ -1495,7 +1498,7 @@ var require_stringifyComment = __commonJS((exports) => {
1495
1498
  exports.stringifyComment = stringifyComment;
1496
1499
  });
1497
1500
 
1498
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js
1501
+ // ../../node_modules/yaml/dist/stringify/foldFlowLines.js
1499
1502
  var require_foldFlowLines = __commonJS((exports) => {
1500
1503
  var FOLD_FLOW = "flow";
1501
1504
  var FOLD_BLOCK = "block";
@@ -1632,7 +1635,7 @@ ${indent}${text.slice(fold + 1, end2)}`;
1632
1635
  exports.foldFlowLines = foldFlowLines;
1633
1636
  });
1634
1637
 
1635
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js
1638
+ // ../../node_modules/yaml/dist/stringify/stringifyString.js
1636
1639
  var require_stringifyString = __commonJS((exports) => {
1637
1640
  var Scalar = require_Scalar();
1638
1641
  var foldFlowLines = require_foldFlowLines();
@@ -1930,7 +1933,7 @@ ${indent}`);
1930
1933
  exports.stringifyString = stringifyString;
1931
1934
  });
1932
1935
 
1933
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js
1936
+ // ../../node_modules/yaml/dist/stringify/stringify.js
1934
1937
  var require_stringify = __commonJS((exports) => {
1935
1938
  var anchors = require_anchors();
1936
1939
  var identity = require_identity();
@@ -2051,7 +2054,7 @@ ${ctx.indent}${str}`;
2051
2054
  exports.stringify = stringify;
2052
2055
  });
2053
2056
 
2054
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js
2057
+ // ../../node_modules/yaml/dist/stringify/stringifyPair.js
2055
2058
  var require_stringifyPair = __commonJS((exports) => {
2056
2059
  var identity = require_identity();
2057
2060
  var Scalar = require_Scalar();
@@ -2187,7 +2190,7 @@ ${ctx.indent}`;
2187
2190
  exports.stringifyPair = stringifyPair;
2188
2191
  });
2189
2192
 
2190
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/log.js
2193
+ // ../../node_modules/yaml/dist/log.js
2191
2194
  var require_log = __commonJS((exports) => {
2192
2195
  var node_process = __require("process");
2193
2196
  function debug(logLevel, ...messages) {
@@ -2206,7 +2209,7 @@ var require_log = __commonJS((exports) => {
2206
2209
  exports.warn = warn;
2207
2210
  });
2208
2211
 
2209
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js
2212
+ // ../../node_modules/yaml/dist/schema/yaml-1.1/merge.js
2210
2213
  var require_merge = __commonJS((exports) => {
2211
2214
  var identity = require_identity();
2212
2215
  var Scalar = require_Scalar();
@@ -2263,7 +2266,7 @@ var require_merge = __commonJS((exports) => {
2263
2266
  exports.merge = merge;
2264
2267
  });
2265
2268
 
2266
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js
2269
+ // ../../node_modules/yaml/dist/nodes/addPairToJSMap.js
2267
2270
  var require_addPairToJSMap = __commonJS((exports) => {
2268
2271
  var log = require_log();
2269
2272
  var merge = require_merge();
@@ -2324,7 +2327,7 @@ var require_addPairToJSMap = __commonJS((exports) => {
2324
2327
  exports.addPairToJSMap = addPairToJSMap;
2325
2328
  });
2326
2329
 
2327
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js
2330
+ // ../../node_modules/yaml/dist/nodes/Pair.js
2328
2331
  var require_Pair = __commonJS((exports) => {
2329
2332
  var createNode = require_createNode();
2330
2333
  var stringifyPair = require_stringifyPair();
@@ -2362,7 +2365,7 @@ var require_Pair = __commonJS((exports) => {
2362
2365
  exports.createPair = createPair;
2363
2366
  });
2364
2367
 
2365
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js
2368
+ // ../../node_modules/yaml/dist/stringify/stringifyCollection.js
2366
2369
  var require_stringifyCollection = __commonJS((exports) => {
2367
2370
  var identity = require_identity();
2368
2371
  var stringify = require_stringify();
@@ -2514,7 +2517,7 @@ ${indent}${end}`;
2514
2517
  exports.stringifyCollection = stringifyCollection;
2515
2518
  });
2516
2519
 
2517
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js
2520
+ // ../../node_modules/yaml/dist/nodes/YAMLMap.js
2518
2521
  var require_YAMLMap = __commonJS((exports) => {
2519
2522
  var stringifyCollection = require_stringifyCollection();
2520
2523
  var addPairToJSMap = require_addPairToJSMap();
@@ -2641,7 +2644,7 @@ var require_YAMLMap = __commonJS((exports) => {
2641
2644
  exports.findPair = findPair;
2642
2645
  });
2643
2646
 
2644
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js
2647
+ // ../../node_modules/yaml/dist/schema/common/map.js
2645
2648
  var require_map = __commonJS((exports) => {
2646
2649
  var identity = require_identity();
2647
2650
  var YAMLMap = require_YAMLMap();
@@ -2660,7 +2663,7 @@ var require_map = __commonJS((exports) => {
2660
2663
  exports.map = map;
2661
2664
  });
2662
2665
 
2663
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js
2666
+ // ../../node_modules/yaml/dist/nodes/YAMLSeq.js
2664
2667
  var require_YAMLSeq = __commonJS((exports) => {
2665
2668
  var createNode = require_createNode();
2666
2669
  var stringifyCollection = require_stringifyCollection();
@@ -2753,7 +2756,7 @@ var require_YAMLSeq = __commonJS((exports) => {
2753
2756
  exports.YAMLSeq = YAMLSeq;
2754
2757
  });
2755
2758
 
2756
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js
2759
+ // ../../node_modules/yaml/dist/schema/common/seq.js
2757
2760
  var require_seq = __commonJS((exports) => {
2758
2761
  var identity = require_identity();
2759
2762
  var YAMLSeq = require_YAMLSeq();
@@ -2772,7 +2775,7 @@ var require_seq = __commonJS((exports) => {
2772
2775
  exports.seq = seq;
2773
2776
  });
2774
2777
 
2775
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js
2778
+ // ../../node_modules/yaml/dist/schema/common/string.js
2776
2779
  var require_string = __commonJS((exports) => {
2777
2780
  var stringifyString = require_stringifyString();
2778
2781
  var string = {
@@ -2788,7 +2791,7 @@ var require_string = __commonJS((exports) => {
2788
2791
  exports.string = string;
2789
2792
  });
2790
2793
 
2791
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js
2794
+ // ../../node_modules/yaml/dist/schema/common/null.js
2792
2795
  var require_null = __commonJS((exports) => {
2793
2796
  var Scalar = require_Scalar();
2794
2797
  var nullTag = {
@@ -2803,7 +2806,7 @@ var require_null = __commonJS((exports) => {
2803
2806
  exports.nullTag = nullTag;
2804
2807
  });
2805
2808
 
2806
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js
2809
+ // ../../node_modules/yaml/dist/schema/core/bool.js
2807
2810
  var require_bool = __commonJS((exports) => {
2808
2811
  var Scalar = require_Scalar();
2809
2812
  var boolTag = {
@@ -2824,7 +2827,7 @@ var require_bool = __commonJS((exports) => {
2824
2827
  exports.boolTag = boolTag;
2825
2828
  });
2826
2829
 
2827
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js
2830
+ // ../../node_modules/yaml/dist/stringify/stringifyNumber.js
2828
2831
  var require_stringifyNumber = __commonJS((exports) => {
2829
2832
  function stringifyNumber({ format, minFractionDigits, tag, value }) {
2830
2833
  if (typeof value === "bigint")
@@ -2848,7 +2851,7 @@ var require_stringifyNumber = __commonJS((exports) => {
2848
2851
  exports.stringifyNumber = stringifyNumber;
2849
2852
  });
2850
2853
 
2851
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js
2854
+ // ../../node_modules/yaml/dist/schema/core/float.js
2852
2855
  var require_float = __commonJS((exports) => {
2853
2856
  var Scalar = require_Scalar();
2854
2857
  var stringifyNumber = require_stringifyNumber();
@@ -2891,7 +2894,7 @@ var require_float = __commonJS((exports) => {
2891
2894
  exports.floatNaN = floatNaN;
2892
2895
  });
2893
2896
 
2894
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js
2897
+ // ../../node_modules/yaml/dist/schema/core/int.js
2895
2898
  var require_int = __commonJS((exports) => {
2896
2899
  var stringifyNumber = require_stringifyNumber();
2897
2900
  var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value);
@@ -2933,7 +2936,7 @@ var require_int = __commonJS((exports) => {
2933
2936
  exports.intOct = intOct;
2934
2937
  });
2935
2938
 
2936
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js
2939
+ // ../../node_modules/yaml/dist/schema/core/schema.js
2937
2940
  var require_schema = __commonJS((exports) => {
2938
2941
  var map = require_map();
2939
2942
  var _null = require_null();
@@ -2958,7 +2961,7 @@ var require_schema = __commonJS((exports) => {
2958
2961
  exports.schema = schema;
2959
2962
  });
2960
2963
 
2961
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js
2964
+ // ../../node_modules/yaml/dist/schema/json/schema.js
2962
2965
  var require_schema2 = __commonJS((exports) => {
2963
2966
  var Scalar = require_Scalar();
2964
2967
  var map = require_map();
@@ -3022,7 +3025,7 @@ var require_schema2 = __commonJS((exports) => {
3022
3025
  exports.schema = schema;
3023
3026
  });
3024
3027
 
3025
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js
3028
+ // ../../node_modules/yaml/dist/schema/yaml-1.1/binary.js
3026
3029
  var require_binary = __commonJS((exports) => {
3027
3030
  var node_buffer = __require("buffer");
3028
3031
  var Scalar = require_Scalar();
@@ -3077,7 +3080,7 @@ var require_binary = __commonJS((exports) => {
3077
3080
  exports.binary = binary;
3078
3081
  });
3079
3082
 
3080
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js
3083
+ // ../../node_modules/yaml/dist/schema/yaml-1.1/pairs.js
3081
3084
  var require_pairs = __commonJS((exports) => {
3082
3085
  var identity = require_identity();
3083
3086
  var Pair = require_Pair();
@@ -3152,7 +3155,7 @@ ${cn.comment}` : item.comment;
3152
3155
  exports.resolvePairs = resolvePairs;
3153
3156
  });
3154
3157
 
3155
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js
3158
+ // ../../node_modules/yaml/dist/schema/yaml-1.1/omap.js
3156
3159
  var require_omap = __commonJS((exports) => {
3157
3160
  var identity = require_identity();
3158
3161
  var toJS = require_toJS();
@@ -3224,7 +3227,7 @@ var require_omap = __commonJS((exports) => {
3224
3227
  exports.omap = omap;
3225
3228
  });
3226
3229
 
3227
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js
3230
+ // ../../node_modules/yaml/dist/schema/yaml-1.1/bool.js
3228
3231
  var require_bool2 = __commonJS((exports) => {
3229
3232
  var Scalar = require_Scalar();
3230
3233
  function boolStringify({ value, source }, ctx) {
@@ -3253,7 +3256,7 @@ var require_bool2 = __commonJS((exports) => {
3253
3256
  exports.trueTag = trueTag;
3254
3257
  });
3255
3258
 
3256
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js
3259
+ // ../../node_modules/yaml/dist/schema/yaml-1.1/float.js
3257
3260
  var require_float2 = __commonJS((exports) => {
3258
3261
  var Scalar = require_Scalar();
3259
3262
  var stringifyNumber = require_stringifyNumber();
@@ -3299,7 +3302,7 @@ var require_float2 = __commonJS((exports) => {
3299
3302
  exports.floatNaN = floatNaN;
3300
3303
  });
3301
3304
 
3302
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js
3305
+ // ../../node_modules/yaml/dist/schema/yaml-1.1/int.js
3303
3306
  var require_int2 = __commonJS((exports) => {
3304
3307
  var stringifyNumber = require_stringifyNumber();
3305
3308
  var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value);
@@ -3375,7 +3378,7 @@ var require_int2 = __commonJS((exports) => {
3375
3378
  exports.intOct = intOct;
3376
3379
  });
3377
3380
 
3378
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js
3381
+ // ../../node_modules/yaml/dist/schema/yaml-1.1/set.js
3379
3382
  var require_set = __commonJS((exports) => {
3380
3383
  var identity = require_identity();
3381
3384
  var Pair = require_Pair();
@@ -3458,7 +3461,7 @@ var require_set = __commonJS((exports) => {
3458
3461
  exports.set = set;
3459
3462
  });
3460
3463
 
3461
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js
3464
+ // ../../node_modules/yaml/dist/schema/yaml-1.1/timestamp.js
3462
3465
  var require_timestamp = __commonJS((exports) => {
3463
3466
  var stringifyNumber = require_stringifyNumber();
3464
3467
  function parseSexagesimal(str, asBigInt) {
@@ -3540,7 +3543,7 @@ var require_timestamp = __commonJS((exports) => {
3540
3543
  exports.timestamp = timestamp;
3541
3544
  });
3542
3545
 
3543
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js
3546
+ // ../../node_modules/yaml/dist/schema/yaml-1.1/schema.js
3544
3547
  var require_schema3 = __commonJS((exports) => {
3545
3548
  var map = require_map();
3546
3549
  var _null = require_null();
@@ -3581,7 +3584,7 @@ var require_schema3 = __commonJS((exports) => {
3581
3584
  exports.schema = schema;
3582
3585
  });
3583
3586
 
3584
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js
3587
+ // ../../node_modules/yaml/dist/schema/tags.js
3585
3588
  var require_tags = __commonJS((exports) => {
3586
3589
  var map = require_map();
3587
3590
  var _null = require_null();
@@ -3672,7 +3675,7 @@ var require_tags = __commonJS((exports) => {
3672
3675
  exports.getTags = getTags;
3673
3676
  });
3674
3677
 
3675
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js
3678
+ // ../../node_modules/yaml/dist/schema/Schema.js
3676
3679
  var require_Schema = __commonJS((exports) => {
3677
3680
  var identity = require_identity();
3678
3681
  var map = require_map();
@@ -3702,7 +3705,7 @@ var require_Schema = __commonJS((exports) => {
3702
3705
  exports.Schema = Schema;
3703
3706
  });
3704
3707
 
3705
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js
3708
+ // ../../node_modules/yaml/dist/stringify/stringifyDocument.js
3706
3709
  var require_stringifyDocument = __commonJS((exports) => {
3707
3710
  var identity = require_identity();
3708
3711
  var stringify = require_stringify();
@@ -3782,7 +3785,7 @@ var require_stringifyDocument = __commonJS((exports) => {
3782
3785
  exports.stringifyDocument = stringifyDocument;
3783
3786
  });
3784
3787
 
3785
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js
3788
+ // ../../node_modules/yaml/dist/doc/Document.js
3786
3789
  var require_Document = __commonJS((exports) => {
3787
3790
  var Alias = require_Alias();
3788
3791
  var Collection = require_Collection();
@@ -4017,7 +4020,7 @@ var require_Document = __commonJS((exports) => {
4017
4020
  exports.Document = Document;
4018
4021
  });
4019
4022
 
4020
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/errors.js
4023
+ // ../../node_modules/yaml/dist/errors.js
4021
4024
  var require_errors = __commonJS((exports) => {
4022
4025
  class YAMLError extends Error {
4023
4026
  constructor(name, pos, code, message) {
@@ -4082,7 +4085,7 @@ ${pointer}
4082
4085
  exports.prettifyError = prettifyError;
4083
4086
  });
4084
4087
 
4085
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js
4088
+ // ../../node_modules/yaml/dist/compose/resolve-props.js
4086
4089
  var require_resolve_props = __commonJS((exports) => {
4087
4090
  function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) {
4088
4091
  let spaceBefore = false;
@@ -4212,7 +4215,7 @@ var require_resolve_props = __commonJS((exports) => {
4212
4215
  exports.resolveProps = resolveProps;
4213
4216
  });
4214
4217
 
4215
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js
4218
+ // ../../node_modules/yaml/dist/compose/util-contains-newline.js
4216
4219
  var require_util_contains_newline = __commonJS((exports) => {
4217
4220
  function containsNewline(key) {
4218
4221
  if (!key)
@@ -4252,7 +4255,7 @@ var require_util_contains_newline = __commonJS((exports) => {
4252
4255
  exports.containsNewline = containsNewline;
4253
4256
  });
4254
4257
 
4255
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js
4258
+ // ../../node_modules/yaml/dist/compose/util-flow-indent-check.js
4256
4259
  var require_util_flow_indent_check = __commonJS((exports) => {
4257
4260
  var utilContainsNewline = require_util_contains_newline();
4258
4261
  function flowIndentCheck(indent, fc, onError) {
@@ -4267,7 +4270,7 @@ var require_util_flow_indent_check = __commonJS((exports) => {
4267
4270
  exports.flowIndentCheck = flowIndentCheck;
4268
4271
  });
4269
4272
 
4270
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js
4273
+ // ../../node_modules/yaml/dist/compose/util-map-includes.js
4271
4274
  var require_util_map_includes = __commonJS((exports) => {
4272
4275
  var identity = require_identity();
4273
4276
  function mapIncludes(ctx, items, search) {
@@ -4280,7 +4283,7 @@ var require_util_map_includes = __commonJS((exports) => {
4280
4283
  exports.mapIncludes = mapIncludes;
4281
4284
  });
4282
4285
 
4283
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js
4286
+ // ../../node_modules/yaml/dist/compose/resolve-block-map.js
4284
4287
  var require_resolve_block_map = __commonJS((exports) => {
4285
4288
  var Pair = require_Pair();
4286
4289
  var YAMLMap = require_YAMLMap();
@@ -4387,7 +4390,7 @@ var require_resolve_block_map = __commonJS((exports) => {
4387
4390
  exports.resolveBlockMap = resolveBlockMap;
4388
4391
  });
4389
4392
 
4390
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js
4393
+ // ../../node_modules/yaml/dist/compose/resolve-block-seq.js
4391
4394
  var require_resolve_block_seq = __commonJS((exports) => {
4392
4395
  var YAMLSeq = require_YAMLSeq();
4393
4396
  var resolveProps = require_resolve_props();
@@ -4435,7 +4438,7 @@ var require_resolve_block_seq = __commonJS((exports) => {
4435
4438
  exports.resolveBlockSeq = resolveBlockSeq;
4436
4439
  });
4437
4440
 
4438
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js
4441
+ // ../../node_modules/yaml/dist/compose/resolve-end.js
4439
4442
  var require_resolve_end = __commonJS((exports) => {
4440
4443
  function resolveEnd(end, offset, reqSpace, onError) {
4441
4444
  let comment = "";
@@ -4475,7 +4478,7 @@ var require_resolve_end = __commonJS((exports) => {
4475
4478
  exports.resolveEnd = resolveEnd;
4476
4479
  });
4477
4480
 
4478
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js
4481
+ // ../../node_modules/yaml/dist/compose/resolve-flow-collection.js
4479
4482
  var require_resolve_flow_collection = __commonJS((exports) => {
4480
4483
  var identity = require_identity();
4481
4484
  var Pair = require_Pair();
@@ -4666,7 +4669,7 @@ var require_resolve_flow_collection = __commonJS((exports) => {
4666
4669
  exports.resolveFlowCollection = resolveFlowCollection;
4667
4670
  });
4668
4671
 
4669
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js
4672
+ // ../../node_modules/yaml/dist/compose/compose-collection.js
4670
4673
  var require_compose_collection = __commonJS((exports) => {
4671
4674
  var identity = require_identity();
4672
4675
  var Scalar = require_Scalar();
@@ -4728,7 +4731,7 @@ var require_compose_collection = __commonJS((exports) => {
4728
4731
  exports.composeCollection = composeCollection;
4729
4732
  });
4730
4733
 
4731
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js
4734
+ // ../../node_modules/yaml/dist/compose/resolve-block-scalar.js
4732
4735
  var require_resolve_block_scalar = __commonJS((exports) => {
4733
4736
  var Scalar = require_Scalar();
4734
4737
  function resolveBlockScalar(ctx, scalar, onError) {
@@ -4921,7 +4924,7 @@ var require_resolve_block_scalar = __commonJS((exports) => {
4921
4924
  exports.resolveBlockScalar = resolveBlockScalar;
4922
4925
  });
4923
4926
 
4924
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js
4927
+ // ../../node_modules/yaml/dist/compose/resolve-flow-scalar.js
4925
4928
  var require_resolve_flow_scalar = __commonJS((exports) => {
4926
4929
  var Scalar = require_Scalar();
4927
4930
  var resolveEnd = require_resolve_end();
@@ -5138,7 +5141,7 @@ var require_resolve_flow_scalar = __commonJS((exports) => {
5138
5141
  exports.resolveFlowScalar = resolveFlowScalar;
5139
5142
  });
5140
5143
 
5141
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js
5144
+ // ../../node_modules/yaml/dist/compose/compose-scalar.js
5142
5145
  var require_compose_scalar = __commonJS((exports) => {
5143
5146
  var identity = require_identity();
5144
5147
  var Scalar = require_Scalar();
@@ -5216,7 +5219,7 @@ var require_compose_scalar = __commonJS((exports) => {
5216
5219
  exports.composeScalar = composeScalar;
5217
5220
  });
5218
5221
 
5219
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js
5222
+ // ../../node_modules/yaml/dist/compose/util-empty-scalar-position.js
5220
5223
  var require_util_empty_scalar_position = __commonJS((exports) => {
5221
5224
  function emptyScalarPosition(offset, before, pos) {
5222
5225
  if (before) {
@@ -5243,7 +5246,7 @@ var require_util_empty_scalar_position = __commonJS((exports) => {
5243
5246
  exports.emptyScalarPosition = emptyScalarPosition;
5244
5247
  });
5245
5248
 
5246
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js
5249
+ // ../../node_modules/yaml/dist/compose/compose-node.js
5247
5250
  var require_compose_node = __commonJS((exports) => {
5248
5251
  var Alias = require_Alias();
5249
5252
  var identity = require_identity();
@@ -5346,7 +5349,7 @@ var require_compose_node = __commonJS((exports) => {
5346
5349
  exports.composeNode = composeNode;
5347
5350
  });
5348
5351
 
5349
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js
5352
+ // ../../node_modules/yaml/dist/compose/compose-doc.js
5350
5353
  var require_compose_doc = __commonJS((exports) => {
5351
5354
  var Document = require_Document();
5352
5355
  var composeNode = require_compose_node();
@@ -5386,7 +5389,7 @@ var require_compose_doc = __commonJS((exports) => {
5386
5389
  exports.composeDoc = composeDoc;
5387
5390
  });
5388
5391
 
5389
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js
5392
+ // ../../node_modules/yaml/dist/compose/composer.js
5390
5393
  var require_composer = __commonJS((exports) => {
5391
5394
  var node_process = __require("process");
5392
5395
  var directives = require_directives();
@@ -5577,7 +5580,7 @@ ${end.comment}` : end.comment;
5577
5580
  exports.Composer = Composer;
5578
5581
  });
5579
5582
 
5580
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js
5583
+ // ../../node_modules/yaml/dist/parse/cst-scalar.js
5581
5584
  var require_cst_scalar = __commonJS((exports) => {
5582
5585
  var resolveBlockScalar = require_resolve_block_scalar();
5583
5586
  var resolveFlowScalar = require_resolve_flow_scalar();
@@ -5767,7 +5770,7 @@ var require_cst_scalar = __commonJS((exports) => {
5767
5770
  exports.setScalarValue = setScalarValue;
5768
5771
  });
5769
5772
 
5770
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js
5773
+ // ../../node_modules/yaml/dist/parse/cst-stringify.js
5771
5774
  var require_cst_stringify = __commonJS((exports) => {
5772
5775
  var stringify = (cst) => ("type" in cst) ? stringifyToken(cst) : stringifyItem(cst);
5773
5776
  function stringifyToken(token) {
@@ -5825,7 +5828,7 @@ var require_cst_stringify = __commonJS((exports) => {
5825
5828
  exports.stringify = stringify;
5826
5829
  });
5827
5830
 
5828
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js
5831
+ // ../../node_modules/yaml/dist/parse/cst-visit.js
5829
5832
  var require_cst_visit = __commonJS((exports) => {
5830
5833
  var BREAK = Symbol("break visit");
5831
5834
  var SKIP = Symbol("skip children");
@@ -5884,7 +5887,7 @@ var require_cst_visit = __commonJS((exports) => {
5884
5887
  exports.visit = visit;
5885
5888
  });
5886
5889
 
5887
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js
5890
+ // ../../node_modules/yaml/dist/parse/cst.js
5888
5891
  var require_cst = __commonJS((exports) => {
5889
5892
  var cstScalar = require_cst_scalar();
5890
5893
  var cstStringify = require_cst_stringify();
@@ -5985,7 +5988,7 @@ var require_cst = __commonJS((exports) => {
5985
5988
  exports.tokenType = tokenType;
5986
5989
  });
5987
5990
 
5988
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js
5991
+ // ../../node_modules/yaml/dist/parse/lexer.js
5989
5992
  var require_lexer = __commonJS((exports) => {
5990
5993
  var cst = require_cst();
5991
5994
  function isEmpty(ch) {
@@ -6582,7 +6585,7 @@ var require_lexer = __commonJS((exports) => {
6582
6585
  exports.Lexer = Lexer;
6583
6586
  });
6584
6587
 
6585
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js
6588
+ // ../../node_modules/yaml/dist/parse/line-counter.js
6586
6589
  var require_line_counter = __commonJS((exports) => {
6587
6590
  class LineCounter {
6588
6591
  constructor() {
@@ -6610,7 +6613,7 @@ var require_line_counter = __commonJS((exports) => {
6610
6613
  exports.LineCounter = LineCounter;
6611
6614
  });
6612
6615
 
6613
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js
6616
+ // ../../node_modules/yaml/dist/parse/parser.js
6614
6617
  var require_parser = __commonJS((exports) => {
6615
6618
  var node_process = __require("process");
6616
6619
  var cst = require_cst();
@@ -7466,7 +7469,7 @@ var require_parser = __commonJS((exports) => {
7466
7469
  exports.Parser = Parser;
7467
7470
  });
7468
7471
 
7469
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/public-api.js
7472
+ // ../../node_modules/yaml/dist/public-api.js
7470
7473
  var require_public_api = __commonJS((exports) => {
7471
7474
  var composer = require_composer();
7472
7475
  var Document = require_Document();
@@ -7560,7 +7563,7 @@ var require_public_api = __commonJS((exports) => {
7560
7563
  exports.stringify = stringify;
7561
7564
  });
7562
7565
 
7563
- // ../../node_modules/.bun/yaml@2.9.0/node_modules/yaml/dist/index.js
7566
+ // ../../node_modules/yaml/dist/index.js
7564
7567
  var composer, Document, Schema, errors, Alias, identity, Pair, Scalar, YAMLMap, YAMLSeq, cst, lexer, lineCounter, parser, publicApi, visit, $Composer, $Document, $Schema, $YAMLError, $YAMLParseError, $YAMLWarning, $Alias, $isAlias, $isCollection, $isDocument, $isMap, $isNode, $isPair, $isScalar, $isSeq, $Pair, $Scalar, $YAMLMap, $YAMLSeq, $Lexer, $LineCounter, $Parser, $parse, $parseAllDocuments, $parseDocument, $stringify, $visit, $visitAsync;
7565
7568
  var init_dist = __esm(() => {
7566
7569
  composer = require_composer();
@@ -7616,8 +7619,8 @@ __export(exports_graph_record, {
7616
7619
  graphRecordCmd: () => graphRecordCmd
7617
7620
  });
7618
7621
  import { createHash as createHash3 } from "crypto";
7619
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
7620
- import { basename as basename2, join as join4 } from "path";
7622
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
7623
+ import { basename as basename2, join as join6 } from "path";
7621
7624
  function nowIso2() {
7622
7625
  return new Date().toISOString();
7623
7626
  }
@@ -7641,8 +7644,8 @@ function makeEvent(actor, type, payload) {
7641
7644
  };
7642
7645
  }
7643
7646
  function readStoryFrontmatter(repoRoot, ticket) {
7644
- const path = join4(repoRoot, ".xera", ticket, "story.md");
7645
- if (!existsSync5(path))
7647
+ const path = join6(repoRoot, ".xera", ticket, "story.md");
7648
+ if (!existsSync7(path))
7646
7649
  return null;
7647
7650
  const raw = readFileSync5(path, "utf8");
7648
7651
  const m = raw.match(/^---\n([\s\S]*?)\n---/);
@@ -7651,8 +7654,8 @@ function readStoryFrontmatter(repoRoot, ticket) {
7651
7654
  return $parse(m[1]);
7652
7655
  }
7653
7656
  function readGraphInput(repoRoot, ticket) {
7654
- const path = join4(repoRoot, ".xera", ticket, "graph-input.json");
7655
- if (!existsSync5(path))
7657
+ const path = join6(repoRoot, ".xera", ticket, "graph-input.json");
7658
+ if (!existsSync7(path))
7656
7659
  return { modifiesAreas: [] };
7657
7660
  try {
7658
7661
  return JSON.parse(readFileSync5(path, "utf8"));
@@ -7703,8 +7706,8 @@ async function recordScript(repoRoot, ticket) {
7703
7706
  return recordScriptImpl2(repoRoot, ticket);
7704
7707
  }
7705
7708
  async function recordExec(repoRoot, ticket, runId) {
7706
- const reporterPath = join4(repoRoot, ".xera", ticket, "runs", runId, "reporter.json");
7707
- if (!existsSync5(reporterPath)) {
7709
+ const reporterPath = join6(repoRoot, ".xera", ticket, "runs", runId, "reporter.json");
7710
+ if (!existsSync7(reporterPath)) {
7708
7711
  console.error(`[graph-record exec] reporter.json missing`);
7709
7712
  return 1;
7710
7713
  }
@@ -7726,8 +7729,8 @@ async function recordExec(repoRoot, ticket, runId) {
7726
7729
  return 0;
7727
7730
  }
7728
7731
  async function recordClassify(repoRoot, ticket, runId) {
7729
- const classifyPath = join4(repoRoot, ".xera", ticket, "runs", runId, "classifier-output.json");
7730
- if (!existsSync5(classifyPath)) {
7732
+ const classifyPath = join6(repoRoot, ".xera", ticket, "runs", runId, "classifier-output.json");
7733
+ if (!existsSync7(classifyPath)) {
7731
7734
  console.error(`[graph-record classify] classifier-output.json missing`);
7732
7735
  return 1;
7733
7736
  }
@@ -7834,7 +7837,10 @@ async function graphRecordCmd(argv) {
7834
7837
  "SELECTOR_DRIFT",
7835
7838
  "FLAKY",
7836
7839
  "PASS",
7837
- "TEST_OUTDATED"
7840
+ "TEST_OUTDATED",
7841
+ "CONTRACT_DRIFT",
7842
+ "RATE_LIMITED",
7843
+ "AUTH_EXPIRED"
7838
7844
  ];
7839
7845
  if (!validClass.includes(from) || !validClass.includes(to)) {
7840
7846
  console.error(`[graph-record dispute] --from and --to must be one of: ${validClass.join(", ")}`);
@@ -7869,11 +7875,11 @@ var exports_graph_backfill = {};
7869
7875
  __export(exports_graph_backfill, {
7870
7876
  graphBackfillCmd: () => graphBackfillCmd
7871
7877
  });
7872
- import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
7873
- import { join as join5 } from "path";
7878
+ import { existsSync as existsSync8, readdirSync as readdirSync3 } from "fs";
7879
+ import { join as join7 } from "path";
7874
7880
  async function backfillTicket(repoRoot, ticket, dryRun) {
7875
- const storyPath = join5(repoRoot, ".xera", ticket, "story.md");
7876
- if (!existsSync6(storyPath))
7881
+ const storyPath = join7(repoRoot, ".xera", ticket, "story.md");
7882
+ if (!existsSync8(storyPath))
7877
7883
  return 0;
7878
7884
  const { recordFetch: recordFetch2 } = await Promise.resolve().then(() => (init_graph_record(), exports_graph_record));
7879
7885
  if (dryRun) {
@@ -7887,8 +7893,8 @@ async function backfillTicket(repoRoot, ticket, dryRun) {
7887
7893
  async function graphBackfillCmd(argv) {
7888
7894
  const dryRun = argv.includes("--dry-run");
7889
7895
  const repoRoot = process.cwd();
7890
- const xeraDir = join5(repoRoot, ".xera");
7891
- if (!existsSync6(xeraDir)) {
7896
+ const xeraDir = join7(repoRoot, ".xera");
7897
+ if (!existsSync8(xeraDir)) {
7892
7898
  console.log("[backfill] no .xera/ directory");
7893
7899
  return 0;
7894
7900
  }
@@ -7914,6 +7920,212 @@ var init_graph_backfill = __esm(() => {
7914
7920
  init_graph_record_script();
7915
7921
  });
7916
7922
 
7923
+ // src/bin-internal/auth-setup.ts
7924
+ import { existsSync as existsSync2 } from "fs";
7925
+ import { join as join2 } from "path";
7926
+ import { pathToFileURL as pathToFileURL2 } from "url";
7927
+
7928
+ // src/config/load.ts
7929
+ import { existsSync } from "fs";
7930
+ import { join } from "path";
7931
+ import { pathToFileURL } from "url";
7932
+
7933
+ // src/config/schema.ts
7934
+ import { z } from "zod";
7935
+ var AuthRoleSchema = z.object({
7936
+ envEmail: z.string().min(1),
7937
+ envPassword: z.string().min(1)
7938
+ });
7939
+ var AuthSchema = z.object({
7940
+ strategy: z.enum(["storageState", "apiToken", "none"]).default("none"),
7941
+ ttl: z.string().default("8h"),
7942
+ refreshBuffer: z.string().default("30m"),
7943
+ setupScript: z.string().optional(),
7944
+ roles: z.record(z.string(), AuthRoleSchema).default({})
7945
+ });
7946
+ var WebSchema = z.object({
7947
+ baseUrl: z.record(z.string(), z.string().url()).refine((m) => Object.keys(m).length > 0, {
7948
+ message: "baseUrl must have at least one environment"
7949
+ }),
7950
+ defaultEnv: z.string(),
7951
+ auth: AuthSchema.prefault({}),
7952
+ testData: z.object({
7953
+ users: z.record(z.string(), z.object({ fromAuth: z.string() })).default({})
7954
+ }).prefault({})
7955
+ }).refine((w) => w.baseUrl[w.defaultEnv] !== undefined, {
7956
+ message: "defaultEnv must exist in baseUrl map",
7957
+ path: ["defaultEnv"]
7958
+ });
7959
+ var HttpAuthRoleSchema = z.object({
7960
+ tokenEnv: z.string().optional(),
7961
+ userEnv: z.string().optional(),
7962
+ passEnv: z.string().optional(),
7963
+ tokenUrl: z.string().url().optional(),
7964
+ clientIdEnv: z.string().optional(),
7965
+ clientSecretEnv: z.string().optional(),
7966
+ scope: z.string().optional()
7967
+ });
7968
+ var HttpAuthSchema = z.object({
7969
+ strategy: z.enum(["bearer", "apiKey", "basic", "oauth-cc", "custom", "none"]).default("none"),
7970
+ ttl: z.string().default("8h"),
7971
+ refreshBuffer: z.string().default("30m"),
7972
+ roles: z.record(z.string(), HttpAuthRoleSchema).default({})
7973
+ });
7974
+ var HttpSchema = z.object({
7975
+ baseUrl: z.record(z.string(), z.string().url()).refine((m) => Object.keys(m).length > 0, {
7976
+ message: "baseUrl must have at least one environment"
7977
+ }),
7978
+ defaultEnv: z.string(),
7979
+ spec: z.string().optional(),
7980
+ auth: HttpAuthSchema.prefault({})
7981
+ }).refine((h) => h.baseUrl[h.defaultEnv] !== undefined, {
7982
+ message: "defaultEnv must exist in baseUrl map",
7983
+ path: ["defaultEnv"]
7984
+ });
7985
+ var JiraSchema = z.object({
7986
+ baseUrl: z.string().url(),
7987
+ projectKeys: z.array(z.string().min(1)).min(1),
7988
+ fields: z.object({
7989
+ story: z.string().min(1),
7990
+ acceptanceCriteria: z.string().optional(),
7991
+ attachments: z.string().default("attachment")
7992
+ })
7993
+ });
7994
+ var AISchema = z.object({
7995
+ livePageSnapshot: z.boolean().default(true),
7996
+ confidenceThreshold: z.enum(["low", "medium", "high"]).default("medium"),
7997
+ maxRetries: z.object({
7998
+ typecheck: z.number().int().min(0).max(5).default(2),
7999
+ lint: z.number().int().min(0).max(5).default(2),
8000
+ validateFeature: z.number().int().min(0).max(5).default(2)
8001
+ }).prefault({})
8002
+ }).prefault({});
8003
+ var ReportingSchema = z.object({
8004
+ language: z.enum(["en", "vi"]).default("en"),
8005
+ postToJira: z.boolean().default(true),
8006
+ transition: z.object({
8007
+ onPass: z.string().nullable().default(null),
8008
+ onFail: z.string().nullable().default(null)
8009
+ }).prefault({}),
8010
+ artifactLinks: z.enum(["git", "local"]).default("git")
8011
+ }).prefault({});
8012
+ var RunSchema = z.object({
8013
+ autoImpact: z.object({
8014
+ enabled: z.boolean().default(true),
8015
+ threshold: z.number().nonnegative().default(8)
8016
+ }).prefault({})
8017
+ }).prefault({});
8018
+ var XeraConfigSchema = z.object({
8019
+ jira: JiraSchema,
8020
+ web: WebSchema.optional(),
8021
+ http: HttpSchema.optional(),
8022
+ ai: AISchema,
8023
+ reporting: ReportingSchema,
8024
+ run: RunSchema.prefault({}),
8025
+ adapters: z.array(z.enum(["web", "http"])).min(1).default(["web"])
8026
+ }).refine((c) => c.web !== undefined || c.http !== undefined, {
8027
+ message: "At least one of `web` or `http` must be configured"
8028
+ }).refine((c) => c.adapters.every((a) => (a === "web" ? c.web : c.http) !== undefined), {
8029
+ message: "Every adapter in `adapters` must have a corresponding config block",
8030
+ path: ["adapters"]
8031
+ });
8032
+
8033
+ // src/config/load.ts
8034
+ async function loadConfig(cwd) {
8035
+ const path = join(cwd, "xera.config.ts");
8036
+ if (!existsSync(path)) {
8037
+ throw new Error(`xera.config.ts not found in ${cwd}`);
8038
+ }
8039
+ const mod = await import(pathToFileURL(path).href);
8040
+ const raw = mod.default ?? mod;
8041
+ return XeraConfigSchema.parse(raw);
8042
+ }
8043
+
8044
+ // src/bin-internal/auth-setup.ts
8045
+ function parseOpts(argv) {
8046
+ const opts = { shape: "all" };
8047
+ for (let i = 0;i < argv.length; i++) {
8048
+ const a = argv[i];
8049
+ const next = argv[i + 1];
8050
+ if (a === "--role" && next) {
8051
+ opts.role = next;
8052
+ i++;
8053
+ } else if (a === "--shape" && next) {
8054
+ if (next === "web" || next === "http" || next === "all")
8055
+ opts.shape = next;
8056
+ i++;
8057
+ }
8058
+ }
8059
+ return opts;
8060
+ }
8061
+ async function authSetupCmd(argv) {
8062
+ const opts = parseOpts(argv);
8063
+ const cwd = process.cwd();
8064
+ const config = await loadConfig(cwd);
8065
+ const authSetupScript = join2(cwd, "shared", "auth-setup.ts");
8066
+ if (!existsSync2(authSetupScript)) {
8067
+ console.error(`[xera:auth-setup] auth-setup.ts not found at ${authSetupScript}. Run 'bunx @xera-ai/cli init' first.`);
8068
+ return 1;
8069
+ }
8070
+ const mod = await import(pathToFileURL2(authSetupScript).href);
8071
+ let exitCode = 0;
8072
+ if ((opts.shape === "all" || opts.shape === "web") && config.web && typeof mod.web === "function") {
8073
+ const { runAuthSetup } = await import("@xera-ai/web");
8074
+ const { chromium } = await import("@playwright/test");
8075
+ const browser = await chromium.launch();
8076
+ try {
8077
+ for (const [roleName, roleCreds] of Object.entries(config.web.auth.roles)) {
8078
+ if (opts.role && roleName !== opts.role)
8079
+ continue;
8080
+ const email = process.env[roleCreds.envEmail];
8081
+ const password = process.env[roleCreds.envPassword];
8082
+ if (!email || !password) {
8083
+ console.error(`[xera:auth-setup] missing env vars ${roleCreds.envEmail} / ${roleCreds.envPassword} for role '${roleName}'`);
8084
+ exitCode = 1;
8085
+ continue;
8086
+ }
8087
+ try {
8088
+ await runAuthSetup({
8089
+ role: roleName,
8090
+ creds: { email, password },
8091
+ setupScriptPath: authSetupScript,
8092
+ authDir: join2(cwd, ".xera", ".auth"),
8093
+ browser
8094
+ });
8095
+ console.log(`[xera:auth-setup] \u2713 ${roleName}.json (web)`);
8096
+ } catch (e) {
8097
+ console.error(`[xera:auth-setup] \u2717 web/${roleName}: ${e.message}`);
8098
+ exitCode = 1;
8099
+ }
8100
+ }
8101
+ } finally {
8102
+ await browser.close();
8103
+ }
8104
+ }
8105
+ if ((opts.shape === "all" || opts.shape === "http") && config.http && typeof mod.http === "function") {
8106
+ globalThis.__XERA_HTTP_CONFIG__ = config.http;
8107
+ const { runHttpAuthSetup } = await import("@xera-ai/http");
8108
+ for (const roleName of Object.keys(config.http.auth.roles)) {
8109
+ if (opts.role && roleName !== opts.role)
8110
+ continue;
8111
+ try {
8112
+ await runHttpAuthSetup({
8113
+ authDir: join2(cwd, ".xera", ".auth"),
8114
+ role: roleName,
8115
+ config: config.http,
8116
+ setupFn: mod.http,
8117
+ creds: { email: "", password: "" }
8118
+ });
8119
+ console.log(`[xera:auth-setup] \u2713 http/${roleName}.json`);
8120
+ } catch (e) {
8121
+ console.error(`[xera:auth-setup] \u2717 http/${roleName}: ${e.message}`);
8122
+ exitCode = 1;
8123
+ }
8124
+ }
8125
+ }
8126
+ return exitCode;
8127
+ }
8128
+
7917
8129
  // src/bin-internal/disputes.ts
7918
8130
  init_store();
7919
8131
  function parseDuration(s) {
@@ -7990,16 +8202,16 @@ async function disputesCmd(argv) {
7990
8202
  }
7991
8203
 
7992
8204
  // src/bin-internal/doctor.ts
7993
- import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync6 } from "fs";
7994
- import { join as join6 } from "path";
8205
+ import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync6 } from "fs";
8206
+ import { join as join8 } from "path";
7995
8207
 
7996
8208
  // src/graph/cost.ts
7997
- import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2 } from "fs";
8209
+ import { appendFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2 } from "fs";
7998
8210
  init_paths();
7999
8211
  function summarizeCost(repoRoot, daysBack) {
8000
8212
  const paths = graphPaths(repoRoot);
8001
8213
  const result = { totalCalls: 0, totalUsd: 0, bySkill: {}, windowDays: daysBack };
8002
- if (!existsSync2(paths.costLog))
8214
+ if (!existsSync4(paths.costLog))
8003
8215
  return result;
8004
8216
  const cutoff = Date.now() - daysBack * 86400 * 1000;
8005
8217
  for (const line of readFileSync2(paths.costLog, "utf8").split(`
@@ -8029,11 +8241,12 @@ function summarizeCost(repoRoot, daysBack) {
8029
8241
  init_store();
8030
8242
 
8031
8243
  // src/bin-internal/verify-prompts.ts
8032
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
8033
- import { join as join2 } from "path";
8244
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
8245
+ import { join as join4 } from "path";
8034
8246
  var IN_SCOPE_PROMPTS = [
8035
8247
  "feature-from-story.md",
8036
- "script-from-feature.md",
8248
+ "script-from-feature-web.md",
8249
+ "script-from-feature-http.md",
8037
8250
  "heal-locator.md",
8038
8251
  "extract-areas.md",
8039
8252
  "similarity-match.md",
@@ -8042,11 +8255,11 @@ var IN_SCOPE_PROMPTS = [
8042
8255
  var REQUIRED_SECTION_HEADING = "## Handling untrusted input";
8043
8256
  var REQUIRED_KEYWORDS = ["UNTRUSTED", "injection-follow", "<XR_"];
8044
8257
  function verifyPrompts(repoRoot) {
8045
- const promptsDir = join2(repoRoot, "packages/prompts");
8258
+ const promptsDir = join4(repoRoot, "packages/prompts");
8046
8259
  const results = [];
8047
8260
  for (const filename of IN_SCOPE_PROMPTS) {
8048
- const path = join2(promptsDir, filename);
8049
- if (!existsSync3(path)) {
8261
+ const path = join4(promptsDir, filename);
8262
+ if (!existsSync5(path)) {
8050
8263
  results.push({
8051
8264
  ok: false,
8052
8265
  message: `${filename}: file missing at packages/prompts/${filename}`
@@ -8101,8 +8314,8 @@ function frontmatterField(content, field) {
8101
8314
  return m?.[1] ?? null;
8102
8315
  }
8103
8316
  function checkGoldenEvalDir(repoRoot) {
8104
- const root = join6(repoRoot, "fixtures/golden-eval");
8105
- if (!existsSync7(root))
8317
+ const root = join8(repoRoot, "fixtures/golden-eval");
8318
+ if (!existsSync9(root))
8106
8319
  return [{ ok: false, message: "fixtures/golden-eval/ does not exist" }];
8107
8320
  const dirs = readdirSync4(root, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("."));
8108
8321
  const results = [];
@@ -8113,9 +8326,9 @@ function checkGoldenEvalDir(repoRoot) {
8113
8326
  });
8114
8327
  }
8115
8328
  for (const entry of dirs) {
8116
- const dir = join6(root, entry.name);
8117
- const metaPath = join6(dir, "meta.json");
8118
- if (!existsSync7(metaPath)) {
8329
+ const dir = join8(root, entry.name);
8330
+ const metaPath = join8(dir, "meta.json");
8331
+ if (!existsSync9(metaPath)) {
8119
8332
  results.push({ ok: false, message: `${entry.name}: meta.json missing` });
8120
8333
  continue;
8121
8334
  }
@@ -8132,12 +8345,12 @@ function checkGoldenEvalDir(repoRoot) {
8132
8345
  const stages = Array.isArray(meta.stages) ? meta.stages : [];
8133
8346
  if (stages.length === 0)
8134
8347
  results.push({ ok: false, message: `${entry.name}: meta.stages is empty` });
8135
- if (!existsSync7(join6(dir, "story.md")))
8348
+ if (!existsSync9(join8(dir, "story.md")))
8136
8349
  results.push({ ok: false, message: `${entry.name}: story.md missing` });
8137
8350
  for (const stage of stages) {
8138
8351
  const required = REQUIRED_FILES_PER_STAGE[stage] ?? [];
8139
8352
  for (const rel of required) {
8140
- if (!existsSync7(join6(dir, rel))) {
8353
+ if (!existsSync9(join8(dir, rel))) {
8141
8354
  results.push({
8142
8355
  ok: false,
8143
8356
  message: `${meta.id ?? entry.name}: stage "${stage}" declared but ${rel} missing`
@@ -8149,8 +8362,8 @@ function checkGoldenEvalDir(repoRoot) {
8149
8362
  return results;
8150
8363
  }
8151
8364
  function checkRubricPrompt(repoRoot) {
8152
- const path = join6(repoRoot, "packages/prompts/eval-rubric.md");
8153
- if (!existsSync7(path))
8365
+ const path = join8(repoRoot, "packages/prompts/eval-rubric.md");
8366
+ if (!existsSync9(path))
8154
8367
  return [{ ok: false, message: "packages/prompts/eval-rubric.md missing" }];
8155
8368
  const text = readFileSync6(path, "utf8");
8156
8369
  const id = frontmatterField(text, "id");
@@ -8162,8 +8375,8 @@ function checkRubricPrompt(repoRoot) {
8162
8375
  return [];
8163
8376
  }
8164
8377
  function checkEvalSkill(repoRoot) {
8165
- const path = join6(repoRoot, "packages/skills/xera-eval.md");
8166
- if (!existsSync7(path))
8378
+ const path = join8(repoRoot, "packages/skills/xera-eval.md");
8379
+ if (!existsSync9(path))
8167
8380
  return [{ ok: false, message: "packages/skills/xera-eval.md missing" }];
8168
8381
  const text = readFileSync6(path, "utf8");
8169
8382
  if (!frontmatterField(text, "name"))
@@ -8174,8 +8387,8 @@ function checkPromptInjectionPreamble(repoRoot) {
8174
8387
  return verifyPrompts(repoRoot);
8175
8388
  }
8176
8389
  function checkRootScripts(repoRoot) {
8177
- const path = join6(repoRoot, "package.json");
8178
- if (!existsSync7(path))
8390
+ const path = join8(repoRoot, "package.json");
8391
+ if (!existsSync9(path))
8179
8392
  return [{ ok: false, message: "root package.json missing" }];
8180
8393
  const pkg = JSON.parse(readFileSync6(path, "utf8"));
8181
8394
  const scripts = pkg.scripts ?? {};
@@ -8202,8 +8415,8 @@ async function doctorCmd(argv, opts = {}) {
8202
8415
  if (top)
8203
8416
  console.log(` Top skill: ${top[0]} (${top[1].calls} calls, $${top[1].usd.toFixed(2)})`);
8204
8417
  }
8205
- const xeraDir = join6(repoRoot, ".xera");
8206
- if (existsSync7(xeraDir)) {
8418
+ const xeraDir = join8(repoRoot, ".xera");
8419
+ if (existsSync9(xeraDir)) {
8207
8420
  const ticketDirs = readdirSync4(xeraDir, { withFileTypes: true }).filter((e) => e.isDirectory() && /^[A-Z]+-\d+$/.test(e.name));
8208
8421
  if (ticketDirs.length > 0) {
8209
8422
  const events = loadAllEvents(repoRoot);
@@ -8238,111 +8451,111 @@ async function doctorCmd(argv, opts = {}) {
8238
8451
  }
8239
8452
 
8240
8453
  // src/bin-internal/eval-deterministic.ts
8241
- import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "fs";
8242
- import { join as join8 } from "path";
8454
+ import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "fs";
8455
+ import { join as join10 } from "path";
8243
8456
  import { validateGherkin } from "@xera-ai/web";
8244
8457
 
8245
8458
  // src/eval/paths.ts
8246
- import { join as join7 } from "path";
8459
+ import { join as join9 } from "path";
8247
8460
  function resolveEvalPaths(cwd, runId) {
8248
- const root = join7(cwd, ".xera", "eval", runId);
8461
+ const root = join9(cwd, ".xera", "eval", runId);
8249
8462
  return {
8250
8463
  root,
8251
- manifest: join7(root, "manifest.json"),
8252
- lock: join7(root, ".lock"),
8253
- deterministicScores: join7(root, "deterministic-scores.json"),
8254
- judgeScores: join7(root, "judge-scores.json"),
8255
- report: join7(root, "report.md"),
8256
- summary: join7(root, "summary.json"),
8257
- inputsDir: join7(root, "inputs"),
8258
- actualDir: join7(root, "actual"),
8259
- ticketInputsDir: (ticket) => join7(root, "inputs", ticket),
8260
- ticketActualDir: (ticket) => join7(root, "actual", ticket)
8464
+ manifest: join9(root, "manifest.json"),
8465
+ lock: join9(root, ".lock"),
8466
+ deterministicScores: join9(root, "deterministic-scores.json"),
8467
+ judgeScores: join9(root, "judge-scores.json"),
8468
+ report: join9(root, "report.md"),
8469
+ summary: join9(root, "summary.json"),
8470
+ inputsDir: join9(root, "inputs"),
8471
+ actualDir: join9(root, "actual"),
8472
+ ticketInputsDir: (ticket) => join9(root, "inputs", ticket),
8473
+ ticketActualDir: (ticket) => join9(root, "actual", ticket)
8261
8474
  };
8262
8475
  }
8263
8476
 
8264
8477
  // src/eval/types.ts
8265
- import { z as z2 } from "zod";
8478
+ import { z as z3 } from "zod";
8266
8479
  var STAGES = ["feature-from-story", "script-from-feature", "diagnose-failure"];
8267
- var StageSchema = z2.enum(STAGES);
8268
- var VerdictSchema = z2.enum(["PASS", "FAIL", "NA"]);
8269
- var PromptVersionsSchema = z2.object({
8270
- "feature-from-story": z2.string(),
8271
- "script-from-feature": z2.string(),
8272
- "diagnose-failure": z2.string(),
8273
- "eval-rubric": z2.string()
8480
+ var StageSchema = z3.enum(STAGES);
8481
+ var VerdictSchema = z3.enum(["PASS", "FAIL", "NA"]);
8482
+ var PromptVersionsSchema = z3.object({
8483
+ "feature-from-story": z3.string(),
8484
+ "script-from-feature": z3.string(),
8485
+ "diagnose-failure": z3.string(),
8486
+ "eval-rubric": z3.string()
8274
8487
  });
8275
- var ManifestSchema = z2.object({
8276
- run_id: z2.string(),
8277
- started_at: z2.string(),
8278
- git_sha: z2.string(),
8279
- tickets: z2.array(z2.string()).min(1),
8280
- stages: z2.array(StageSchema).min(1),
8281
- ticket_stages: z2.record(z2.string(), z2.array(StageSchema).min(1)),
8488
+ var ManifestSchema = z3.object({
8489
+ run_id: z3.string(),
8490
+ started_at: z3.string(),
8491
+ git_sha: z3.string(),
8492
+ tickets: z3.array(z3.string()).min(1),
8493
+ stages: z3.array(StageSchema).min(1),
8494
+ ticket_stages: z3.record(z3.string(), z3.array(StageSchema).min(1)),
8282
8495
  prompt_versions: PromptVersionsSchema,
8283
- flags: z2.object({
8284
- force: z2.boolean(),
8496
+ flags: z3.object({
8497
+ force: z3.boolean(),
8285
8498
  only_prompt: StageSchema.nullable(),
8286
- only_ticket: z2.string().nullable(),
8287
- judge_only: z2.boolean()
8499
+ only_ticket: z3.string().nullable(),
8500
+ judge_only: z3.boolean()
8288
8501
  })
8289
8502
  });
8290
- var DimensionSchema = z2.object({
8291
- name: z2.string(),
8503
+ var DimensionSchema = z3.object({
8504
+ name: z3.string(),
8292
8505
  verdict: VerdictSchema,
8293
- notes: z2.string()
8506
+ notes: z3.string()
8294
8507
  });
8295
- var JudgmentSchema = z2.object({
8508
+ var JudgmentSchema = z3.object({
8296
8509
  stage: StageSchema,
8297
- ticket: z2.string(),
8298
- dimensions: z2.array(DimensionSchema).min(1)
8510
+ ticket: z3.string(),
8511
+ dimensions: z3.array(DimensionSchema).min(1)
8299
8512
  });
8300
- var JudgeScoresSchema = z2.object({
8301
- run_id: z2.string(),
8302
- judgments: z2.array(JudgmentSchema)
8513
+ var JudgeScoresSchema = z3.object({
8514
+ run_id: z3.string(),
8515
+ judgments: z3.array(JudgmentSchema)
8303
8516
  });
8304
- var DeterministicEntrySchema = z2.object({
8305
- ticket: z2.string(),
8517
+ var DeterministicEntrySchema = z3.object({
8518
+ ticket: z3.string(),
8306
8519
  stage: StageSchema,
8307
- passed: z2.boolean(),
8308
- checks: z2.array(z2.string()),
8309
- error: z2.string().optional()
8520
+ passed: z3.boolean(),
8521
+ checks: z3.array(z3.string()),
8522
+ error: z3.string().optional()
8310
8523
  });
8311
- var DeterministicScoresSchema = z2.object({
8312
- run_id: z2.string(),
8313
- entries: z2.array(DeterministicEntrySchema)
8524
+ var DeterministicScoresSchema = z3.object({
8525
+ run_id: z3.string(),
8526
+ entries: z3.array(DeterministicEntrySchema)
8314
8527
  });
8315
- var ResultSchema = z2.object({
8316
- ticket: z2.string(),
8528
+ var ResultSchema = z3.object({
8529
+ ticket: z3.string(),
8317
8530
  stage: StageSchema,
8318
- deterministic: z2.object({
8319
- passed: z2.boolean(),
8320
- checks: z2.array(z2.string()),
8321
- error: z2.string().optional()
8531
+ deterministic: z3.object({
8532
+ passed: z3.boolean(),
8533
+ checks: z3.array(z3.string()),
8534
+ error: z3.string().optional()
8322
8535
  }),
8323
- judge: z2.object({
8324
- passed: z2.boolean(),
8325
- dimensions: z2.array(DimensionSchema),
8326
- score: z2.number().min(0).max(1)
8536
+ judge: z3.object({
8537
+ passed: z3.boolean(),
8538
+ dimensions: z3.array(DimensionSchema),
8539
+ score: z3.number().min(0).max(1)
8327
8540
  }).nullable(),
8328
- skipped: z2.boolean().optional()
8541
+ skipped: z3.boolean().optional()
8329
8542
  });
8330
- var SummarySchema = z2.object({
8331
- run_id: z2.string(),
8332
- git_sha: z2.string(),
8543
+ var SummarySchema = z3.object({
8544
+ run_id: z3.string(),
8545
+ git_sha: z3.string(),
8333
8546
  prompt_versions: PromptVersionsSchema,
8334
- results: z2.array(ResultSchema),
8335
- overall: z2.object({
8336
- passed: z2.number().int().nonnegative(),
8337
- failed: z2.number().int().nonnegative(),
8338
- total: z2.number().int().nonnegative(),
8339
- score: z2.number().min(0).max(1)
8547
+ results: z3.array(ResultSchema),
8548
+ overall: z3.object({
8549
+ passed: z3.number().int().nonnegative(),
8550
+ failed: z3.number().int().nonnegative(),
8551
+ total: z3.number().int().nonnegative(),
8552
+ score: z3.number().min(0).max(1)
8340
8553
  })
8341
8554
  });
8342
8555
 
8343
8556
  // src/bin-internal/eval-deterministic.ts
8344
8557
  function checkFeatureFromStory(actualFeaturePath) {
8345
- if (!existsSync8(actualFeaturePath)) {
8558
+ if (!existsSync10(actualFeaturePath)) {
8346
8559
  return { passed: false, checks: ["validate-feature"], error: "actual missing: test.feature" };
8347
8560
  }
8348
8561
  try {
@@ -8359,23 +8572,23 @@ function checkFeatureFromStory(actualFeaturePath) {
8359
8572
  }
8360
8573
  }
8361
8574
  function checkScriptFromFeature(actualTicketDir) {
8362
- const specPath = join8(actualTicketDir, "spec.ts");
8363
- if (!existsSync8(specPath)) {
8575
+ const specPath = join10(actualTicketDir, "spec.ts");
8576
+ if (!existsSync10(specPath)) {
8364
8577
  return { passed: false, checks: ["file-presence"], error: "actual missing: spec.ts" };
8365
8578
  }
8366
8579
  return { passed: true, checks: ["file-presence"] };
8367
8580
  }
8368
8581
  function checkDiagnoseFailure(inputsTicketDir, actualTicketDir) {
8369
- const inputPath = join8(inputsTicketDir, "classifier-input.json");
8370
- const actualPath = join8(actualTicketDir, "classification.json");
8371
- if (!existsSync8(actualPath)) {
8582
+ const inputPath = join10(inputsTicketDir, "classifier-input.json");
8583
+ const actualPath = join10(actualTicketDir, "classification.json");
8584
+ if (!existsSync10(actualPath)) {
8372
8585
  return {
8373
8586
  passed: false,
8374
8587
  checks: ["bucket-match"],
8375
8588
  error: "actual missing: classification.json"
8376
8589
  };
8377
8590
  }
8378
- if (!existsSync8(inputPath)) {
8591
+ if (!existsSync10(inputPath)) {
8379
8592
  return {
8380
8593
  passed: false,
8381
8594
  checks: ["bucket-match"],
@@ -8413,7 +8626,7 @@ async function evalDeterministicCmd(argv, opts = {}) {
8413
8626
  return 1;
8414
8627
  }
8415
8628
  const paths = resolveEvalPaths(cwd, runId);
8416
- if (!existsSync8(paths.manifest)) {
8629
+ if (!existsSync10(paths.manifest)) {
8417
8630
  console.error(`[xera:eval-deterministic] missing manifest.json at ${paths.manifest}`);
8418
8631
  return 1;
8419
8632
  }
@@ -8425,7 +8638,7 @@ async function evalDeterministicCmd(argv, opts = {}) {
8425
8638
  const actualDir = paths.ticketActualDir(ticket);
8426
8639
  let result;
8427
8640
  if (stage === "feature-from-story") {
8428
- result = checkFeatureFromStory(join8(actualDir, "test.feature"));
8641
+ result = checkFeatureFromStory(join10(actualDir, "test.feature"));
8429
8642
  } else if (stage === "script-from-feature") {
8430
8643
  result = checkScriptFromFeature(actualDir);
8431
8644
  } else {
@@ -8452,13 +8665,13 @@ async function evalDeterministicCmd(argv, opts = {}) {
8452
8665
  // src/bin-internal/eval-prepare.ts
8453
8666
  import {
8454
8667
  copyFileSync,
8455
- existsSync as existsSync10,
8668
+ existsSync as existsSync12,
8456
8669
  mkdirSync as mkdirSync4,
8457
8670
  readdirSync as readdirSync5,
8458
8671
  readFileSync as readFileSync9,
8459
8672
  writeFileSync as writeFileSync4
8460
8673
  } from "fs";
8461
- import { join as join9 } from "path";
8674
+ import { join as join11 } from "path";
8462
8675
 
8463
8676
  // src/eval/run-id.ts
8464
8677
  import { execSync } from "child_process";
@@ -8483,11 +8696,11 @@ function generateRunId(opts = {}) {
8483
8696
  }
8484
8697
 
8485
8698
  // src/lock/file-lock.ts
8486
- import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync8, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
8699
+ import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync8, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
8487
8700
  import { hostname } from "os";
8488
8701
  import { dirname as dirname2 } from "path";
8489
8702
  function acquireLock(path, runId) {
8490
- if (existsSync9(path))
8703
+ if (existsSync11(path))
8491
8704
  return false;
8492
8705
  mkdirSync3(dirname2(path), { recursive: true });
8493
8706
  const data = {
@@ -8504,11 +8717,11 @@ function acquireLock(path, runId) {
8504
8717
  }
8505
8718
  }
8506
8719
  function releaseLock(path) {
8507
- if (existsSync9(path))
8720
+ if (existsSync11(path))
8508
8721
  unlinkSync(path);
8509
8722
  }
8510
8723
  function readLock(path) {
8511
- if (!existsSync9(path))
8724
+ if (!existsSync11(path))
8512
8725
  return null;
8513
8726
  return JSON.parse(readFileSync8(path, "utf8"));
8514
8727
  }
@@ -8551,16 +8764,16 @@ function parseFlags2(argv) {
8551
8764
  return flags;
8552
8765
  }
8553
8766
  function readPromptVersion(repoRoot, name) {
8554
- const path = join9(repoRoot, "packages/prompts", `${name}.md`);
8555
- if (!existsSync10(path))
8767
+ const path = join11(repoRoot, "packages/prompts", `${name}.md`);
8768
+ if (!existsSync12(path))
8556
8769
  return "0.0.0";
8557
8770
  const text = readFileSync9(path, "utf8");
8558
8771
  const m = /^version:\s*(\S+)\s*$/m.exec(text);
8559
8772
  return m?.[1] ?? "0.0.0";
8560
8773
  }
8561
8774
  function discoverEvalTickets(repoRoot) {
8562
- const root = join9(repoRoot, "fixtures/golden-eval");
8563
- if (!existsSync10(root))
8775
+ const root = join11(repoRoot, "fixtures/golden-eval");
8776
+ if (!existsSync12(root))
8564
8777
  return [];
8565
8778
  const out = [];
8566
8779
  for (const entry of readdirSync5(root, { withFileTypes: true })) {
@@ -8568,9 +8781,9 @@ function discoverEvalTickets(repoRoot) {
8568
8781
  continue;
8569
8782
  if (entry.name === "README.md" || entry.name.startsWith("."))
8570
8783
  continue;
8571
- const dir = join9(root, entry.name);
8572
- const metaPath = join9(dir, "meta.json");
8573
- if (!existsSync10(metaPath))
8784
+ const dir = join11(root, entry.name);
8785
+ const metaPath = join11(dir, "meta.json");
8786
+ if (!existsSync12(metaPath))
8574
8787
  continue;
8575
8788
  const meta = JSON.parse(readFileSync9(metaPath, "utf8"));
8576
8789
  out.push({ id: meta.id, dir, stages: meta.stages });
@@ -8578,14 +8791,14 @@ function discoverEvalTickets(repoRoot) {
8578
8791
  return out.sort((a, b) => a.id.localeCompare(b.id));
8579
8792
  }
8580
8793
  function discoverClassifierTickets(repoRoot) {
8581
- const root = join9(repoRoot, "fixtures/golden-tickets");
8582
- if (!existsSync10(root))
8794
+ const root = join11(repoRoot, "fixtures/golden-tickets");
8795
+ if (!existsSync12(root))
8583
8796
  return [];
8584
8797
  const out = [];
8585
8798
  for (const entry of readdirSync5(root, { withFileTypes: true })) {
8586
8799
  if (!entry.isFile() || !entry.name.endsWith(".json"))
8587
8800
  continue;
8588
- const path = join9(root, entry.name);
8801
+ const path = join11(root, entry.name);
8589
8802
  const data = JSON.parse(readFileSync9(path, "utf8"));
8590
8803
  if (typeof data.ticket === "string")
8591
8804
  out.push({ id: data.ticket, path });
@@ -8645,7 +8858,7 @@ async function evalPrepareCmd(argv, opts = {}) {
8645
8858
  ...opts.getGitSha ? { getGitSha: opts.getGitSha } : {}
8646
8859
  });
8647
8860
  const paths = resolveEvalPaths(repoRoot, runId);
8648
- if (existsSync10(paths.root) && !flags.force) {
8861
+ if (existsSync12(paths.root) && !flags.force) {
8649
8862
  console.error(`[xera:eval-prepare] run dir already exists: ${paths.root}. Pass --force to re-run.`);
8650
8863
  return 1;
8651
8864
  }
@@ -8657,13 +8870,13 @@ async function evalPrepareCmd(argv, opts = {}) {
8657
8870
  const evalT = evalTickets.find((t) => t.id === ticket);
8658
8871
  const classT = classifierTickets.find((t) => t.id === ticket);
8659
8872
  if (evalT) {
8660
- copyFileSync(join9(evalT.dir, "story.md"), join9(ticketInputs, "story.md"));
8661
- const featurePath = join9(evalT.dir, "golden/test.feature");
8662
- if (existsSync10(featurePath))
8663
- copyFileSync(featurePath, join9(ticketInputs, "test.feature"));
8873
+ copyFileSync(join11(evalT.dir, "story.md"), join11(ticketInputs, "story.md"));
8874
+ const featurePath = join11(evalT.dir, "golden/test.feature");
8875
+ if (existsSync12(featurePath))
8876
+ copyFileSync(featurePath, join11(ticketInputs, "test.feature"));
8664
8877
  }
8665
8878
  if (classT) {
8666
- copyFileSync(classT.path, join9(ticketInputs, "classifier-input.json"));
8879
+ copyFileSync(classT.path, join11(ticketInputs, "classifier-input.json"));
8667
8880
  }
8668
8881
  }
8669
8882
  const now = (opts.now ?? (() => new Date))();
@@ -8699,7 +8912,7 @@ async function evalPrepareCmd(argv, opts = {}) {
8699
8912
  }
8700
8913
 
8701
8914
  // src/bin-internal/eval-report.ts
8702
- import { existsSync as existsSync11, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
8915
+ import { existsSync as existsSync13, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
8703
8916
  function scoreJudgment(j) {
8704
8917
  const nonNa = j.dimensions.filter((d) => d.verdict !== "NA");
8705
8918
  if (nonNa.length === 0)
@@ -8754,7 +8967,7 @@ async function evalReportCmd(argv, opts = {}) {
8754
8967
  return 1;
8755
8968
  }
8756
8969
  const paths = resolveEvalPaths(cwd, runId);
8757
- if (!existsSync11(paths.manifest)) {
8970
+ if (!existsSync13(paths.manifest)) {
8758
8971
  console.error(`[xera:eval-report] missing manifest.json at ${paths.manifest}`);
8759
8972
  return 1;
8760
8973
  }
@@ -8841,40 +9054,77 @@ async function evalReportCmd(argv, opts = {}) {
8841
9054
  }
8842
9055
 
8843
9056
  // src/bin-internal/exec.ts
8844
- import { existsSync as existsSync15, mkdirSync as mkdirSync7 } from "fs";
8845
- import { join as join13 } from "path";
9057
+ import { existsSync as existsSync17, mkdirSync as mkdirSync8 } from "fs";
9058
+ import { join as join14 } from "path";
8846
9059
  import { chromium } from "@playwright/test";
8847
9060
  import { runAuthSetup, runPlaywright, stagePlaywrightState } from "@xera-ai/web";
8848
9061
 
9062
+ // src/artifact/meta.ts
9063
+ import { existsSync as existsSync14, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "fs";
9064
+ import { dirname as dirname3 } from "path";
9065
+ import { z as z4 } from "zod";
9066
+ var MetaJsonSchema = z4.object({
9067
+ ticket: z4.string(),
9068
+ adapter: z4.string(),
9069
+ xera_version: z4.string(),
9070
+ prompts_version: z4.string(),
9071
+ fetched_at: z4.string().optional(),
9072
+ story_hash: z4.string().optional(),
9073
+ feature_generated_at: z4.string().optional(),
9074
+ feature_generated_from_story_hash: z4.string().optional(),
9075
+ feature_hash: z4.string().optional(),
9076
+ script_generated_at: z4.string().optional(),
9077
+ script_generated_from_feature_hash: z4.string().optional(),
9078
+ script_warnings: z4.array(z4.string()).optional()
9079
+ });
9080
+ function readMeta(path) {
9081
+ if (!existsSync14(path))
9082
+ return null;
9083
+ return MetaJsonSchema.parse(JSON.parse(readFileSync11(path, "utf8")));
9084
+ }
9085
+ function writeMeta(path, meta) {
9086
+ mkdirSync5(dirname3(path), { recursive: true });
9087
+ writeFileSync6(path, JSON.stringify(meta, null, 2));
9088
+ }
9089
+ function updateMeta(path, patch) {
9090
+ const existing = readMeta(path);
9091
+ if (!existing) {
9092
+ throw new Error(`meta.json not found at ${path}; cannot update`);
9093
+ }
9094
+ const next = { ...existing, ...patch };
9095
+ writeMeta(path, next);
9096
+ return next;
9097
+ }
9098
+
8849
9099
  // src/artifact/paths.ts
8850
- import { join as join10 } from "path";
9100
+ import { join as join12 } from "path";
8851
9101
  var TICKET_RE = /^[A-Z][A-Z0-9_]*-\d+$|^SAMPLE-\d+$/;
8852
9102
  function resolveArtifactPaths(repoRoot, ticket) {
8853
9103
  if (!TICKET_RE.test(ticket)) {
8854
9104
  throw new Error(`Invalid ticket key: "${ticket}" (expected e.g. JIRA-123 or SAMPLE-001)`);
8855
9105
  }
8856
- const ticketDir = join10(repoRoot, ".xera", ticket);
9106
+ const ticketDir = join12(repoRoot, ".xera", ticket);
8857
9107
  return {
8858
9108
  ticketDir,
8859
- storyPath: join10(ticketDir, "story.md"),
8860
- featurePath: join10(ticketDir, "test.feature"),
8861
- specPath: join10(ticketDir, "spec.ts"),
8862
- pageObjectsDir: join10(ticketDir, "page-objects"),
8863
- runsDir: join10(ticketDir, "runs"),
8864
- metaPath: join10(ticketDir, "meta.json"),
8865
- statusPath: join10(ticketDir, "status.json"),
8866
- logPath: join10(ticketDir, "xera.log"),
8867
- lockPath: join10(ticketDir, ".lock"),
8868
- authDir: join10(repoRoot, ".xera", ".auth"),
9109
+ storyPath: join12(ticketDir, "story.md"),
9110
+ featurePath: join12(ticketDir, "test.feature"),
9111
+ specPath: join12(ticketDir, "spec.ts"),
9112
+ pageObjectsDir: join12(ticketDir, "page-objects"),
9113
+ runsDir: join12(ticketDir, "runs"),
9114
+ metaPath: join12(ticketDir, "meta.json"),
9115
+ statusPath: join12(ticketDir, "status.json"),
9116
+ logPath: join12(ticketDir, "xera.log"),
9117
+ lockPath: join12(ticketDir, ".lock"),
9118
+ authDir: join12(repoRoot, ".xera", ".auth"),
8869
9119
  runPath: (runId) => {
8870
- const runDir = join10(ticketDir, "runs", runId);
9120
+ const runDir = join12(ticketDir, "runs", runId);
8871
9121
  return {
8872
9122
  runDir,
8873
- reportJsonPath: join10(runDir, "report.json"),
8874
- tracePath: join10(runDir, "trace.zip"),
8875
- normalizedPath: join10(runDir, "normalized.json"),
8876
- screenshotsDir: join10(runDir, "screenshots"),
8877
- videoDir: join10(runDir, "videos")
9123
+ reportJsonPath: join12(runDir, "report.json"),
9124
+ tracePath: join12(runDir, "trace.zip"),
9125
+ normalizedPath: join12(runDir, "normalized.json"),
9126
+ screenshotsDir: join12(runDir, "screenshots"),
9127
+ videoDir: join12(runDir, "videos")
8878
9128
  };
8879
9129
  }
8880
9130
  };
@@ -8912,9 +9162,9 @@ function needsRefresh(entry, policy, now = new Date) {
8912
9162
  }
8913
9163
 
8914
9164
  // src/auth/state.ts
8915
- import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "fs";
8916
- import { join as join11 } from "path";
8917
- import { z as z3 } from "zod";
9165
+ import { existsSync as existsSync15, mkdirSync as mkdirSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
9166
+ import { join as join13 } from "path";
9167
+ import { z as z5 } from "zod";
8918
9168
 
8919
9169
  // src/auth/encrypt.ts
8920
9170
  import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
@@ -8971,123 +9221,39 @@ function resolveAuthKey() {
8971
9221
  }
8972
9222
 
8973
9223
  // src/auth/state.ts
8974
- var AuthStateEntrySchema = z3.object({
8975
- role: z3.string(),
8976
- strategy: z3.enum(["storageState", "apiToken"]),
8977
- created_at: z3.string(),
8978
- expires_at: z3.string(),
8979
- payload: z3.record(z3.string(), z3.unknown())
9224
+ var AuthStateEntrySchema = z5.object({
9225
+ role: z5.string(),
9226
+ strategy: z5.enum(["storageState", "apiToken"]),
9227
+ created_at: z5.string(),
9228
+ expires_at: z5.string(),
9229
+ payload: z5.record(z5.string(), z5.unknown())
8980
9230
  });
8981
9231
  function pathFor(authDir, role) {
8982
- return join11(authDir, `${role}.json`);
9232
+ return join13(authDir, `${role}.json`);
8983
9233
  }
8984
9234
  function writeAuthState(authDir, entry) {
8985
- mkdirSync5(authDir, { recursive: true });
9235
+ mkdirSync6(authDir, { recursive: true });
8986
9236
  const ct = encrypt(JSON.stringify(entry), resolveAuthKey());
8987
- writeFileSync6(pathFor(authDir, entry.role), ct);
9237
+ writeFileSync7(pathFor(authDir, entry.role), ct);
8988
9238
  }
8989
9239
  function readAuthState(authDir, role) {
8990
9240
  const p = pathFor(authDir, role);
8991
- if (!existsSync12(p))
9241
+ if (!existsSync15(p))
8992
9242
  return null;
8993
- const txt = readFileSync11(p, "utf8");
9243
+ const txt = readFileSync12(p, "utf8");
8994
9244
  const plain = decrypt(txt, resolveAuthKey());
8995
9245
  return AuthStateEntrySchema.parse(JSON.parse(plain));
8996
9246
  }
8997
9247
 
8998
- // src/config/load.ts
8999
- import { existsSync as existsSync13 } from "fs";
9000
- import { join as join12 } from "path";
9001
- import { pathToFileURL } from "url";
9002
-
9003
- // src/config/schema.ts
9004
- import { z as z4 } from "zod";
9005
- var AuthRoleSchema = z4.object({
9006
- envEmail: z4.string().min(1),
9007
- envPassword: z4.string().min(1)
9008
- });
9009
- var AuthSchema = z4.object({
9010
- strategy: z4.enum(["storageState", "apiToken", "none"]).default("none"),
9011
- ttl: z4.string().default("8h"),
9012
- refreshBuffer: z4.string().default("30m"),
9013
- setupScript: z4.string().optional(),
9014
- roles: z4.record(z4.string(), AuthRoleSchema).default({})
9015
- });
9016
- var WebSchema = z4.object({
9017
- baseUrl: z4.record(z4.string(), z4.string().url()).refine((m) => Object.keys(m).length > 0, {
9018
- message: "baseUrl must have at least one environment"
9019
- }),
9020
- defaultEnv: z4.string(),
9021
- auth: AuthSchema.prefault({}),
9022
- testData: z4.object({
9023
- users: z4.record(z4.string(), z4.object({ fromAuth: z4.string() })).default({})
9024
- }).prefault({})
9025
- }).refine((w) => w.baseUrl[w.defaultEnv] !== undefined, {
9026
- message: "defaultEnv must exist in baseUrl map",
9027
- path: ["defaultEnv"]
9028
- });
9029
- var JiraSchema = z4.object({
9030
- baseUrl: z4.string().url(),
9031
- projectKeys: z4.array(z4.string().min(1)).min(1),
9032
- fields: z4.object({
9033
- story: z4.string().min(1),
9034
- acceptanceCriteria: z4.string().optional(),
9035
- attachments: z4.string().default("attachment")
9036
- })
9037
- });
9038
- var AISchema = z4.object({
9039
- livePageSnapshot: z4.boolean().default(true),
9040
- confidenceThreshold: z4.enum(["low", "medium", "high"]).default("medium"),
9041
- maxRetries: z4.object({
9042
- typecheck: z4.number().int().min(0).max(5).default(2),
9043
- lint: z4.number().int().min(0).max(5).default(2),
9044
- validateFeature: z4.number().int().min(0).max(5).default(2)
9045
- }).prefault({})
9046
- }).prefault({});
9047
- var ReportingSchema = z4.object({
9048
- language: z4.enum(["en", "vi"]).default("en"),
9049
- postToJira: z4.boolean().default(true),
9050
- transition: z4.object({
9051
- onPass: z4.string().nullable().default(null),
9052
- onFail: z4.string().nullable().default(null)
9053
- }).prefault({}),
9054
- artifactLinks: z4.enum(["git", "local"]).default("git")
9055
- }).prefault({});
9056
- var RunSchema = z4.object({
9057
- autoImpact: z4.object({
9058
- enabled: z4.boolean().default(true),
9059
- threshold: z4.number().nonnegative().default(8)
9060
- }).prefault({})
9061
- }).prefault({});
9062
- var XeraConfigSchema = z4.object({
9063
- jira: JiraSchema,
9064
- web: WebSchema,
9065
- ai: AISchema,
9066
- reporting: ReportingSchema,
9067
- run: RunSchema.prefault({}),
9068
- adapters: z4.array(z4.string().min(1)).min(1).default(["web"])
9069
- });
9070
-
9071
- // src/config/load.ts
9072
- async function loadConfig(cwd) {
9073
- const path = join12(cwd, "xera.config.ts");
9074
- if (!existsSync13(path)) {
9075
- throw new Error(`xera.config.ts not found in ${cwd}`);
9076
- }
9077
- const mod = await import(pathToFileURL(path).href);
9078
- const raw = mod.default ?? mod;
9079
- return XeraConfigSchema.parse(raw);
9080
- }
9081
-
9082
9248
  // src/logging/ndjson-logger.ts
9083
- import { appendFileSync as appendFileSync2, existsSync as existsSync14, mkdirSync as mkdirSync6, readFileSync as readFileSync12 } from "fs";
9084
- import { dirname as dirname3 } from "path";
9249
+ import { appendFileSync as appendFileSync2, existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync13 } from "fs";
9250
+ import { dirname as dirname4 } from "path";
9085
9251
 
9086
9252
  class NdjsonLogger {
9087
9253
  path;
9088
9254
  constructor(path) {
9089
9255
  this.path = path;
9090
- mkdirSync6(dirname3(path), { recursive: true });
9256
+ mkdirSync7(dirname4(path), { recursive: true });
9091
9257
  }
9092
9258
  log(payload) {
9093
9259
  const entry = { ts: new Date().toISOString(), ...payload };
@@ -9095,9 +9261,9 @@ class NdjsonLogger {
9095
9261
  `);
9096
9262
  }
9097
9263
  static readAll(path) {
9098
- if (!existsSync14(path))
9264
+ if (!existsSync16(path))
9099
9265
  return [];
9100
- const txt = readFileSync12(path, "utf8").trim();
9266
+ const txt = readFileSync13(path, "utf8").trim();
9101
9267
  if (!txt)
9102
9268
  return [];
9103
9269
  return txt.split(`
@@ -9132,14 +9298,41 @@ async function execCmd(argv) {
9132
9298
  }
9133
9299
  const t0 = Date.now();
9134
9300
  try {
9135
- if (config.web.auth.strategy === "storageState" && config.web.auth.setupScript) {
9301
+ const meta = readMeta(paths.metaPath);
9302
+ const adapter = meta?.adapter ?? "web";
9303
+ if (adapter === "http") {
9304
+ if (!config.http) {
9305
+ throw new Error("http adapter requires http config block");
9306
+ }
9307
+ const env = process.env["XERA_ENV"] ?? config.http.defaultEnv;
9308
+ const { HttpAdapter } = await import("@xera-ai/http");
9309
+ const result = await HttpAdapter.execute({
9310
+ ticketDir: paths.ticketDir,
9311
+ config,
9312
+ runId,
9313
+ env
9314
+ });
9315
+ log.log({
9316
+ step: "exec.complete",
9317
+ runId,
9318
+ outcome: result.outcome,
9319
+ elapsedMs: Date.now() - t0
9320
+ });
9321
+ console.log(`[xera:exec] runId=${runId} outcome=${result.outcome}`);
9322
+ return result.outcome === "PASS" ? 0 : 3;
9323
+ }
9324
+ if (!config.web) {
9325
+ throw new Error("web adapter requires web config block");
9326
+ }
9327
+ const webConfig = config.web;
9328
+ if (webConfig.auth.strategy === "storageState" && webConfig.auth.setupScript) {
9136
9329
  const browser = await chromium.launch();
9137
9330
  try {
9138
- for (const [roleName, roleCreds] of Object.entries(config.web.auth.roles)) {
9331
+ for (const [roleName, roleCreds] of Object.entries(webConfig.auth.roles)) {
9139
9332
  const entry = readAuthState(paths.authDir, roleName);
9140
9333
  if (needsRefresh(entry, {
9141
- ttl: config.web.auth.ttl,
9142
- refreshBuffer: config.web.auth.refreshBuffer
9334
+ ttl: webConfig.auth.ttl,
9335
+ refreshBuffer: webConfig.auth.refreshBuffer
9143
9336
  })) {
9144
9337
  const email = process.env[roleCreds.envEmail];
9145
9338
  const password = process.env[roleCreds.envPassword];
@@ -9150,7 +9343,7 @@ async function execCmd(argv) {
9150
9343
  await runAuthSetup({
9151
9344
  role: roleName,
9152
9345
  creds: { email, password },
9153
- setupScriptPath: join13(cwd, config.web.auth.setupScript),
9346
+ setupScriptPath: join14(cwd, webConfig.auth.setupScript),
9154
9347
  authDir: paths.authDir,
9155
9348
  browser
9156
9349
  });
@@ -9161,23 +9354,23 @@ async function execCmd(argv) {
9161
9354
  await browser.close();
9162
9355
  }
9163
9356
  }
9164
- if (config.web.auth.strategy === "storageState") {
9165
- for (const roleName of Object.keys(config.web.auth.roles)) {
9357
+ if (webConfig.auth.strategy === "storageState") {
9358
+ for (const roleName of Object.keys(webConfig.auth.roles)) {
9166
9359
  if (readAuthState(paths.authDir, roleName)) {
9167
9360
  stagePlaywrightState(paths.authDir, roleName);
9168
9361
  }
9169
9362
  }
9170
9363
  }
9171
- const cfgPath = join13(cwd, "playwright.config.ts");
9172
- if (!existsSync15(cfgPath)) {
9364
+ const cfgPath = join14(cwd, "playwright.config.ts");
9365
+ if (!existsSync17(cfgPath)) {
9173
9366
  console.error(`[xera:exec] missing ${cfgPath}. Run \`xera init\` to scaffold it, then re-run.`);
9174
9367
  return 1;
9175
9368
  }
9176
9369
  const runDir = paths.runPath(runId).runDir;
9177
- mkdirSync7(runDir, { recursive: true });
9178
- const envName = process.env.XERA_ENV ?? config.web.defaultEnv;
9179
- const baseURL = config.web.baseUrl[envName] ?? config.web.baseUrl[config.web.defaultEnv];
9180
- const reportJsonPath = join13(runDir, "report.json");
9370
+ mkdirSync8(runDir, { recursive: true });
9371
+ const envName = process.env.XERA_ENV ?? webConfig.defaultEnv;
9372
+ const baseURL = webConfig.baseUrl[envName] ?? webConfig.baseUrl[webConfig.defaultEnv];
9373
+ const reportJsonPath = join14(runDir, "report.json");
9181
9374
  log.log({ step: "exec.start", runId, env: envName, baseURL });
9182
9375
  const r = await runPlaywright({
9183
9376
  specPath: paths.specPath,
@@ -9204,83 +9397,46 @@ import { dirname as dirname5 } from "path";
9204
9397
 
9205
9398
  // src/artifact/hash.ts
9206
9399
  import { createHash as createHash4 } from "crypto";
9207
- import { existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
9400
+ import { existsSync as existsSync18, readFileSync as readFileSync14 } from "fs";
9208
9401
  function hashString(s) {
9209
9402
  return `sha256:${createHash4("sha256").update(s).digest("hex")}`;
9210
9403
  }
9211
9404
  function hashFile(path) {
9212
- return hashString(readFileSync13(path, "utf8"));
9405
+ return hashString(readFileSync14(path, "utf8"));
9213
9406
  }
9214
9407
  function hashFileIfExists(path) {
9215
- if (!existsSync16(path))
9408
+ if (!existsSync18(path))
9216
9409
  return null;
9217
9410
  return hashFile(path);
9218
9411
  }
9219
9412
 
9220
- // src/artifact/meta.ts
9221
- import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
9222
- import { dirname as dirname4 } from "path";
9223
- import { z as z5 } from "zod";
9224
- var MetaJsonSchema = z5.object({
9225
- ticket: z5.string(),
9226
- adapter: z5.string(),
9227
- xera_version: z5.string(),
9228
- prompts_version: z5.string(),
9229
- fetched_at: z5.string().optional(),
9230
- story_hash: z5.string().optional(),
9231
- feature_generated_at: z5.string().optional(),
9232
- feature_generated_from_story_hash: z5.string().optional(),
9233
- feature_hash: z5.string().optional(),
9234
- script_generated_at: z5.string().optional(),
9235
- script_generated_from_feature_hash: z5.string().optional(),
9236
- script_warnings: z5.array(z5.string()).optional()
9237
- });
9238
- function readMeta(path) {
9239
- if (!existsSync17(path))
9240
- return null;
9241
- return MetaJsonSchema.parse(JSON.parse(readFileSync14(path, "utf8")));
9242
- }
9243
- function writeMeta(path, meta) {
9244
- mkdirSync8(dirname4(path), { recursive: true });
9245
- writeFileSync7(path, JSON.stringify(meta, null, 2));
9246
- }
9247
- function updateMeta(path, patch) {
9248
- const existing = readMeta(path);
9249
- if (!existing) {
9250
- throw new Error(`meta.json not found at ${path}; cannot update`);
9251
- }
9252
- const next = { ...existing, ...patch };
9253
- writeMeta(path, next);
9254
- return next;
9255
- }
9256
-
9257
9413
  // src/jira/mcp-backend.ts
9258
- import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
9414
+ import { existsSync as existsSync19, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
9259
9415
  import { tmpdir } from "os";
9260
- import { join as join14 } from "path";
9416
+ import { join as join15 } from "path";
9261
9417
  var MCP_ENV = "XERA_MCP_JIRA";
9262
9418
  async function createMcpBackend(_baseUrl) {
9263
9419
  if (process.env[MCP_ENV] !== "1")
9264
9420
  return null;
9265
- const tmpDir = join14(tmpdir(), "xera-mcp");
9421
+ const tmpDir = join15(tmpdir(), "xera-mcp");
9266
9422
  mkdirSync9(tmpDir, { recursive: true });
9267
9423
  return {
9268
9424
  backend: "mcp",
9269
9425
  async fetchTicket(key, _fields) {
9270
- const cachePath = join14(tmpDir, `${key}.json`);
9271
- if (!existsSync18(cachePath)) {
9426
+ const cachePath = join15(tmpDir, `${key}.json`);
9427
+ if (!existsSync19(cachePath)) {
9272
9428
  throw new Error(`MCP-mode fetch requires the skill to first call mcp__atlassian__getJiraIssue and write ${cachePath}. ` + `If you are running this directly, unset ${MCP_ENV} to use REST.`);
9273
9429
  }
9274
9430
  const parsed = JSON.parse(readFileSync15(cachePath, "utf8"));
9275
9431
  return parsed;
9276
9432
  },
9277
9433
  async postComment(key, body) {
9278
- const outPath = join14(tmpDir, `${key}.comment.json`);
9434
+ const outPath = join15(tmpDir, `${key}.comment.json`);
9279
9435
  writeFileSync8(outPath, JSON.stringify({ key, body }));
9280
9436
  return { id: "mcp-pending" };
9281
9437
  },
9282
9438
  async transitionStatus(key, statusName) {
9283
- const outPath = join14(tmpDir, `${key}.transition.json`);
9439
+ const outPath = join15(tmpDir, `${key}.transition.json`);
9284
9440
  writeFileSync8(outPath, JSON.stringify({ key, statusName }));
9285
9441
  },
9286
9442
  async listFields(_sampleKey) {
@@ -9455,8 +9611,8 @@ init_graph_backfill();
9455
9611
  // src/graph/enrich.ts
9456
9612
  init_store();
9457
9613
  init_ulid();
9458
- import { existsSync as existsSync19, readFileSync as readFileSync16 } from "fs";
9459
- import { join as join15 } from "path";
9614
+ import { existsSync as existsSync20, readFileSync as readFileSync16 } from "fs";
9615
+ import { join as join16 } from "path";
9460
9616
  import { z as z6 } from "zod";
9461
9617
  var MAX_SIMILAR_EDGES = 10;
9462
9618
  var MIN_CONFIDENCE = 0.7;
@@ -9478,8 +9634,8 @@ var mk2 = (actor, type, payload) => ({
9478
9634
  payload
9479
9635
  });
9480
9636
  async function enrichTicket(repoRoot, ticketId, opts) {
9481
- const inputPath = join15(repoRoot, ".xera", ticketId, "enrichment-input.json");
9482
- if (!existsSync19(inputPath)) {
9637
+ const inputPath = join16(repoRoot, ".xera", ticketId, "enrichment-input.json");
9638
+ if (!existsSync20(inputPath)) {
9483
9639
  throw new Error(`enrichment-input.json not found at ${inputPath}`);
9484
9640
  }
9485
9641
  const raw = JSON.parse(readFileSync16(inputPath, "utf8"));
@@ -9592,11 +9748,11 @@ init_graph_record();
9592
9748
 
9593
9749
  // src/bin-internal/graph-render.ts
9594
9750
  import { mkdirSync as mkdirSync11, renameSync as renameSync2, writeFileSync as writeFileSync10 } from "fs";
9595
- import { dirname as dirname7, join as join17 } from "path";
9751
+ import { dirname as dirname7, join as join18 } from "path";
9596
9752
 
9597
9753
  // src/graph/render.ts
9598
9754
  import { readFileSync as readFileSync17 } from "fs";
9599
- import { dirname as dirname6, join as join16 } from "path";
9755
+ import { dirname as dirname6, join as join17 } from "path";
9600
9756
  import { fileURLToPath } from "url";
9601
9757
  var COLORS = {
9602
9758
  ticket: "#3B82F6",
@@ -9815,9 +9971,9 @@ function transformForVisNetwork(snap, opts) {
9815
9971
  }
9816
9972
  var __filename2 = fileURLToPath(import.meta.url);
9817
9973
  var __dirname2 = dirname6(__filename2);
9818
- var TEMPLATES_DIR = join16(__dirname2, "templates");
9974
+ var TEMPLATES_DIR = join17(__dirname2, "templates");
9819
9975
  function loadTemplate(name) {
9820
- return readFileSync17(join16(TEMPLATES_DIR, name), "utf8");
9976
+ return readFileSync17(join17(TEMPLATES_DIR, name), "utf8");
9821
9977
  }
9822
9978
  function statsToHuman(s) {
9823
9979
  return `${s.tickets} tickets \xB7 ${s.scenarios} scenarios \xB7 ${s.poms} POMs \xB7 ${s.edges} edges`;
@@ -9863,7 +10019,7 @@ async function graphRenderCmd(argv) {
9863
10019
  depth = parseDepth(argv[++i]);
9864
10020
  }
9865
10021
  const repoRoot = process.cwd();
9866
- const finalPath = outPath ?? join17(repoRoot, ".xera/graph.html");
10022
+ const finalPath = outPath ?? join18(repoRoot, ".xera/graph.html");
9867
10023
  const snap = deriveSnapshot(loadAllEvents(repoRoot));
9868
10024
  const totalNodeCount = Object.keys(snap.tickets).length + Object.keys(snap.scenarios).length + Object.keys(snap.poms).length + Object.keys(snap.areas).length;
9869
10025
  const performanceMode = decidePerformanceMode(totalNodeCount);
@@ -9915,11 +10071,11 @@ async function graphSnapshotCmd(argv) {
9915
10071
  }
9916
10072
 
9917
10073
  // src/bin-internal/heal-prepare.ts
9918
- import { existsSync as existsSync20, readdirSync as readdirSync6, readFileSync as readFileSync18, writeFileSync as writeFileSync11 } from "fs";
9919
- import { join as join18 } from "path";
10074
+ import { existsSync as existsSync21, readdirSync as readdirSync6, readFileSync as readFileSync18, writeFileSync as writeFileSync11 } from "fs";
10075
+ import { join as join19 } from "path";
9920
10076
  import { scrubFreeText } from "@xera-ai/web";
9921
10077
 
9922
- // ../../node_modules/.bun/fflate@0.8.3/node_modules/fflate/esm/index.mjs
10078
+ // ../../node_modules/fflate/esm/index.mjs
9923
10079
  import { createRequire } from "module";
9924
10080
  var require2 = createRequire("/");
9925
10081
  var _a;
@@ -10344,7 +10500,7 @@ function classifyKind(raw) {
10344
10500
  return "other";
10345
10501
  }
10346
10502
  function extractDomSnapshot(tracePath) {
10347
- if (!existsSync20(tracePath))
10503
+ if (!existsSync21(tracePath))
10348
10504
  return "";
10349
10505
  const buf = readFileSync18(tracePath);
10350
10506
  const entries = unzipSync(buf);
@@ -10394,12 +10550,12 @@ function extractDomSnapshot(tracePath) {
10394
10550
  return scrubFreeText(html);
10395
10551
  }
10396
10552
  function findPomLine(ticketDir, rawLocator) {
10397
- const pomDir = join18(ticketDir, "page-objects");
10553
+ const pomDir = join19(ticketDir, "page-objects");
10398
10554
  const candidates = [];
10399
- if (existsSync20(pomDir)) {
10555
+ if (existsSync21(pomDir)) {
10400
10556
  for (const name of readdirSync6(pomDir)) {
10401
10557
  if (name.endsWith(".ts"))
10402
- candidates.push(join18(pomDir, name));
10558
+ candidates.push(join19(pomDir, name));
10403
10559
  }
10404
10560
  }
10405
10561
  for (const file of candidates) {
@@ -10441,13 +10597,13 @@ function findGherkinStep(featureText, rawLocator) {
10441
10597
  }
10442
10598
  function healPrepare(repoRoot, ticket, runId, scenarioName) {
10443
10599
  const paths = resolveArtifactPaths(repoRoot, ticket);
10444
- const classifierPath = join18(paths.ticketDir, "classifier-input.json");
10600
+ const classifierPath = join19(paths.ticketDir, "classifier-input.json");
10445
10601
  const classifier = JSON.parse(readFileSync18(classifierPath, "utf8"));
10446
10602
  const cls = classifier.scenarios.find((s) => s.name === scenarioName);
10447
10603
  if (!cls)
10448
10604
  throw new Error(`scenario not found in classifier-input: "${scenarioName}"`);
10449
- const runDir = join18(paths.runsDir, runId);
10450
- const normalized = JSON.parse(readFileSync18(join18(runDir, "normalized.json"), "utf8"));
10605
+ const runDir = join19(paths.runsDir, runId);
10606
+ const normalized = JSON.parse(readFileSync18(join19(runDir, "normalized.json"), "utf8"));
10451
10607
  const normSc = normalized.scenarios.find((s) => s.name === scenarioName);
10452
10608
  if (!normSc?.failure)
10453
10609
  throw new Error(`no failure recorded for scenario "${scenarioName}"`);
@@ -10460,7 +10616,7 @@ function healPrepare(repoRoot, ticket, runId, scenarioName) {
10460
10616
  const pomLoc = findPomLine(paths.ticketDir, raw);
10461
10617
  const featureText = readFileSync18(paths.featurePath, "utf8");
10462
10618
  const gherkinStep = findGherkinStep(featureText, raw);
10463
- const domSnapshotAtFailure = extractDomSnapshot(join18(runDir, "trace.zip"));
10619
+ const domSnapshotAtFailure = extractDomSnapshot(join19(runDir, "trace.zip"));
10464
10620
  return {
10465
10621
  ticket,
10466
10622
  runId,
@@ -10480,7 +10636,7 @@ async function healPrepareCmd(argv) {
10480
10636
  try {
10481
10637
  const result = healPrepare(process.cwd(), ticket, runId, scenarioName);
10482
10638
  const paths = resolveArtifactPaths(process.cwd(), ticket);
10483
- const outPath = join18(paths.runsDir, runId, "heal-input.json");
10639
+ const outPath = join19(paths.runsDir, runId, "heal-input.json");
10484
10640
  writeFileSync11(outPath, JSON.stringify(result, null, 2));
10485
10641
  console.log(`[xera:heal-prepare] wrote ${outPath}`);
10486
10642
  return 0;
@@ -10492,7 +10648,7 @@ async function healPrepareCmd(argv) {
10492
10648
 
10493
10649
  // src/bin-internal/impact-prepare.ts
10494
10650
  import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync12 } from "fs";
10495
- import { join as join19 } from "path";
10651
+ import { join as join20 } from "path";
10496
10652
 
10497
10653
  // src/graph/impact.ts
10498
10654
  var PRIORITY_WEIGHT = { p0: 3, p1: 2, p2: 1 };
@@ -10742,11 +10898,11 @@ async function impactPrepareCmd(argv) {
10742
10898
  scenarios,
10743
10899
  generatedAt: new Date().toISOString()
10744
10900
  };
10745
- const impactDir = join19(repoRoot, ".xera/impact");
10901
+ const impactDir = join20(repoRoot, ".xera/impact");
10746
10902
  mkdirSync12(impactDir, { recursive: true });
10747
- writeFileSync12(join19(impactDir, `${ticket}.json`), JSON.stringify(report, null, 2));
10903
+ writeFileSync12(join20(impactDir, `${ticket}.json`), JSON.stringify(report, null, 2));
10748
10904
  if (!quiet) {
10749
- writeFileSync12(join19(impactDir, `${ticket}.md`), renderImpactMarkdown(report));
10905
+ writeFileSync12(join20(impactDir, `${ticket}.md`), renderImpactMarkdown(report));
10750
10906
  }
10751
10907
  return 0;
10752
10908
  }
@@ -10771,9 +10927,8 @@ async function lintCmd(argv) {
10771
10927
  }
10772
10928
 
10773
10929
  // src/bin-internal/normalize.ts
10774
- import { existsSync as existsSync21, readdirSync as readdirSync7 } from "fs";
10775
- import { join as join20 } from "path";
10776
- import { normalizeRun } from "@xera-ai/web";
10930
+ import { existsSync as existsSync22, readdirSync as readdirSync7 } from "fs";
10931
+ import { join as join21 } from "path";
10777
10932
  async function normalizeCmd(argv) {
10778
10933
  const ticket = argv[0];
10779
10934
  if (!ticket) {
@@ -10787,22 +10942,31 @@ async function normalizeCmd(argv) {
10787
10942
  console.error("[xera:normalize] no run found");
10788
10943
  return 1;
10789
10944
  }
10790
- const runDir = join20(paths.runsDir, runId);
10791
- if (!existsSync21(runDir)) {
10945
+ const runDir = join21(paths.runsDir, runId);
10946
+ if (!existsSync22(runDir)) {
10792
10947
  console.error(`[xera:normalize] runs/${runId} missing`);
10793
10948
  return 1;
10794
10949
  }
10950
+ const meta = readMeta(paths.metaPath);
10951
+ const adapter = meta?.adapter ?? "web";
10952
+ if (adapter === "http") {
10953
+ const { normalizeHttpRun } = await import("@xera-ai/http");
10954
+ await normalizeHttpRun({ runId, runDir });
10955
+ console.log(`[xera:normalize] wrote normalized.json (http)`);
10956
+ return 0;
10957
+ }
10958
+ const { normalizeRun } = await import("@xera-ai/web");
10795
10959
  const r = await normalizeRun({ runId, runDir });
10796
10960
  console.log(`[xera:normalize] wrote normalized.json (scrubbed_fields_count=${r.scrubbed_fields_count})`);
10797
10961
  return 0;
10798
10962
  }
10799
10963
 
10800
10964
  // src/bin-internal/post.ts
10801
- import { existsSync as existsSync23, readFileSync as readFileSync20 } from "fs";
10802
- import { join as join21 } from "path";
10965
+ import { existsSync as existsSync24, readFileSync as readFileSync20 } from "fs";
10966
+ import { join as join22 } from "path";
10803
10967
 
10804
10968
  // src/artifact/status.ts
10805
- import { existsSync as existsSync22, mkdirSync as mkdirSync13, readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
10969
+ import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
10806
10970
  import { dirname as dirname8 } from "path";
10807
10971
  import { z as z7 } from "zod";
10808
10972
  var ClassificationEnum = z7.enum([
@@ -10811,7 +10975,10 @@ var ClassificationEnum = z7.enum([
10811
10975
  "SELECTOR_DRIFT",
10812
10976
  "FLAKY",
10813
10977
  "TEST_BUG",
10814
- "TEST_OUTDATED"
10978
+ "TEST_OUTDATED",
10979
+ "CONTRACT_DRIFT",
10980
+ "RATE_LIMITED",
10981
+ "AUTH_EXPIRED"
10815
10982
  ]);
10816
10983
  var ResultEnum = z7.enum(["PASS", "FAIL"]);
10817
10984
  var ConfidenceEnum = z7.enum(["low", "medium", "high"]);
@@ -10837,7 +11004,7 @@ var StatusJsonSchema = z7.object({
10837
11004
  });
10838
11005
  var HISTORY_CAP = 20;
10839
11006
  function readStatus(path) {
10840
- if (!existsSync22(path))
11007
+ if (!existsSync23(path))
10841
11008
  return null;
10842
11009
  return StatusJsonSchema.parse(JSON.parse(readFileSync19(path, "utf8")));
10843
11010
  }
@@ -10869,8 +11036,8 @@ async function postCmd(argv) {
10869
11036
  return 0;
10870
11037
  }
10871
11038
  const paths = resolveArtifactPaths(cwd, ticket);
10872
- const draftPath = join21(paths.ticketDir, "jira-comment.draft.md");
10873
- if (!existsSync23(draftPath)) {
11039
+ const draftPath = join22(paths.ticketDir, "jira-comment.draft.md");
11040
+ if (!existsSync24(draftPath)) {
10874
11041
  console.error(`[xera:post] no draft at ${draftPath}; run \`xera-internal report\` first.`);
10875
11042
  return 1;
10876
11043
  }
@@ -10902,12 +11069,15 @@ async function promoteCmd(argv) {
10902
11069
  }
10903
11070
 
10904
11071
  // src/bin-internal/report.ts
10905
- import { existsSync as existsSync25, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "fs";
10906
- import { join as join22 } from "path";
11072
+ import { existsSync as existsSync26, readFileSync as readFileSync21, writeFileSync as writeFileSync14 } from "fs";
11073
+ import { join as join23 } from "path";
10907
11074
 
10908
11075
  // src/classifier/aggregate.ts
10909
11076
  var CLASS_PRIORITY = [
10910
11077
  "REAL_BUG",
11078
+ "CONTRACT_DRIFT",
11079
+ "AUTH_EXPIRED",
11080
+ "RATE_LIMITED",
10911
11081
  "TEST_OUTDATED",
10912
11082
  "TEST_BUG",
10913
11083
  "SELECTOR_DRIFT",
@@ -10934,6 +11104,134 @@ function aggregateScenarios(scenarios) {
10934
11104
  return { overall: chosen, overallConfidence: minConf, scenarios };
10935
11105
  }
10936
11106
 
11107
+ // src/classifier/auth-expired.ts
11108
+ function jwtExpPast(jwt, now) {
11109
+ const parts = jwt.split(".");
11110
+ if (parts.length !== 3)
11111
+ return false;
11112
+ try {
11113
+ const payloadB64 = parts[1];
11114
+ if (!payloadB64)
11115
+ return false;
11116
+ const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString("utf8"));
11117
+ return typeof payload.exp === "number" && payload.exp * 1000 < now;
11118
+ } catch {
11119
+ return false;
11120
+ }
11121
+ }
11122
+ function classifyAuthExpired(input) {
11123
+ const has401 = input.calls.some((c) => c.status === 401);
11124
+ if (!has401)
11125
+ return null;
11126
+ const now = Date.now();
11127
+ for (const [role, entry] of Object.entries(input.authFiles)) {
11128
+ const fileExpired = new Date(entry.expires_at).getTime() < now;
11129
+ const jwtExpired = entry.type === "bearer" && jwtExpPast(entry.token, now);
11130
+ if (fileExpired || jwtExpired) {
11131
+ return {
11132
+ class: "AUTH_EXPIRED",
11133
+ rationale: `HTTP 401 captured; auth file for role '${role}' is past expiry. Run: bun run xera:auth-setup --role ${role}`
11134
+ };
11135
+ }
11136
+ }
11137
+ return null;
11138
+ }
11139
+
11140
+ // src/classifier/contract-drift.ts
11141
+ function matchPath(specPaths, actualUrl) {
11142
+ const path = actualUrl.split("?")[0] ?? actualUrl;
11143
+ for (const tmpl of specPaths) {
11144
+ const re = new RegExp(`^${tmpl.replace(/\{[^}]+\}/g, "[^/]+")}$`);
11145
+ if (re.test(path))
11146
+ return tmpl;
11147
+ }
11148
+ return null;
11149
+ }
11150
+ function matchesSchema(body, schema) {
11151
+ if (!schema)
11152
+ return true;
11153
+ if (schema.type === "object") {
11154
+ if (typeof body !== "object" || body === null || Array.isArray(body))
11155
+ return false;
11156
+ const obj = body;
11157
+ for (const req of schema.required ?? []) {
11158
+ if (!(req in obj))
11159
+ return false;
11160
+ }
11161
+ return true;
11162
+ }
11163
+ if (schema.type === "array")
11164
+ return Array.isArray(body);
11165
+ if (schema.type === "string")
11166
+ return typeof body === "string";
11167
+ if (schema.type === "integer" || schema.type === "number")
11168
+ return typeof body === "number";
11169
+ if (schema.type === "boolean")
11170
+ return typeof body === "boolean";
11171
+ if (schema.type === "null")
11172
+ return body === null;
11173
+ return true;
11174
+ }
11175
+ var VERBS = ["get", "post", "put", "patch", "delete"];
11176
+ function isVerb(s) {
11177
+ return VERBS.includes(s);
11178
+ }
11179
+ function classifyContractDrift(input) {
11180
+ if (input.openapi === null)
11181
+ return null;
11182
+ const specPaths = Object.keys(input.openapi.paths);
11183
+ for (const call of input.calls) {
11184
+ const tmpl = matchPath(specPaths, call.url);
11185
+ if (!tmpl) {
11186
+ return {
11187
+ class: "CONTRACT_DRIFT",
11188
+ rationale: `Endpoint ${call.method} ${call.url} not found in OpenAPI`
11189
+ };
11190
+ }
11191
+ const methodLower = call.method.toLowerCase();
11192
+ if (!isVerb(methodLower)) {
11193
+ return {
11194
+ class: "CONTRACT_DRIFT",
11195
+ rationale: `Method ${call.method} not supported by classifier for ${tmpl}`
11196
+ };
11197
+ }
11198
+ const pathItem = input.openapi.paths[tmpl];
11199
+ const op = pathItem?.[methodLower];
11200
+ if (!op) {
11201
+ return {
11202
+ class: "CONTRACT_DRIFT",
11203
+ rationale: `${call.method} not defined for ${tmpl} in OpenAPI`
11204
+ };
11205
+ }
11206
+ const respDef = op.responses?.[String(call.status)];
11207
+ if (!respDef) {
11208
+ return {
11209
+ class: "CONTRACT_DRIFT",
11210
+ rationale: `Status ${call.status} not enumerated for ${call.method} ${tmpl} in OpenAPI`
11211
+ };
11212
+ }
11213
+ const schema = respDef.content?.["application/json"]?.schema;
11214
+ if (!matchesSchema(call.respBody, schema)) {
11215
+ return {
11216
+ class: "CONTRACT_DRIFT",
11217
+ rationale: `Response body for ${call.method} ${tmpl} (${call.status}) does not match OpenAPI schema`
11218
+ };
11219
+ }
11220
+ }
11221
+ return null;
11222
+ }
11223
+
11224
+ // src/classifier/rate-limited.ts
11225
+ function classifyRateLimited(input) {
11226
+ const hit = input.calls.find((c) => c.status === 429);
11227
+ if (!hit)
11228
+ return null;
11229
+ return {
11230
+ class: "RATE_LIMITED",
11231
+ rationale: `Captured HTTP 429 on ${hit.method} ${hit.url}`
11232
+ };
11233
+ }
11234
+
10937
11235
  // src/graph/classify.ts
10938
11236
  var DEFAULT_THRESHOLD = 0.7;
10939
11237
  var SHORT_CIRCUIT = ["FLAKY", "PASS"];
@@ -11038,11 +11336,11 @@ xera v${input.xeraVersion} \u2022 prompts v${input.promptsVersion}`;
11038
11336
  }
11039
11337
 
11040
11338
  // src/reporter/status-writer.ts
11041
- import { existsSync as existsSync24 } from "fs";
11339
+ import { existsSync as existsSync25 } from "fs";
11042
11340
  function writeStatusFromClassification(path, input) {
11043
11341
  const result = input.classification.overall === "PASS" ? "PASS" : "FAIL";
11044
11342
  const entry = { ts: input.runTs, result, class: input.classification.overall };
11045
- if (!existsSync24(path)) {
11343
+ if (!existsSync25(path)) {
11046
11344
  writeStatus(path, {
11047
11345
  ticket: input.ticket,
11048
11346
  lastRun: input.runTs,
@@ -11074,11 +11372,70 @@ async function reportCmd(argv) {
11074
11372
  console.error("[xera:report] usage: report <TICKET> --input=<classifier-output.json>");
11075
11373
  return 1;
11076
11374
  }
11077
- const paths = resolveArtifactPaths(process.cwd(), ticket);
11375
+ const cwd = process.cwd();
11376
+ const paths = resolveArtifactPaths(cwd, ticket);
11078
11377
  const input = JSON.parse(readFileSync21(inputArg.slice("--input=".length), "utf8"));
11079
- const aggregated = aggregateScenarios(input.scenarios);
11080
- const decisionsPath = join22(paths.ticketDir, "runs", input.runId, "outdated-decisions.json");
11081
- const decisions = existsSync25(decisionsPath) ? JSON.parse(readFileSync21(decisionsPath, "utf8")) : {};
11378
+ let httpRuleOverride = null;
11379
+ const meta = readMeta(paths.metaPath);
11380
+ if (meta?.adapter === "http") {
11381
+ const config = await loadConfig(cwd);
11382
+ if (config.http) {
11383
+ const normalizedPath = join23(paths.ticketDir, "runs", input.runId, "normalized.json");
11384
+ if (existsSync26(normalizedPath)) {
11385
+ const norm = JSON.parse(readFileSync21(normalizedPath, "utf8"));
11386
+ const calls = norm.http?.calls ?? [];
11387
+ const rate = classifyRateLimited({ calls });
11388
+ if (rate)
11389
+ httpRuleOverride = rate;
11390
+ if (!httpRuleOverride) {
11391
+ const authFiles = {};
11392
+ const httpAuthDir = join23(cwd, ".xera", ".auth", "http");
11393
+ for (const role of Object.keys(config.http.auth.roles)) {
11394
+ const entry = readAuthState(httpAuthDir, role);
11395
+ if (entry) {
11396
+ const p = entry.payload;
11397
+ if (typeof p.token === "string" && typeof p.type === "string") {
11398
+ authFiles[role] = {
11399
+ token: p.token,
11400
+ type: p.type,
11401
+ expires_at: entry.expires_at
11402
+ };
11403
+ }
11404
+ }
11405
+ }
11406
+ const authExp = classifyAuthExpired({ calls, authFiles });
11407
+ if (authExp)
11408
+ httpRuleOverride = authExp;
11409
+ }
11410
+ if (!httpRuleOverride && config.http.spec) {
11411
+ const { loadOpenApi } = await import("@xera-ai/http");
11412
+ const openapi = await loadOpenApi(config.http.spec);
11413
+ if (openapi) {
11414
+ const drift = classifyContractDrift({
11415
+ calls: calls.map((c) => ({
11416
+ method: c.method,
11417
+ url: c.url,
11418
+ status: c.status,
11419
+ respBody: c.respBody
11420
+ })),
11421
+ openapi
11422
+ });
11423
+ if (drift)
11424
+ httpRuleOverride = drift;
11425
+ }
11426
+ }
11427
+ }
11428
+ }
11429
+ }
11430
+ const scenariosForAggregation = httpRuleOverride ? input.scenarios.map((s) => s.outcome === "FAIL" ? {
11431
+ ...s,
11432
+ class: httpRuleOverride.class,
11433
+ rationale: httpRuleOverride.rationale,
11434
+ confidence: "high"
11435
+ } : s) : input.scenarios;
11436
+ const aggregated = aggregateScenarios(scenariosForAggregation);
11437
+ const decisionsPath = join23(paths.ticketDir, "runs", input.runId, "outdated-decisions.json");
11438
+ const decisions = existsSync26(decisionsPath) ? JSON.parse(readFileSync21(decisionsPath, "utf8")) : {};
11082
11439
  const graph = deriveSnapshot(loadAllEvents(process.cwd()));
11083
11440
  const normalizeScenarioName = (name) => name.trim().toLowerCase().replace(/\s+/g, " ");
11084
11441
  const scenarioIdByName = {};
@@ -11126,7 +11483,7 @@ async function reportCmd(argv) {
11126
11483
  xeraVersion: "0.1.0",
11127
11484
  promptsVersion: "1.0.0"
11128
11485
  });
11129
- const draftPath = join22(paths.ticketDir, "jira-comment.draft.md");
11486
+ const draftPath = join23(paths.ticketDir, "jira-comment.draft.md");
11130
11487
  writeFileSync14(draftPath, md);
11131
11488
  console.log(`[xera:report] wrote status.json and ${draftPath}`);
11132
11489
  return 0;
@@ -11194,7 +11551,7 @@ async function unlockCmd(argv) {
11194
11551
  }
11195
11552
 
11196
11553
  // src/bin-internal/validate-feature.ts
11197
- import { existsSync as existsSync26, readFileSync as readFileSync22 } from "fs";
11554
+ import { existsSync as existsSync27, readFileSync as readFileSync22 } from "fs";
11198
11555
  import { validateGherkin as validateGherkin2 } from "@xera-ai/web";
11199
11556
  async function validateFeatureCmd(argv) {
11200
11557
  const ticket = argv[0];
@@ -11203,7 +11560,7 @@ async function validateFeatureCmd(argv) {
11203
11560
  return 1;
11204
11561
  }
11205
11562
  const paths = resolveArtifactPaths(process.cwd(), ticket);
11206
- if (!existsSync26(paths.featurePath)) {
11563
+ if (!existsSync27(paths.featurePath)) {
11207
11564
  console.error(`[xera:validate-feature] missing ${paths.featurePath}`);
11208
11565
  return 1;
11209
11566
  }
@@ -11219,6 +11576,7 @@ async function validateFeatureCmd(argv) {
11219
11576
 
11220
11577
  // src/bin-internal/index.ts
11221
11578
  var COMMANDS = {
11579
+ "auth-setup": authSetupCmd,
11222
11580
  disputes: disputesCmd,
11223
11581
  doctor: doctorCmd,
11224
11582
  "eval-deterministic": evalDeterministicCmd,