@marcusrbrown/infra 0.24.0 → 0.25.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 +61 -18
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -6950,7 +6950,7 @@ import { goke } from "goke";
6950
6950
  // package.json
6951
6951
  var package_default = {
6952
6952
  name: "@marcusrbrown/infra",
6953
- version: "0.24.0",
6953
+ version: "0.25.1",
6954
6954
  description: "Infrastructure management CLI \u2014 deploy automation, health checks, and MCP bridge",
6955
6955
  keywords: [
6956
6956
  "infra",
@@ -6993,7 +6993,7 @@ var package_default = {
6993
6993
  },
6994
6994
  devDependencies: {
6995
6995
  "@marcusrbrown/infra-shared": "workspace:*",
6996
- "@modelcontextprotocol/sdk": "1.31.0",
6996
+ "@modelcontextprotocol/sdk": "1.32.1",
6997
6997
  yaml: "2.9.1"
6998
6998
  },
6999
6999
  engines: {
@@ -8406,7 +8406,7 @@ var CONTENT_EVENTS = new Set([
8406
8406
  "discussion_comment"
8407
8407
  ]);
8408
8408
  var VERIFIED_ACTION_SHA_BY_LAYOUT = {
8409
- "fro-bot/agent@v0.96.0": "c29ac295b8da06768b140c32e5bd0ae3aff45dc6"
8409
+ "fro-bot/agent@v0.119.2": "83ed683da24111bd1474eb4bda39715ed5799bc2"
8410
8410
  };
8411
8411
  var REQUIRED_S3_INPUTS = {
8412
8412
  "s3-backup": undefined,
@@ -8849,9 +8849,16 @@ function hasHandoff(job) {
8849
8849
  const serialized = stringifyUnknown(job);
8850
8850
  return /needs\.[\w-]+\.outputs\./.test(serialized) || /actions\/(?:download-artifact|upload-artifact|cache|cache\/restore|cache\/save)@/.test(serialized);
8851
8851
  }
8852
+ var STATIC_AWS_CREDENTIAL_PATTERN = /AWS_ACCESS_KEY_ID|AWS_SECRET_ACCESS_KEY|aws-access-key-id|aws-secret-access-key/;
8853
+ function withoutBlankedCredentialKeys(value) {
8854
+ if (Array.isArray(value))
8855
+ return value.map(withoutBlankedCredentialKeys);
8856
+ if (!isMapping(value))
8857
+ return value;
8858
+ return Object.fromEntries(Object.entries(value).filter(([key, entry]) => !(entry === "" && STATIC_AWS_CREDENTIAL_PATTERN.test(key))).map(([key, entry]) => [key, withoutBlankedCredentialKeys(entry)]));
8859
+ }
8852
8860
  function hasStaticAwsCredentials(job) {
8853
- const serialized = stringifyUnknown(job);
8854
- return /AWS_ACCESS_KEY_ID|AWS_SECRET_ACCESS_KEY|aws-access-key-id|aws-secret-access-key/.test(serialized);
8861
+ return STATIC_AWS_CREDENTIAL_PATTERN.test(stringifyUnknown(withoutBlankedCredentialKeys(job)));
8855
8862
  }
8856
8863
  function ancestors(jobId, analyses) {
8857
8864
  const visited = new Set;
@@ -8910,7 +8917,7 @@ function formatStorageWorkflowSnippet() {
8910
8917
  with:
8911
8918
  role-to-assume: \${{ vars.FRO_BOT_S3_ROLE_TO_ASSUME }}
8912
8919
  aws-region: \${{ vars.FRO_BOT_S3_REGION }}
8913
- - uses: fro-bot/agent@c29ac295b8da06768b140c32e5bd0ae3aff45dc6
8920
+ - uses: fro-bot/agent@83ed683da24111bd1474eb4bda39715ed5799bc2
8914
8921
  with:
8915
8922
  s3-backup: true
8916
8923
  s3-bucket: \${{ vars.FRO_BOT_S3_BUCKET }}
@@ -8946,10 +8953,6 @@ function environmentResult(environmentValue, branchPoliciesValue) {
8946
8953
  violations.push("GitHub Environment fro-bot-storage is missing or returned an invalid response.");
8947
8954
  return { verified: false, violations };
8948
8955
  }
8949
- const protectionRules = Array.isArray(environmentValue.protection_rules) ? environmentValue.protection_rules : [];
8950
- const hasReviewer = protectionRules.some((rule) => isMapping(rule) && rule.type === "required_reviewers" && Array.isArray(rule.reviewers) && rule.reviewers.length > 0);
8951
- if (!hasReviewer)
8952
- violations.push("GitHub Environment fro-bot-storage has no required reviewer protection rule.");
8953
8956
  const branchPolicy = environmentValue.deployment_branch_policy;
8954
8957
  if (!isMapping(branchPolicy) || branchPolicy.protected_branches !== false || branchPolicy.custom_branch_policies !== true) {
8955
8958
  violations.push("GitHub Environment fro-bot-storage does not use a custom deployment-branch policy.");
@@ -9135,7 +9138,7 @@ var STORAGE_VARIABLE_NAMES = {
9135
9138
  prefix: "FRO_BOT_S3_PREFIX",
9136
9139
  expectedBucketOwner: "FRO_BOT_S3_EXPECTED_BUCKET_OWNER"
9137
9140
  };
9138
- var KNOWN_KEY_LAYOUT_VERSION = "fro-bot/agent@v0.96.0";
9141
+ var KNOWN_KEY_LAYOUT_VERSION = "fro-bot/agent@v0.119.2";
9139
9142
  var manifestSchema = z6.object({
9140
9143
  owner: z6.string().min(1),
9141
9144
  repo: z6.string().min(1),
@@ -11931,8 +11934,29 @@ function validateGatewayHost(host) {
11931
11934
  return host;
11932
11935
  }
11933
11936
 
11937
+ // src/commands/gateway/volume.ts
11938
+ var GATEWAY_COMPOSE_PROJECT_NAME = "fro-bot";
11939
+ var MITMPROXY_CERTS_VOLUME = `${GATEWAY_COMPOSE_PROJECT_NAME}_mitmproxy-certs`;
11940
+ var VOLUME_EXISTS_SENTINEL = "EXISTS";
11941
+ var VOLUME_MISSING_SENTINEL = "MISSING";
11942
+ function volumeProbeCommand(volume) {
11943
+ return `docker volume inspect ${volume} >/dev/null 2>&1 && echo ${VOLUME_EXISTS_SENTINEL} || echo ${VOLUME_MISSING_SENTINEL}`;
11944
+ }
11945
+ function checkVolumeProbeResult(volume, result) {
11946
+ if (result.exitCode !== 0) {
11947
+ return `Could not verify Docker volume ${volume} on the gateway host (exit ${result.exitCode}): ${result.stderr.trim() || "unknown error"}`;
11948
+ }
11949
+ const sentinel = result.stdout.trim();
11950
+ if (sentinel === VOLUME_EXISTS_SENTINEL) {
11951
+ return;
11952
+ }
11953
+ if (sentinel === VOLUME_MISSING_SENTINEL) {
11954
+ return `Docker volume ${volume} does not exist on the gateway host; refusing to continue (it would be created empty as a side effect).`;
11955
+ }
11956
+ return `Could not verify Docker volume ${volume} on the gateway host: unrecognized probe output`;
11957
+ }
11958
+
11934
11959
  // src/commands/gateway/backup.ts
11935
- var MITMPROXY_CERTS_VOLUME = "mitmproxy-certs";
11936
11960
  var CA_CERT_FILE = "mitmproxy-ca-cert.pem";
11937
11961
  var CA_KEY_FILE = "mitmproxy-ca.pem";
11938
11962
  var DEFAULT_OUTPUT = "apps/gateway/.local/mitmproxy-ca.tar";
@@ -11945,7 +11969,7 @@ async function backupGatewayCa(opts, spawn = defaultSpawn3, printErr) {
11945
11969
  return { ok: false, error: "Only CA backup is currently supported; --no-include-ca is not accepted" };
11946
11970
  }
11947
11971
  validateGatewayHost(opts.host);
11948
- const sshCmd = [
11972
+ const sshCommand = (remote) => [
11949
11973
  "ssh",
11950
11974
  "-o",
11951
11975
  "BatchMode=yes",
@@ -11953,13 +11977,28 @@ async function backupGatewayCa(opts, spawn = defaultSpawn3, printErr) {
11953
11977
  "StrictHostKeyChecking=yes",
11954
11978
  ...buildKnownHostsArgs(),
11955
11979
  `root@${opts.host}`,
11956
- `docker run --rm -v ${MITMPROXY_CERTS_VOLUME}:/src:ro alpine tar -cf - -C /src ${CA_CERT_FILE} ${CA_KEY_FILE}`
11980
+ remote
11957
11981
  ];
11958
11982
  const env = {
11959
11983
  PATH: process.env.PATH ?? "/usr/bin:/bin",
11960
11984
  HOME: process.env.HOME ?? "/tmp",
11961
11985
  ...process.env.SSH_AUTH_SOCK ? { SSH_AUTH_SOCK: process.env.SSH_AUTH_SOCK } : {}
11962
11986
  };
11987
+ const probe = spawn(sshCommand(volumeProbeCommand(MITMPROXY_CERTS_VOLUME)), { env, stdout: "pipe", stderr: "pipe" });
11988
+ const [probeStdout, probeStderr, probeExit] = await Promise.all([
11989
+ new Response(probe.stdout).text(),
11990
+ new Response(probe.stderr).text(),
11991
+ probe.exited
11992
+ ]);
11993
+ const probeError = checkVolumeProbeResult(MITMPROXY_CERTS_VOLUME, {
11994
+ stdout: probeStdout,
11995
+ stderr: probeStderr,
11996
+ exitCode: probeExit
11997
+ });
11998
+ if (probeError !== undefined) {
11999
+ return { ok: false, error: probeError };
12000
+ }
12001
+ const sshCmd = sshCommand(`docker run --rm -v ${MITMPROXY_CERTS_VOLUME}:/src:ro alpine tar -cf - -C /src ${CA_CERT_FILE} ${CA_KEY_FILE}`);
11963
12002
  const child = spawn(sshCmd, { env, stdout: "pipe", stderr: "pipe" });
11964
12003
  const [tarBytes, stderrText, exitCode] = await Promise.all([
11965
12004
  new Response(child.stdout).arrayBuffer(),
@@ -12228,7 +12267,6 @@ function registerGatewayLogs(cli) {
12228
12267
 
12229
12268
  // src/commands/gateway/restore.ts
12230
12269
  import { z as z25 } from "zod";
12231
- var MITMPROXY_CERTS_VOLUME2 = "mitmproxy-certs";
12232
12270
  var CA_CERT_FILE2 = "mitmproxy-ca-cert.pem";
12233
12271
  var CA_KEY_FILE2 = "mitmproxy-ca.pem";
12234
12272
  var COMPOSE_PROJECT_DIR4 = "/opt/gateway/deploy";
@@ -12297,6 +12335,11 @@ async function restoreGatewayCa(opts, spawn = defaultSpawn4) {
12297
12335
  if (archiveError !== undefined) {
12298
12336
  return { ok: false, error: archiveError };
12299
12337
  }
12338
+ const probeResult = await spawnText(spawn, sshCommand(opts.host, volumeProbeCommand(MITMPROXY_CERTS_VOLUME)), env);
12339
+ const probeError = checkVolumeProbeResult(MITMPROXY_CERTS_VOLUME, probeResult);
12340
+ if (probeError !== undefined) {
12341
+ return { ok: false, error: probeError };
12342
+ }
12300
12343
  const mktempResult = await spawnText(spawn, sshCommand(opts.host, "mktemp -t gateway-ca-restore-XXXXXX.tar"), env);
12301
12344
  if (mktempResult.exitCode !== 0) {
12302
12345
  return {
@@ -12333,7 +12376,7 @@ async function restoreGatewayCa(opts, spawn = defaultSpawn4) {
12333
12376
  };
12334
12377
  return primaryError;
12335
12378
  }
12336
- const extractResult = await spawnText(spawn, sshCommand(opts.host, `docker run --rm -v ${MITMPROXY_CERTS_VOLUME2}:/dst -v ${tmpRemote}:/src.tar:ro alpine sh -c 'tar -xf /src.tar -C /dst'`), env);
12379
+ const extractResult = await spawnText(spawn, sshCommand(opts.host, `docker run --rm -v ${MITMPROXY_CERTS_VOLUME}:/dst -v ${tmpRemote}:/src.tar:ro alpine sh -c 'tar -xf /src.tar -C /dst'`), env);
12337
12380
  if (extractResult.exitCode !== 0) {
12338
12381
  primaryError = {
12339
12382
  ok: false,
@@ -12350,8 +12393,8 @@ async function restoreGatewayCa(opts, spawn = defaultSpawn4) {
12350
12393
  return primaryError;
12351
12394
  }
12352
12395
  const [certResult, keyResult] = await Promise.all([
12353
- spawnText(spawn, sshCommand(opts.host, `docker run --rm -v ${MITMPROXY_CERTS_VOLUME2}:/src:ro alpine cat /src/${CA_CERT_FILE2}`), env),
12354
- spawnText(spawn, sshCommand(opts.host, `docker run --rm -v ${MITMPROXY_CERTS_VOLUME2}:/src:ro alpine cat /src/${CA_KEY_FILE2}`), env)
12396
+ spawnText(spawn, sshCommand(opts.host, `docker run --rm -v ${MITMPROXY_CERTS_VOLUME}:/src:ro alpine cat /src/${CA_CERT_FILE2}`), env),
12397
+ spawnText(spawn, sshCommand(opts.host, `docker run --rm -v ${MITMPROXY_CERTS_VOLUME}:/src:ro alpine cat /src/${CA_KEY_FILE2}`), env)
12355
12398
  ]);
12356
12399
  if (certResult.exitCode !== 0 || keyResult.exitCode !== 0) {
12357
12400
  primaryError = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marcusrbrown/infra",
3
- "version": "0.24.0",
3
+ "version": "0.25.1",
4
4
  "description": "Infrastructure management CLI — deploy automation, health checks, and MCP bridge",
5
5
  "keywords": [
6
6
  "infra",
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "devDependencies": {
45
45
  "@marcusrbrown/infra-shared": "workspace:*",
46
- "@modelcontextprotocol/sdk": "1.31.0",
46
+ "@modelcontextprotocol/sdk": "1.32.1",
47
47
  "yaml": "2.9.1"
48
48
  },
49
49
  "engines": {