@marcusrbrown/infra 0.15.0 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +58 -14
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -6949,7 +6949,7 @@ import { goke } from "goke";
6949
6949
  // package.json
6950
6950
  var package_default = {
6951
6951
  name: "@marcusrbrown/infra",
6952
- version: "0.15.0",
6952
+ version: "0.15.1",
6953
6953
  description: "Infrastructure management CLI \u2014 deploy automation, health checks, and MCP bridge",
6954
6954
  keywords: [
6955
6955
  "infra",
@@ -7201,8 +7201,16 @@ function readablePipe(pipe) {
7201
7201
  throw new Error("CLI subprocess did not expose piped output.");
7202
7202
  return pipe;
7203
7203
  }
7204
- async function runCommand(command, args, timeoutMs = COMMAND_TIMEOUT_MS, stdin) {
7205
- const operation = `${command} ${args.join(" ")}`.trim();
7204
+ function redactExactValues(text2, values) {
7205
+ let redacted = text2;
7206
+ for (const value of new Set(values)) {
7207
+ if (value.length > 0)
7208
+ redacted = redacted.split(value).join("[REDACTED]");
7209
+ }
7210
+ return redacted;
7211
+ }
7212
+ async function runCommand(command, args, timeoutMs = COMMAND_TIMEOUT_MS, stdin, env = process.env, redactValues = []) {
7213
+ const operation = redactExactValues(`${command} ${args.join(" ")}`.trim(), redactValues);
7206
7214
  const controller = new AbortController;
7207
7215
  let timeout;
7208
7216
  let child;
@@ -7212,7 +7220,7 @@ async function runCommand(command, args, timeoutMs = COMMAND_TIMEOUT_MS, stdin)
7212
7220
  signal: controller.signal,
7213
7221
  stdout: "pipe",
7214
7222
  stderr: "pipe",
7215
- env: process.env
7223
+ env
7216
7224
  });
7217
7225
  } else {
7218
7226
  child = Bun.spawn([command, ...args], {
@@ -7220,7 +7228,7 @@ async function runCommand(command, args, timeoutMs = COMMAND_TIMEOUT_MS, stdin)
7220
7228
  stdin: new Blob([stdin]).stream(),
7221
7229
  stdout: "pipe",
7222
7230
  stderr: "pipe",
7223
- env: process.env
7231
+ env
7224
7232
  });
7225
7233
  }
7226
7234
  } catch (error) {
@@ -7245,7 +7253,11 @@ async function runCommand(command, args, timeoutMs = COMMAND_TIMEOUT_MS, stdin)
7245
7253
  timeoutPromise
7246
7254
  ]);
7247
7255
  const [stdout, stderr, exitCode] = result;
7248
- return { stdout, stderr, exitCode };
7256
+ return {
7257
+ stdout: redactExactValues(stdout, redactValues),
7258
+ stderr: redactExactValues(stderr, redactValues),
7259
+ exitCode
7260
+ };
7249
7261
  } catch (error) {
7250
7262
  if (controller.signal.aborted)
7251
7263
  throw new Error(`${command} CLI timed out during ${operation}.`);
@@ -9211,8 +9223,32 @@ function parseManifest(raw) {
9211
9223
  throw error;
9212
9224
  }
9213
9225
  }
