@rtrentjones/greenlight 0.2.11 → 0.2.13

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.
@@ -45,7 +45,7 @@ without carrying the wrapper's `greenlight.config.ts` into the tool repo.
45
45
  **Deployment Protection gotcha:** `deployment_status.target_url` is the `*.vercel.app` *deployment*
46
46
  URL, which Vercel **Deployment Protection** gates (→ **401**) even though the public custom domain
47
47
  is 200. To verify the real app, create a **Protection Bypass for Automation** secret (Vercel →
48
- project → Settings → Deployment Protection) and set it as `VERCEL_AUTOMATION_BYPASS_SECRET` on the
48
+ project → Settings → Deployment Protection) and set it as `VERCEL_AUTOMATION_BYPASS_SECRET_<TOOL>` (per-tool — the bypass value is per Vercel project, so a second vercel tool never collides) on the
49
49
  tool repo — the api check sends it as `x-vercel-protection-bypass` and asserts 200. Without it the
50
50
  generated spec asserts **401** (the deployment is served + protected), so the gate stays green.
51
51
 
package/dist/bin.js CHANGED
@@ -276,12 +276,11 @@ var PACKS = [
276
276
  });
277
277
  return { ok: okStatus(r), detail: `HTTP ${r.status}` };
278
278
  }
279
- },
280
- {
281
- envVar: "TF_VAR_supabase_database_password",
282
- label: "database password (ignored when importing an existing project)",
283
- optional: true
284
279
  }
280
+ // NOTE: the database password is PER-PROJECT, so it's emitted as a per-tool variable
281
+ // (`<name>_supabase_database_password`) inline in each tool's <name>.tf — like the per-tool
282
+ // `<name>_vercel_project_id` — not a shared account credential gathered here. Set
283
+ // TF_VAR_<name>_supabase_database_password only when CREATING a project (ignored on import).
285
284
  ],
