@rtrentjones/greenlight 0.2.19 → 0.2.20
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.
- package/dist/bin.js +62 -10
- package/dist/{chunk-6USV5AQV.js → chunk-PSNO7F4Q.js} +10 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
loadConfig,
|
|
6
6
|
resolveUrl,
|
|
7
7
|
verifyAll
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PSNO7F4Q.js";
|
|
9
9
|
import "./chunk-HX7VA25D.js";
|
|
10
10
|
import "./chunk-N3IKUCSF.js";
|
|
11
11
|
import "./chunk-KP3Y6WRU.js";
|
|
@@ -58,6 +58,11 @@ function serializeTool(t) {
|
|
|
58
58
|
if (pv.path !== void 0) pvParts.push(`path: ${q(pv.path)}`);
|
|
59
59
|
parts.push(`preview: { ${pvParts.join(", ")} }`);
|
|
60
60
|
}
|
|
61
|
+
if (t.tokens?.length) parts.push(`tokens: [${t.tokens.map(q).join(", ")}]`);
|
|
62
|
+
if (t.tokenOverrides && Object.keys(t.tokenOverrides).length) {
|
|
63
|
+
const ov = Object.entries(t.tokenOverrides).map(([k, v]) => `${k}: ${q(v)}`).join(", ");
|
|
64
|
+
parts.push(`tokenOverrides: { ${ov} }`);
|
|
65
|
+
}
|
|
61
66
|
return ` { ${parts.join(", ")} },`;
|
|
62
67
|
}
|
|
63
68
|
function serializeConfig(c) {
|
|
@@ -103,7 +108,9 @@ function addTool(config, t) {
|
|
|
103
108
|
...t.adopted ? { adopted: true } : {},
|
|
104
109
|
...t.external ? { external: true } : {},
|
|
105
110
|
...t.port !== void 0 ? { port: t.port } : {},
|
|
106
|
-
...t.preview ? { preview: t.preview } : {}
|
|
111
|
+
...t.preview ? { preview: t.preview } : {},
|
|
112
|
+
...t.tokens?.length ? { tokens: t.tokens } : {},
|
|
113
|
+
...t.tokenOverrides && Object.keys(t.tokenOverrides).length ? { tokenOverrides: t.tokenOverrides } : {}
|
|
107
114
|
}
|
|
108
115
|
]
|
|
109
116
|
};
|
|
@@ -129,7 +136,9 @@ function upsertTool(config, t) {
|
|
|
129
136
|
...t.adopted ? { adopted: true } : {},
|
|
130
137
|
...t.external ? { external: true } : {},
|
|
131
138
|
...t.port !== void 0 ? { port: t.port } : {},
|
|
132
|
-
...t.preview ? { preview: t.preview } : {}
|
|
139
|
+
...t.preview ? { preview: t.preview } : {},
|
|
140
|
+
...t.tokens?.length ? { tokens: t.tokens } : {},
|
|
141
|
+
...t.tokenOverrides && Object.keys(t.tokenOverrides).length ? { tokenOverrides: t.tokenOverrides } : {}
|
|
133
142
|
};
|
|
134
143
|
const tools = config.tools.some((x) => x.name === t.name) ? config.tools.map((x) => x.name === t.name ? entry : x) : [...config.tools, entry];
|
|
135
144
|
const result = ConfigSchema.safeParse({ ...config, tools });
|
|
@@ -186,7 +195,9 @@ function resolveEntry(config, name) {
|
|
|
186
195
|
dir: tool.dir ?? `tools/${tool.name}`,
|
|
187
196
|
external: tool.external,
|
|
188
197
|
port: tool.port,
|
|
189
|
-
preview: tool.preview
|
|
198
|
+
preview: tool.preview,
|
|
199
|
+
tokens: tool.tokens,
|
|
200
|
+
tokenOverrides: tool.tokenOverrides
|
|
190
201
|
};
|
|
191
202
|
}
|
|
192
203
|
var VERIFY_MODES = /* @__PURE__ */ new Set(["api", "mcp", "playwright", "test", "agent-web", "eval"]);
|
|
@@ -401,6 +412,12 @@ var PACKS = [
|
|
|
401
412
|
tfModules: ["tool", "tunnel", "oci-network", "oci-container-instance"]
|
|
402
413
|
}
|
|
403
414
|
];
|
|
415
|
+
function secretKeyFor(tok, toolName, overrides) {
|
|
416
|
+
const override = overrides?.[tok.envVar];
|
|
417
|
+
if (override) return override;
|
|
418
|
+
const suffix = `_${toolName.toUpperCase().replace(/-/g, "_")}`;
|
|
419
|
+
return tok.envVar.toUpperCase() + (tok.perTool ? suffix : "");
|
|
420
|
+
}
|
|
404
421
|
function packsForTool(tool) {
|
|
405
422
|
return PACKS.filter((p) => p.always || (tool ? p.appliesTo(tool) : false));
|
|
406
423
|
}
|
|
@@ -426,7 +443,7 @@ function tokensForTool(tool) {
|
|
|
426
443
|
}
|
|
427
444
|
|
|
428
445
|
// src/version.ts
|
|
429
|
-
var MODULE_REF = "v0.2.
|
|
446
|
+
var MODULE_REF = "v0.2.20";
|
|
430
447
|
var MODULE_SOURCE_BASE = "git::https://github.com/RTrentJones/greenlight.git//infra/modules";
|
|
431
448
|
function moduleSource(module, ref = MODULE_REF) {
|
|
432
449
|
return `${MODULE_SOURCE_BASE}/${module}?ref=${ref}`;
|
|
@@ -441,6 +458,7 @@ function emitToolTf(opts) {
|
|
|
441
458
|
const useSupabase = data === "supabase";
|
|
442
459
|
const useVercel = target === "vercel";
|
|
443
460
|
const useOci = target === "oci";
|
|
461
|
+
const supabaseOverride = opts.tokenOverrides?.SUPABASE_ACCESS_TOKEN;
|
|
444
462
|
const envList = envs.map((e) => `"${e}"`).join(", ");
|
|
445
463
|
const blocks = [];
|
|
446
464
|
const assumes = ["var.cloudflare_zone_id"];
|
|
@@ -455,9 +473,25 @@ function emitToolTf(opts) {
|
|
|
455
473
|
# External tool: app code + deploy live in ${slug}; this manages only its infra here.` : ""}`
|
|
456
474
|
);
|
|
457
475
|
if (useSupabase) {
|
|
476
|
+
const providersLine = supabaseOverride ? `
|
|
477
|
+
providers = { supabase = supabase.${name} }` : "";
|
|
478
|
+
const overrideBlock = supabaseOverride ? `
|
|
479
|
+
|
|
480
|
+
# Multi-account: ${name}'s Supabase lives in a SECOND account \u2014 an aliased provider authenticates
|
|
481
|
+
# with its own token. In infra.yml: TF_VAR_${name}_supabase_access_token: \${{ secrets.${supabaseOverride} }}
|
|
482
|
+
provider "supabase" {
|
|
483
|
+
alias = "${name}"
|
|
484
|
+
access_token = var.${name}_supabase_access_token
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
variable "${name}_supabase_access_token" {
|
|
488
|
+
type = string
|
|
489
|
+
sensitive = true
|
|
490
|
+
description = "Supabase Management API token for ${name}'s account (scoped secret ${supabaseOverride})."
|
|
491
|
+
}` : "";
|
|
458
492
|
blocks.push(`# One Supabase project (schema-per-env), kept declarative + recreatable + kept alive.
|
|
459
493
|
module "${name}_supabase" {
|
|
460
|
-
source = "${moduleSource("supabase", ref)}"
|
|
494
|
+
source = "${moduleSource("supabase", ref)}"${providersLine}
|
|
461
495
|
|
|
462
496
|
name = "${name}"
|
|
463
497
|
project_name = "${name}-db"
|
|
@@ -473,7 +507,7 @@ variable "${name}_supabase_database_password" {
|
|
|
473
507
|
type = string
|
|
474
508
|
sensitive = true
|
|
475
509
|
default = "import-placeholder" # ignored when importing an existing project
|
|
476
|
-
}`);
|
|
510
|
+
}${overrideBlock}`);
|
|
477
511
|
}
|
|
478
512
|
if (useVercel) {
|
|
479
513
|
const env = useSupabase ? `
|
|
@@ -918,8 +952,7 @@ async function gatherSecrets(name, repo, env, prefill) {
|
|
|
918
952
|
for (const pack of packs) {
|
|
919
953
|
console.log(`\u2500\u2500 ${pack.name}${pack.setupUrl ? ` \u2192 ${pack.setupUrl}` : ""}`);
|
|
920
954
|
for (const tok of pack.tokens) {
|
|
921
|
-
const
|
|
922
|
-
const key = tok.envVar.toUpperCase() + (tok.perTool ? suffix : "");
|
|
955
|
+
const key = secretKeyFor(tok, name, entry.tokenOverrides);
|
|
923
956
|
if (key === "GITHUB_TOKEN") {
|
|
924
957
|
console.log(" \xB7 GITHUB_TOKEN \u2014 provided automatically by Actions; skipping");
|
|
925
958
|
continue;
|
|
@@ -1070,7 +1103,16 @@ async function addCommand(args) {
|
|
|
1070
1103
|
} else {
|
|
1071
1104
|
writeFileSync2(
|
|
1072
1105
|
toolTf,
|
|
1073
|
-
emitToolTf({
|
|
1106
|
+
emitToolTf({
|
|
1107
|
+
name,
|
|
1108
|
+
domain: config.domain,
|
|
1109
|
+
lane,
|
|
1110
|
+
target,
|
|
1111
|
+
data,
|
|
1112
|
+
envs,
|
|
1113
|
+
port: entry?.port,
|
|
1114
|
+
tokenOverrides: entry?.tokenOverrides
|
|
1115
|
+
})
|
|
1074
1116
|
);
|
|
1075
1117
|
console.log(`\u2714 wrote infra/${name}.tf (modules: ${providers.join(", ")})`);
|
|
1076
1118
|
}
|
|
@@ -1976,6 +2018,16 @@ function conformanceChecks(t, root) {
|
|
|
1976
2018
|
status: gateable ? "ok" : "warn",
|
|
1977
2019
|
detail: platformPreview ? "vercel per-PR preview + deployment_status verify" : gateable ? void 0 : `no built-in serve for ${t.external ? "an external " : ""}${t.target} tool \u2014 add preview:{ command, \u2026 } so \`greenlight preview ${t.name}\` works`
|
|
1978
2020
|
});
|
|
2021
|
+
const declared = [...t.tokens ?? [], ...Object.values(t.tokenOverrides ?? {})];
|
|
2022
|
+
if (declared.length) {
|
|
2023
|
+
const tag = t.name.toUpperCase().replace(/-/g, "_");
|
|
2024
|
+
const generic = declared.filter((s) => !s.toUpperCase().includes(tag));
|
|
2025
|
+
out.push({
|
|
2026
|
+
name: `${t.name}: token scoping`,
|
|
2027
|
+
status: generic.length ? "warn" : "ok",
|
|
2028
|
+
detail: generic.length ? `not tool-scoped (should contain ${tag}): ${generic.join(", ")}` : `${declared.length} scoped secret name(s)`
|
|
2029
|
+
});
|
|
2030
|
+
}
|
|
1979
2031
|
return out;
|
|
1980
2032
|
}
|
|
1981
2033
|
function runDoctor(config, root) {
|
|
@@ -56,7 +56,16 @@ var ToolSchema = z.object({
|
|
|
56
56
|
// local port (default: tool.port ?? lane default)
|
|
57
57
|
path: z.string().optional()
|
|
58
58
|
// connect path (default: lane default, e.g. `/mcp`)
|
|
59
|
-
}).optional()
|
|
59
|
+
}).optional(),
|
|
60
|
+
// The project-scoped secret names this tool needs (e.g. ['TF_VAR_HEISTMIND_GITHUB_ADMIN_TOKEN']).
|
|
61
|
+
// The convention (docs/tokens-reference.md): a project-scoped secret carries the uppercased tool name.
|
|
62
|
+
// `doctor` warns on a name that doesn't — documentation + conformance, no behavior.
|
|
63
|
+
tokens: z.array(z.string()).optional(),
|
|
64
|
+
// Opt-in per-tool provider-token OVERRIDES (multi-account). Maps a provider's default token env
|
|
65
|
+
// var to an alternate secret name, so this tool authenticates that provider with a SECOND account
|
|
66
|
+
// — e.g. { SUPABASE_ACCESS_TOKEN: 'SUPABASE_ACCESS_TOKEN_HEISTMIND' }. Absent ⇒ unchanged (the
|
|
67
|
+
// default token). `add`/`adopt` emit an aliased provider + scoped var/secret for an overridden token.
|
|
68
|
+
tokenOverrides: z.record(z.string(), z.string()).optional()
|
|
60
69
|
}).superRefine((tool, ctx) => {
|
|
61
70
|
const rule = MATRIX[tool.lane];
|
|
62
71
|
if (!rule.targets.includes(tool.target)) {
|
package/dist/index.js
CHANGED