@rtrentjones/greenlight 0.2.22 → 0.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  verifyAgentWeb
3
- } from "./chunk-UXHHLEYO.js";
3
+ } from "./chunk-KVOI4UL2.js";
4
4
  import "./chunk-QFKE5JKC.js";
5
5
  export {
6
6
  verifyAgentWeb
package/dist/bin.js CHANGED
@@ -5,12 +5,12 @@ import {
5
5
  loadConfig,
6
6
  resolveUrl,
7
7
  verifyAll
8
- } from "./chunk-GO2RVNOP.js";
8
+ } from "./chunk-TFWXR7PP.js";
9
9
  import "./chunk-HX7VA25D.js";
10
10
  import "./chunk-N3IKUCSF.js";
11
11
  import "./chunk-KP3Y6WRU.js";
12
- import "./chunk-UXHHLEYO.js";
13
- import "./chunk-6N7MD6FR.js";
12
+ import "./chunk-KVOI4UL2.js";
13
+ import "./chunk-XWTOJHLV.js";
14
14
  import "./chunk-QFKE5JKC.js";
15
15
 
16
16
  // src/commands/add.ts
@@ -443,7 +443,7 @@ function tokensForTool(tool) {
443
443
  }
444
444
 
445
445
  // src/version.ts
446
- var MODULE_REF = "v0.2.22";
446
+ var MODULE_REF = "v0.2.23";
447
447
  var MODULE_SOURCE_BASE = "git::https://github.com/RTrentJones/greenlight.git//infra/modules";