286
285
  mcp: {
287
286
  supabase: {
@@ -414,7 +413,7 @@ function tokensForTool(tool) {
414
413
  }
415
414
 
416
415
  // src/version.ts
417
- var MODULE_REF = "v0.2.11";
416
+ var MODULE_REF = "v0.2.13";
418
417
  var MODULE_SOURCE_BASE = "git::https://github.com/RTrentJones/greenlight.git//infra/modules";
419
418
  function moduleSource(module, ref = MODULE_REF) {
420
419
  return `${MODULE_SOURCE_BASE}/${module}?ref=${ref}`;
@@ -433,7 +432,7 @@ function emitToolTf(opts) {
433
432
  const blocks = [];
434
433
  const assumes = ["var.cloudflare_zone_id"];
435
434
  if (useOci) assumes.push("var.cloudflare_account_id", "local.oci_compartment_id");
436
- if (useSupabase) assumes.push("var.supabase_organization_id", "var.supabase_database_password");
435
+ if (useSupabase) assumes.push("var.supabase_organization_id");
437
436
  const ghcrOwner = (slug.split("/")[0] ?? "owner").toLowerCase();
438
437
  blocks.push(
439
438
  `# ${name} \u2014 ${lane}/${target}${useSupabase ? "/supabase" : ""}, emitted by \`greenlight add\`.
@@ -450,8 +449,17 @@ module "${name}_supabase" {
450
449
  name = "${name}"
451
450
  project_name = "${name}-db"
452
451
  organization_id = var.supabase_organization_id
453
- database_password = var.supabase_database_password
452
+ database_password = var.${name}_supabase_database_password
454
453
  region = "us-east-1"
454
+ }
455
+
456
+ # Per-tool (the password is per Supabase PROJECT) \u2014 so a second data:supabase tool doesn't collide
457
+ # on a shared variable. Set TF_VAR_${name}_supabase_database_password only when CREATING a project;
458
+ # on import the module ignores it (the default placeholder is fine).
459
+ variable "${name}_supabase_database_password" {
460
+ type = string
461
+ sensitive = true
462
+ default = "import-placeholder" # ignored when importing an existing project
455
463
  }`);
456
464
  }
457
465
  if (useVercel) {
@@ -603,9 +611,6 @@ function emitWrapperMainTf(opts) {
603
611
  vars.push('variable "cloudflare_account_id" {\n type = string\n default = ""\n}');
604
612
  if (need.has("supabase")) {
605
613
  vars.push('variable "supabase_organization_id" { type = string }');
606
- vars.push(
607
- 'variable "supabase_database_password" {\n type = string\n sensitive = true\n default = "import-placeholder" # ignored when importing an existing project\n}'
608
- );
609
614
  }
610
615
  if (need.has("oci")) {
611
616
  vars.push('variable "oci_tenancy_ocid" { type = string }');
@@ -1101,13 +1106,13 @@ function vendorDeps(vendorDir) {
1101
1106
  }
1102
1107
  function starterVerifyConfig(lane, target) {
1103
1108
  const spec = lane === "mcp" ? "mode: 'mcp', expectTools: []" : "mode: 'api', checks: [{ path: '/', status: 200 }]";
1104
- const logHint = target === "oci" ? "oci logging-search search-logs ... // the instance/container logs" : target === "vercel" ? "vercel logs <deployment-url> --token $VERCEL_API_TOKEN" : "wrangler tail --once // workers observability";
1109
+ const logHint = target === "vercel" ? 'vercel logs "$GREENLIGHT_VERIFY_URL" --token "$VERCEL_API_TOKEN" 2>&1 | head -40 || true' : 'curl -sS -i "$GREENLIGHT_VERIFY_URL" 2>&1 | head -30 || true';
1105
1110
  return `// Greenlight verify spec \u2014 edit to assert this tool's real contract.
1106
1111
  export default {
1107
1112
  ${spec},
1108
1113
  // Telemetry-into-verify: a shell command run ONLY when this report FAILS; its last ~50 lines
1109
- // attach to the report so the agent/CI sees the "why" in-loop. Best-effort (never fails the
1110
- // gate). Uncomment + adjust:
1114
+ // attach to the report so the agent/CI sees the "why" in-loop. $GREENLIGHT_VERIFY_URL is the
1115
+ // failing URL (no hard-coding). Best-effort (never fails the gate). Uncomment + adjust:
1111
1116
  // logsOnFailure: '${logHint}',
1112
1117
  };
1113
1118
  `;
@@ -1463,7 +1468,9 @@ jobs:
1463
1468
  env:
1464
1469
  # Bypass Vercel Deployment Protection on the deployment URL (Vercel \u2192 project \u2192 Deployment
1465
1470
  # Protection \u2192 Protection Bypass for Automation). Without it the gate asserts 401 (served).
1466
- VERCEL_AUTOMATION_BYPASS_SECRET: \${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }}
1471
+ # The SECRET is per-tool (the bypass value is per Vercel PROJECT) so two vercel tools never
1472
+ # collide; the env var the spec reads stays generic.
1473
+ VERCEL_AUTOMATION_BYPASS_SECRET: \${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET_${name.toUpperCase().replace(/-/g, "_")} }}
1467
1474
  ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}
1468
1475
  run: npx -y @rtrentjones/greenlight@latest verify --url "\${{ github.event.deployment_status.target_url }}" --spec verify/${name}.config.ts
1469
1476
  `;
@@ -1480,8 +1487,10 @@ function nextVerifyConfig(name) {
1480
1487
  // { mode: 'test', command: 'pnpm test' } + a tolerant deps-install step in greenlight-verify.yml.
1481
1488
  const bypass = process.env.VERCEL_AUTOMATION_BYPASS_SECRET;
1482
1489
  // Telemetry-into-verify: on a FAILED report, fetch the Vercel deployment's runtime logs and attach
1483
- // the tail to the report (best-effort, never fails the gate). Needs VERCEL_API_TOKEN in CI.
1484
- const logsOnFailure = 'vercel logs "$DEPLOYMENT_URL" --token "$VERCEL_API_TOKEN" 2>&1 || true';
1490
+ // the tail to the report (best-effort, never fails the gate). $GREENLIGHT_VERIFY_URL is the exact
1491
+ // failing deployment URL (injected \u2014 no hard-coding); needs VERCEL_API_TOKEN in CI.
1492
+ const logsOnFailure =
1493
+ 'vercel logs "$GREENLIGHT_VERIFY_URL" --token "$VERCEL_API_TOKEN" 2>&1 | head -40 || true';
1485
1494
  const api = bypass
1486
1495
  ? {
1487
1496
  mode: 'api',
@@ -1641,8 +1650,9 @@ Next:
1641
1650
  greenlight secrets gather ${name} --repo ${slug} # GREENLIGHT_DISPATCH_TOKEN
1642
1651
  The instance OCID is auto-resolved by the deploy workflow (by display name) \u2014 nothing to set.` : target === "vercel" ? `
1643
1652
  Deploy is Vercel's git integration (no wrapper deploy). The tool's greenlight-verify.yml verifies
1644
- each deployment (deployment_status). Optional: add ANTHROPIC_API_KEY to ${slug} to enable the
1645
- agent-web scenarios in verify/${name}.config.ts (absent \u2192 api + test gate alone).` : ""}`);
1653
+ each deployment (deployment_status). Optional secrets on ${slug}:
1654
+ \xB7 VERCEL_AUTOMATION_BYPASS_SECRET_${name.toUpperCase().replace(/-/g, "_")} (Vercel \u2192 project \u2192 Deployment Protection \u2192 Bypass for Automation) \u2192 verify asserts 200, not 401
1655
+ \xB7 ANTHROPIC_API_KEY \u2192 enables the agent-web scenarios in verify/${name}.config.ts (absent \u2192 api gate alone)` : ""}`);
1646
1656
  }
1647
1657
  async function adoptStandalone(ctx) {
1648
1658
  const { name, repoArg, lane, target, data, auth, envs, domain, reg, regPath } = ctx;
@@ -2245,7 +2255,9 @@ function attachFailureLogs(reports, specs, toolDir) {
2245
2255
  cwd: toolDir,
2246
2256
  timeout: 3e4,
2247
2257
  encoding: "utf8",
2248
- maxBuffer: 10 * 1024 * 1024
2258
+ maxBuffer: 10 * 1024 * 1024,
2259
+ // Let the command target the exact failing URL without hard-coding it.
2260
+ env: { ...process.env, GREENLIGHT_VERIFY_URL: report.url }
2249
2261
  });
2250
2262
  const out = `${res.stdout ?? ""}${res.stderr ?? ""}`.trimEnd();
2251
2263
  const tail = out.split("\n").slice(-LOG_TAIL_LINES).join("\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rtrentjones/greenlight",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
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-loop": "0.2.4",
35
- "@rtrentjones/greenlight-shared": "0.2.4",
36
34
  "@rtrentjones/greenlight-adapters": "0.2.4",
37
- "@rtrentjones/greenlight-verify": "0.2.4"
35
+ "@rtrentjones/greenlight-shared": "0.2.4",
36
+ "@rtrentjones/greenlight-verify": "0.2.4",
37
+ "@rtrentjones/greenlight-loop": "0.2.4"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "node scripts/copy-assets.mjs && tsup",