9214
- async function runAwsCommand(args) {
9215
- return runCommand("aws", args);
9226
+ async function runAwsCommand(args, env, redactValues) {
9227
+ return runCommand("aws", args, undefined, undefined, env, redactValues);
9228
+ }
9229
+ var AWS_CHILD_LOCALE_KEYS = new Set(["LANG", "LANGUAGE"]);
9230
+ function buildAwsChildEnv(manifest, sourceEnv = process.env) {
9231
+ const accessKeyId = sourceEnv.AGENT_AWS_ACCESS_KEY_ID?.trim();
9232
+ const secretAccessKey = sourceEnv.AGENT_AWS_SECRET_ACCESS_KEY?.trim();
9233
+ if (!accessKeyId || !secretAccessKey) {
9234
+ throw new Error("Dedicated AWS credentials are required for agent storage preflight. Set AGENT_AWS_ACCESS_KEY_ID and AGENT_AWS_SECRET_ACCESS_KEY in the operator-local environment.");
9235
+ }
9236
+ const childEnv = {};
9237
+ for (const [key, value] of Object.entries(sourceEnv)) {
9238
+ if (value !== undefined && (key === "PATH" || key === "HOME" || key === "TMPDIR" || AWS_CHILD_LOCALE_KEYS.has(key) || key.startsWith("LC_"))) {
9239
+ childEnv[key] = value;
9240
+ }
9241
+ }
9242
+ childEnv.AWS_CONFIG_FILE = "/dev/null";
9243
+ childEnv.AWS_SHARED_CREDENTIALS_FILE = "/dev/null";
9244
+ childEnv.AWS_ACCESS_KEY_ID = accessKeyId;
9245
+ childEnv.AWS_SECRET_ACCESS_KEY = secretAccessKey;
9246
+ childEnv.AWS_REGION = sourceEnv.AGENT_AWS_REGION?.trim() || manifest.bucket_region;
9247
+ childEnv.AWS_DEFAULT_REGION = childEnv.AWS_REGION;
9248
+ const sessionToken = sourceEnv.AGENT_AWS_SESSION_TOKEN?.trim();
9249
+ if (sessionToken)
9250
+ childEnv.AWS_SESSION_TOKEN = sessionToken;
9251
+ return childEnv;
9216
9252
  }
9217
9253
  async function runProvisionerCommand(manifest, options) {
9218
9254
  const args = ["run", "provision:agent", "--", "--teardown", "--manifest", "-"];
@@ -9256,8 +9292,16 @@ function canonicalBucketRegion(location) {
9256
9292
  return "eu-west-1";
9257
9293
  return String(location);
9258
9294
  }
9259
- async function verifyProvisionedResources(manifest, runAws = runAwsCommand) {
9260
- const roleResult = await runAws([
9295
+ async function verifyProvisionedResources(manifest, runAws, awsCommand = runAwsCommand) {
9296
+ let executeAws;
9297
+ if (runAws) {
9298
+ executeAws = runAws;
9299
+ } else {
9300
+ const awsEnv = buildAwsChildEnv(manifest);
9301
+ const redactValues = [awsEnv.AWS_ACCESS_KEY_ID, awsEnv.AWS_SECRET_ACCESS_KEY, awsEnv.AWS_SESSION_TOKEN].filter((value) => value !== undefined && value.length > 0);
9302
+ executeAws = (args) => awsCommand(args, awsEnv, redactValues);
9303
+ }
9304
+ const roleResult = await executeAws([
9261
9305
  "iam",
9262
9306
  "get-role",
9263
9307
  "--role-name",
@@ -9277,7 +9321,7 @@ async function verifyProvisionedResources(manifest, runAws = runAwsCommand) {
9277
9321
  if (roleArn !== manifest.role_arn) {
9278
9322
  throw new Error(`AWS IAM role readback does not match the handoff manifest for ${manifest.owner}/${manifest.repo}; run the provisioner first.`);
9279
9323
  }
9280
- const bucketResult = await runAws([
9324
+ const bucketResult = await executeAws([
9281
9325
  "s3api",
9282
9326
  "head-bucket",
9283
9327
  "--bucket",
@@ -9289,7 +9333,7 @@ async function verifyProvisionedResources(manifest, runAws = runAwsCommand) {
9289
9333
  if (bucketResult.exitCode !== 0) {
9290
9334
  throw new Error(`AWS S3 bucket ${manifest.bucket} is absent, inaccessible, or owned by another account; run the provisioner first. ${bucketResult.stderr.trim()}`.trim());
9291
9335
  }
9292
- const locationResult = await runAws([
9336
+ const locationResult = await executeAws([
9293
9337
  "s3api",
9294
9338
  "get-bucket-location",
9295
9339
  "--bucket",
@@ -9372,7 +9416,7 @@ async function prepareStorageSetup(repo, options, deps = {}) {
9372
9416
  const rawManifest = await loadManifest(options.manifest ?? "-");
9373
9417
  const manifest = parseManifest(rawManifest);
9374
9418
  await verifyRepoIdentity(repo, manifest, deps.runGh ?? runGh);
9375
- const verifyResources = deps.verifyResources ?? ((value) => verifyProvisionedResources(value, deps.runAws));
9419
+ const verifyResources = deps.verifyResources ?? ((value) => verifyProvisionedResources(value, deps.runAws, deps.runAwsCommand));
9376
9420
  await verifyResources(manifest);
9377
9421
  await verifyOidcSubject(repo, deps.runGh ?? runGh);
9378
9422
  const verifyWorkflowSetup = deps.verifyWorkflow ?? ((value, handoff) => verifyWorkflow(value, handoff, { runGh: deps.runGh }));
@@ -9454,7 +9498,7 @@ async function runStorageTeardown(repo, options = {}, deps = {}) {
9454
9498
  await runProvisioner(rawManifest, { purgeState: options.purgeState, plan: options.plan ?? false });
9455
9499
  }
9456
9500
  function registerAgentStorageCommand(cli) {
9457
- cli.command("agent storage", "Verify the provisioned S3 handoff and wire non-secret GitHub storage variables.").option("--repo [repo]", z6.string().describe("Target GitHub repository in owner/repo format.")).option("--manifest [manifest]", z6.string().default("-").describe("Handoff manifest path, or - to read JSON from stdin.")).option("--plan", z6.boolean().optional().describe("Verify and report storage writes without mutating GitHub.")).option("--dry-run", z6.boolean().optional().describe("Alias for --plan.")).example("infra agent storage --repo owner/repo --manifest handoff.json").example("cat handoff.json | infra agent storage --repo owner/repo --manifest -").action(async (options) => {
9501
+ cli.command("agent storage", "Verify the provisioned S3 handoff and wire non-secret GitHub storage variables. AWS readback requires AGENT_AWS_ACCESS_KEY_ID and AGENT_AWS_SECRET_ACCESS_KEY and ignores ambient AWS_* values.").option("--repo [repo]", z6.string().describe("Target GitHub repository in owner/repo format.")).option("--manifest [manifest]", z6.string().default("-").describe("Handoff manifest path, or - to read JSON from stdin.")).option("--plan", z6.boolean().optional().describe("Verify and report storage writes without mutating GitHub.")).option("--dry-run", z6.boolean().optional().describe("Alias for --plan.")).example("infra agent storage --repo owner/repo --manifest handoff.json").example("cat handoff.json | infra agent storage --repo owner/repo --manifest -").action(async (options) => {
9458
9502
  if (!options.repo) {
9459
9503
  throw new Error("--repo is required for agent storage.");
9460
9504
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marcusrbrown/infra",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "description": "Infrastructure management CLI — deploy automation, health checks, and MCP bridge",
5
5
  "keywords": [
6
6
  "infra",