448
448
  function moduleSource(module, ref = MODULE_REF) {
449
449
  return `${MODULE_SOURCE_BASE}/${module}?ref=${ref}`;
@@ -1991,7 +1991,8 @@ async function deployCommand(args) {
1991
1991
  }
1992
1992
 
1993
1993
  // src/commands/doctor.ts
1994
- import { existsSync as existsSync7 } from "fs";
1994
+ import { execFileSync as execFileSync4 } from "child_process";
1995
+ import { existsSync as existsSync7, readFileSync as readFileSync5, readdirSync as readdirSync2 } from "fs";
1995
1996
  import { join as join4 } from "path";
1996
1997
  function dirCheck(label, dir) {
1997
1998
  return existsSync7(dir) ? { name: `${label}: directory`, status: "ok" } : { name: `${label}: directory`, status: "fail", detail: `missing ${dir}` };
@@ -2030,6 +2031,61 @@ function conformanceChecks(t, root) {
2030
2031
  }
2031
2032
  return out;
2032
2033
  }
2034
+ function versionDriftCheck(root) {
2035
+ const name = "framework version drift";
2036
+ let installed;
2037
+ try {
2038
+ const pkg = JSON.parse(
2039
+ readFileSync5(join4(root, "node_modules/@rtrentjones/greenlight/package.json"), "utf8")
2040
+ );
2041
+ installed = pkg.version;
2042
+ } catch {
2043
+ }
2044
+ const refs = /* @__PURE__ */ new Set();
2045
+ try {
2046
+ for (const f of readdirSync2(join4(root, "infra")).filter((f2) => f2.endsWith(".tf"))) {
2047
+ const body = readFileSync5(join4(root, "infra", f), "utf8");
2048
+ for (const m of body.matchAll(/greenlight\.git\/\/infra\/modules\/[^?"]+\?ref=(v[0-9.]+)/g)) {
2049
+ if (m[1]) refs.add(m[1]);
2050
+ }
2051
+ }
2052
+ } catch {
2053
+ }
2054
+ if (!installed && refs.size === 0) {
2055
+ return {
2056
+ name,
2057
+ status: "skip",
2058
+ detail: "no installed @rtrentjones/greenlight or infra pins here"
2059
+ };
2060
+ }
2061
+ const refList = [...refs];
2062
+ if (installed) {
2063
+ const want = `v${installed}`;
2064
+ const bad = refList.filter((r) => r !== want);
2065
+ return bad.length === 0 ? { name, status: "ok", detail: `infra pins == installed ${want}` } : {
2066
+ name,
2067
+ status: "warn",
2068
+ detail: `installed ${want}, but infra pins ${bad.join(", ")} \u2014 bump ?ref to ${want}`
2069
+ };
2070
+ }
2071
+ return refList.length <= 1 ? { name, status: "ok", detail: `infra pins uniform (${refList[0] ?? "none"})` } : { name, status: "warn", detail: `infra ?ref pins not uniform: ${refList.join(", ")}` };
2072
+ }
2073
+ function submoduleDriftCheck(root) {
2074
+ const name = "submodule drift";
2075
+ let out;
2076
+ try {
2077
+ out = execFileSync4("git", ["submodule", "status"], {
2078
+ cwd: root,
2079
+ encoding: "utf8",
2080
+ stdio: ["ignore", "pipe", "ignore"]
2081
+ }).trim();
2082
+ } catch {
2083
+ return { name, status: "skip", detail: "no git / not a repo" };
2084
+ }
2085
+ if (!out) return { name, status: "skip", detail: "no submodules" };
2086
+ const dirty = out.split("\n").filter((l) => /^[+\-U]/.test(l));
2087
+ return dirty.length === 0 ? { name, status: "ok", detail: "all submodules match their recorded commit" } : { name, status: "warn", detail: dirty.map((l) => l.trim()).join("; ") };
2088
+ }
2033
2089
  function runDoctor(config, root) {
2034
2090
  const checks = [];
2035
2091
  if (config.blog) checks.push(dirCheck("blog", join4(root, "apps/blog")));
@@ -2042,6 +2098,15 @@ function runDoctor(config, root) {
2042
2098
  mcp: t.lane === "mcp"
2043
2099
  });
2044
2100
  checks.push({ name: `${t.name}: external (registry)`, status: "ok", detail: url });
2101
+ if (t.dir) {
2102
+ checks.push(
2103
+ existsSync7(join4(root, t.dir)) ? { name: `${t.name}: dir present`, status: "ok", detail: t.dir } : {
2104
+ name: `${t.name}: dir present`,
2105
+ status: "warn",
2106
+ detail: `declared dir "${t.dir}" missing \u2014 run \`git submodule update --init\``
2107
+ }
2108
+ );
2109
+ }
2045
2110
  } else {
2046
2111
  checks.push(dirCheck(t.name, join4(root, t.dir ?? join4("tools", t.name))));
2047
2112
  }
@@ -2053,13 +2118,14 @@ function runDoctor(config, root) {
2053
2118
  status: needsKeepalive.length > 0 ? "ok" : "skip",
2054
2119
  detail: needsKeepalive.length > 0 ? needsKeepalive.map((t) => `${t.name} (${t.data === "supabase" ? "supabase" : "oci"})`).join(", ") : "no data:supabase / target:oci tools"
2055
2120
  });
2121
+ checks.push(versionDriftCheck(root));
2122
+ checks.push(submoduleDriftCheck(root));
2056
2123
  for (const name of [
2057
2124
  "DNS propagation",
2058
2125
  "terraform drift",
2059
2126
  "Vercel cap headroom",
2060
2127
  "keepalive health (live)",
2061
- "OCI PAYG status",
2062
- "framework version drift"
2128
+ "OCI PAYG status"
2063
2129
  ]) {
2064
2130
  checks.push({ name, status: "skip", detail: "needs provider creds / packages (Phase 5/7/8)" });
2065
2131
  }
@@ -2091,7 +2157,7 @@ import { resolve as resolve8 } from "path";
2091
2157
  import { createInterface as createInterface3 } from "readline/promises";
2092
2158
 
2093
2159
  // src/tokens.ts
2094
- import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
2160
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
2095
2161
  import { resolve as resolve7 } from "path";
2096
2162
  import { createInterface as createInterface2 } from "readline/promises";
2097
2163
  var SECRETS_DIR = ".greenlight";
@@ -2100,7 +2166,7 @@ function presentEnv(cwd) {
2100
2166
  const out = {};
2101
2167
  const p = resolve7(cwd, SECRETS_DIR, SECRETS_FILE);
2102
2168
  if (existsSync8(p)) {
2103
- for (const { key, value } of parseSecretsEnv(readFileSync5(p, "utf8"))) out[key] = value;
2169
+ for (const { key, value } of parseSecretsEnv(readFileSync6(p, "utf8"))) out[key] = value;
2104
2170
  }
2105
2171
  for (const [k, v] of Object.entries(process.env)) {
2106
2172
  if (v !== void 0 && !(k in out)) out[k] = v;
@@ -2111,7 +2177,7 @@ function upsertSecret(cwd, key, value) {
2111
2177
  const dir = resolve7(cwd, SECRETS_DIR);
2112
2178
  mkdirSync4(dir, { recursive: true });
2113
2179
  const p = resolve7(dir, SECRETS_FILE);
2114
- const lines = existsSync8(p) ? readFileSync5(p, "utf8").split("\n") : [];
2180
+ const lines = existsSync8(p) ? readFileSync6(p, "utf8").split("\n") : [];
2115
2181
  const idx = lines.findIndex((l) => l.startsWith(`${key}=`));
2116
2182
  if (idx >= 0) lines[idx] = `${key}=${value}`;
2117
2183
  else {
@@ -2338,7 +2404,7 @@ Next:
2338
2404
  }
2339
2405
 
2340
2406
  // src/commands/preview.ts
2341
- import { execFileSync as execFileSync4, spawn } from "child_process";
2407
+ import { execFileSync as execFileSync5, spawn } from "child_process";
2342
2408
  import { resolve as resolve10 } from "path";
2343
2409
  import { setTimeout as sleep } from "timers/promises";
2344
2410
 
@@ -2377,6 +2443,15 @@ ${report.logs}
2377
2443
  }
2378
2444
  }
2379
2445
  var LOG_TAIL_LINES = 50;
2446
+ function redactSecrets(text, env = process.env) {
2447
+ let out = text;
2448
+ for (const [k, v] of Object.entries(env)) {
2449
+ if (!v || v.length < 6) continue;
2450
+ if (!/TOKEN|KEY|SECRET|PASSWORD|PWD/i.test(k)) continue;
2451
+ out = out.split(v).join("***");
2452
+ }
2453
+ return out;
2454
+ }
2380
2455
  function attachFailureLogs(reports, specs, toolDir) {
2381
2456
  reports.forEach((report, i) => {
2382
2457
  if (report.pass) return;
@@ -2395,7 +2470,7 @@ function attachFailureLogs(reports, specs, toolDir) {
2395
2470
  // Let the command target the exact failing URL without hard-coding it.
2396
2471
  env: { ...process.env, GREENLIGHT_VERIFY_URL: report.url }
2397
2472
  });
2398
- const out = `${res.stdout ?? ""}${res.stderr ?? ""}`.trimEnd();
2473
+ const out = redactSecrets(`${res.stdout ?? ""}${res.stderr ?? ""}`.trimEnd());
2399
2474
  const tail = out.split("\n").slice(-LOG_TAIL_LINES).join("\n");
2400
2475
  report.logs = tail || `(logsOnFailure produced no output${res.error ? `: ${res.error.message}` : ""})`;
2401
2476
  } catch (e) {
@@ -2528,7 +2603,7 @@ async function previewViaDescriptor(entry, name, portOverride) {
2528
2603
  } finally {
2529
2604
  if (pv.teardown) {
2530
2605
  try {
2531
- execFileSync4(pv.teardown, { cwd: toolDir, shell: true, stdio: "inherit" });
2606
+ execFileSync5(pv.teardown, { cwd: toolDir, shell: true, stdio: "inherit" });
2532
2607
  } catch {
2533
2608
  }
2534
2609
  }
@@ -2545,7 +2620,7 @@ async function previewViaBuiltIn(entry, name, portOverride) {
2545
2620
  const plan = servePlan(entry.lane, portOverride);
2546
2621
  if (plan.build) {
2547
2622
  console.log(`build ${name} (${entry.dir})`);
2548
- execFileSync4("pnpm", ["-C", entry.dir, "run", "build"], { stdio: "inherit" });
2623
+ execFileSync5("pnpm", ["-C", entry.dir, "run", "build"], { stdio: "inherit" });
2549
2624
  }
2550
2625
  console.log(`serve ${name} on :${plan.port}`);
2551
2626
  const runArgs = ["-C", entry.dir, "run", plan.script];
@@ -2600,12 +2675,12 @@ async function previewCommand(args) {
2600
2675
  }
2601
2676
 
2602
2677
  // ../packages/loop/src/promote.ts
2603
- import { execFileSync as execFileSync5 } from "child_process";
2678
+ import { execFileSync as execFileSync6 } from "child_process";
2604
2679
  function git(repoDir, args) {
2605
- execFileSync5("git", args, { cwd: repoDir, stdio: "ignore" });
2680
+ execFileSync6("git", args, { cwd: repoDir, stdio: "ignore" });
2606
2681
  }
2607
2682
  function gitOut(repoDir, args) {
2608
- return execFileSync5("git", args, { cwd: repoDir, encoding: "utf8" }).trim();
2683
+ return execFileSync6("git", args, { cwd: repoDir, encoding: "utf8" }).trim();
2609
2684
  }
2610
2685
  function tryRev(repoDir, ref) {
2611
2686
  try {
@@ -2615,9 +2690,16 @@ function tryRev(repoDir, ref) {
2615
2690
  }
2616
2691
  }
2617
2692
  function fetchRefs(repoDir, branches) {
2693
+ try {
2694
+ git(repoDir, ["remote", "get-url", "origin"]);
2695
+ } catch {
2696
+ return { ok: false, hasOrigin: false };
2697
+ }
2618
2698
  try {
2619
2699
  git(repoDir, ["fetch", "--no-tags", "origin", ...branches]);
2700
+ return { ok: true, hasOrigin: true };
2620
2701
  } catch {
2702
+ return { ok: false, hasOrigin: true };
2621
2703
  }
2622
2704
  }
2623
2705
  function resolveRef(repoDir, branch) {
@@ -2644,13 +2726,23 @@ function staleLocalWarnings(repoDir, branches) {
2644
2726
  return warnings;
2645
2727
  }
2646
2728
  function canPromote(repoDir, from = "develop", to = "main") {
2647
- fetchRefs(repoDir, [from, to]);
2729
+ const warnings = [];
2730
+ const fetched = fetchRefs(repoDir, [from, to]);
2731
+ if (fetched.hasOrigin && !fetched.ok) {
2732
+ warnings.push(
2733
+ "could not `git fetch origin` \u2014 eligibility may be based on stale remote-tracking refs (offline / auth?). Re-run after a successful fetch."
2734
+ );
2735
+ }
2648
2736
  const fromRef = resolveRef(repoDir, from);
2649
2737
  const toRef = resolveRef(repoDir, to);
2650
2738
  if (!fromRef || !toRef) {
2651
- return { canPromote: false, reason: `branch "${from}" or "${to}" not found in ${repoDir}` };
2739
+ return {
2740
+ canPromote: false,
2741
+ reason: `branch "${from}" or "${to}" not found in ${repoDir}`,
2742
+ warnings: warnings.length ? warnings : void 0
2743
+ };
2652
2744
  }
2653
- const warnings = staleLocalWarnings(repoDir, [from, to]);
2745
+ warnings.push(...staleLocalWarnings(repoDir, [from, to]));
2654
2746
  try {
2655
2747
  git(repoDir, ["merge-base", "--is-ancestor", toRef, fromRef]);
2656
2748
  return { canPromote: true, reason: `"${to}" can fast-forward to "${from}"`, warnings };
@@ -2716,10 +2808,10 @@ async function promoteCommand(args) {
2716
2808
  }
2717
2809
 
2718
2810
  // src/commands/status.ts
2719
- import { execFileSync as execFileSync6 } from "child_process";
2811
+ import { execFileSync as execFileSync7 } from "child_process";
2720
2812
  function repoSlug(dir) {
2721
2813
  try {
2722
- const url = execFileSync6("git", ["-C", dir, "remote", "get-url", "origin"], {
2814
+ const url = execFileSync7("git", ["-C", dir, "remote", "get-url", "origin"], {
2723
2815
  encoding: "utf8"
2724
2816
  }).trim();
2725
2817
  const m = url.match(/[:/]([^/]+\/[^/]+?)(?:\.git)?$/);
@@ -2752,7 +2844,7 @@ function workflowsFor(entry, name, wrapper, toolRepo) {
2752
2844
  }
2753
2845
  function lastRun(repo, workflow) {
2754
2846
  try {
2755
- const out = execFileSync6(
2847
+ const out = execFileSync7(
2756
2848
  "gh",
2757
2849
  [
2758
2850
  "run",
@@ -195,7 +195,11 @@ async function verifyAgentWeb(baseUrl, spec) {
195
195
  { name: "@anthropic-ai/sdk available", pass: false, detail: "pnpm add @anthropic-ai/sdk" }
196
196
  ]);
197
197
  }
198
- const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
198
+ const client = new Anthropic({
199
+ apiKey: process.env.ANTHROPIC_API_KEY,
200
+ timeout: 6e4,
201
+ maxRetries: 1
202
+ });
199
203
  let browser;
200
204
  try {
201
205
  browser = await chromium.launch({ headless: !spec.headed });
@@ -136,10 +136,15 @@ import { setTimeout as sleep } from "timers/promises";
136
136
 
137
137
  // ../packages/verify/src/api.ts
138
138
  var trimSlash = (s) => s.replace(/\/+$/, "");
139
- async function checkRoute(base, c) {
139
+ var DEFAULT_TIMEOUT_MS = 1e4;
140
+ var DEFAULT_MAX_LINKS = 50;
141
+ function timedFetch(url, timeoutMs, init) {
142
+ return fetch(url, { redirect: "manual", ...init, signal: AbortSignal.timeout(timeoutMs) });
143
+ }
144
+ async function checkRoute(base, c, timeoutMs) {
140
145
  const name = `GET ${c.path}`;
141
146
  try {
142
- const res = await fetch(base + c.path, { redirect: "manual", headers: c.requestHeaders });
147
+ const res = await timedFetch(base + c.path, timeoutMs, { headers: c.requestHeaders });
143
148
  const reasons = [];
144
149
  if (c.status !== void 0 && res.status !== c.status) {
145
150
  reasons.push(`status ${res.status} != ${c.status}`);
@@ -160,10 +165,10 @@ async function checkRoute(base, c) {
160
165
  return { name, pass: false, detail: msg(e) };
161
166
  }
162
167
  }
163
- async function checkXml(base, candidates, label, marker) {
168
+ async function checkXml(base, candidates, label, marker, timeoutMs) {
164
169
  for (const path of candidates) {
165
170
  try {
166
- const res = await fetch(base + path, { redirect: "manual" });
171
+ const res = await timedFetch(base + path, timeoutMs);
167
172
  if (res.status === 200) {
168
173
  const body = await res.text();
169
174
  const ok = marker.test(body);
@@ -178,65 +183,97 @@ async function checkXml(base, candidates, label, marker) {
178
183
  }
179
184
  return { name: label, pass: false, detail: `none of ${candidates.join(", ")} returned 200` };
180
185
  }
181
- async function checkInternalLinks(base, max = 25) {
186
+ async function checkInternalLinks(base, timeoutMs, max = DEFAULT_MAX_LINKS) {
182
187
  try {
183
- const res = await fetch(`${base}/`, { redirect: "manual" });
188
+ const res = await timedFetch(`${base}/`, timeoutMs);
184
189
  const html = await res.text();
185
190
  const hrefs = /* @__PURE__ */ new Set();
191
+ let capped = false;
186
192
  for (const m of html.matchAll(/href="(\/[^"#?]*)"/g)) {
187
193
  const href = m[1];
188
194
  if (href && !href.startsWith("//")) hrefs.add(href);
189
- if (hrefs.size >= max) break;
195
+ if (hrefs.size >= max) {
196
+ capped = true;
197
+ break;
198
+ }
199
+ }
200
+ if (hrefs.size === 0) {
201
+ return {
202
+ name: "no broken internal links",
203
+ pass: false,
204
+ detail: `no internal links found on ${base}/ (status ${res.status}) \u2014 page empty or unparseable`
205
+ };
190
206
  }
191
207
  const broken = [];
192
208
  for (const href of hrefs) {
193
209
  try {
194
- const r = await fetch(base + href, { redirect: "manual" });
210
+ const r = await timedFetch(base + href, timeoutMs);
195
211
  if (r.status >= 400) broken.push(`${href} (${r.status})`);
196
212
  } catch {
197
213
  broken.push(`${href} (unreachable)`);
198
214
  }
199
215
  }
216
+ const capNote = capped ? `; capped at first ${max} \u2014 raise maxLinks to check more` : "";
200
217
  return {
201
- name: `no broken internal links (${hrefs.size} checked)`,
218
+ name: `no broken internal links (${hrefs.size} checked${capped ? `, capped at ${max}` : ""})`,
202
219
  pass: broken.length === 0,
203
- detail: broken.length ? `broken: ${broken.join(", ")}` : void 0
220
+ detail: broken.length ? `broken: ${broken.join(", ")}${capNote}` : capNote || void 0
204
221
  };
205
222
  } catch (e) {
206
223
  return { name: "no broken internal links", pass: false, detail: msg(e) };
207
224
  }
208
225
  }
209
- async function runChecks(base, spec) {
210
- const checks = [];
211
- for (const c of spec.checks ?? []) checks.push(await checkRoute(base, c));
226
+ function buildTasks(base, spec) {
227
+ const timeoutMs = spec.timeoutMs ?? DEFAULT_TIMEOUT_MS;
228
+ const tasks = [];
229
+ for (const c of spec.checks ?? []) tasks.push(() => checkRoute(base, c, timeoutMs));
212
230
  if (spec.rssValid) {
213
- checks.push(
214
- await checkXml(base, ["/rss.xml", "/feed.xml", "/index.xml"], "rss", /<(rss|feed)[\s>]/i)
231
+ tasks.push(
232
+ () => checkXml(
233
+ base,
234
+ ["/rss.xml", "/feed.xml", "/index.xml"],
235
+ "rss",
236
+ /<(rss|feed)[\s>]/i,
237
+ timeoutMs
238
+ )
215
239
  );
216
240
  }
217
241
  if (spec.sitemapValid) {
218
- checks.push(
219
- await checkXml(
242
+ tasks.push(
243
+ () => checkXml(
220
244
  base,
221
245
  ["/sitemap.xml", "/sitemap-index.xml"],
222
246
  "sitemap",
223
- /<(urlset|sitemapindex)[\s>]/i
247
+ /<(urlset|sitemapindex)[\s>]/i,
248
+ timeoutMs
224
249
  )
225
250
  );
226
251
  }
227
- if (spec.noBrokenInternalLinks) checks.push(await checkInternalLinks(base));
228
- return checks;
252
+ if (spec.noBrokenInternalLinks) {
253
+ tasks.push(() => checkInternalLinks(base, timeoutMs, spec.maxLinks));
254
+ }
255
+ return tasks;
229
256
  }
230
257
  async function verifyApi(baseUrl, spec) {
231
258
  const base = trimSlash(baseUrl);
232
259
  const retries = Math.max(0, spec.settleRetries ?? 0);
233
260
  const delayMs = spec.settleMs ?? 5e3;
234
- let checks = await runChecks(base, spec);
235
- for (let i = 0; i < retries && !checks.every((c) => c.pass); i++) {
261
+ const state = await Promise.all(
262
+ buildTasks(base, spec).map(async (task) => ({ task, check: await task() }))
263
+ );
264
+ for (let i = 0; i < retries && !state.every((s) => s.check.pass); i++) {
236
265
  if (delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
237
- checks = await runChecks(base, spec);
266
+ await Promise.all(
267
+ state.filter((s) => !s.check.pass).map(async (s) => {
268
+ s.check = await s.task();
269
+ })
270
+ );
238
271
  }
239
- return report("api", baseUrl, checks);
272
+ return report(
273
+ "api",
274
+ baseUrl,
275
+ state.map((s) => s.check)
276
+ );
240
277
  }
241
278
 
242
279
  // ../packages/verify/src/index.ts
@@ -274,11 +311,11 @@ async function verify(baseUrl, spec, opts) {
274
311
  return verifyTest2(spec, opts?.toolDir ?? process.cwd());
275
312
  }
276
313
  case "agent-web": {
277
- const { verifyAgentWeb: verifyAgentWeb2 } = await import("./agent-web-I4LXW4SR.js");
314
+ const { verifyAgentWeb: verifyAgentWeb2 } = await import("./agent-web-3FTO2TLJ.js");
278
315
  return verifyAgentWeb2(baseUrl, spec);
279
316
  }
280
317
  case "eval": {
281
- const { verifyEval: verifyEval2 } = await import("./eval-LLQPOEQX.js");
318
+ const { verifyEval: verifyEval2 } = await import("./eval-44S2BATV.js");
282
319
  return verifyEval2(baseUrl, spec);
283
320
  }
284
321
  }
@@ -19,7 +19,11 @@ function llmJudge(model) {
19
19
  if (!process.env.ANTHROPIC_API_KEY) throw new Error("ANTHROPIC_API_KEY not set");
20
20
  const sdkName = "@anthropic-ai/sdk";
21
21
  const Anthropic = (await import(sdkName)).default;
22
- const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
22
+ const client = new Anthropic({
23
+ apiKey: process.env.ANTHROPIC_API_KEY,
24
+ timeout: 6e4,
25
+ maxRetries: 1
26
+ });
23
27
  const resp = await client.messages.create({
24
28
  model,
25
29
  max_tokens: 512,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  llmJudge,
3
3
  verifyEval
4
- } from "./chunk-6N7MD6FR.js";
4
+ } from "./chunk-XWTOJHLV.js";
5
5
  import "./chunk-QFKE5JKC.js";
6
6
  export {
7
7
  llmJudge,
package/dist/index.js CHANGED
@@ -2,12 +2,12 @@ import {
2
2
  defineConfig,
3
3
  defineVerify,
4
4
  loadConfig
5
- } from "./chunk-GO2RVNOP.js";
5
+ } from "./chunk-TFWXR7PP.js";
6
6
  import "./chunk-HX7VA25D.js";
7
7
  import "./chunk-N3IKUCSF.js";
8
8
  import "./chunk-KP3Y6WRU.js";
9
- import "./chunk-UXHHLEYO.js";
10
- import "./chunk-6N7MD6FR.js";
9
+ import "./chunk-KVOI4UL2.js";
10
+ import "./chunk-XWTOJHLV.js";
11
11
  import "./chunk-QFKE5JKC.js";
12
12
  export {
13
13
  defineConfig,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rtrentjones/greenlight",
3
- "version": "0.2.22",
3
+ "version": "0.2.23",
4
4
  "description": "Greenlight CLI — setup and lifecycle for the harness.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -31,10 +31,10 @@
31
31
  "@anthropic-ai/sdk": "^0.69.0"
32
32
  },
33
33
  "devDependencies": {
34
- "@rtrentjones/greenlight-adapters": "0.2.22",
35
- "@rtrentjones/greenlight-verify": "0.2.22",
36
- "@rtrentjones/greenlight-loop": "0.2.22",
37
- "@rtrentjones/greenlight-shared": "0.2.22"
34
+ "@rtrentjones/greenlight-loop": "0.2.23",
35
+ "@rtrentjones/greenlight-shared": "0.2.23",
36
+ "@rtrentjones/greenlight-adapters": "0.2.23",
37
+ "@rtrentjones/greenlight-verify": "0.2.23"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "node scripts/copy-assets.mjs && tsup",