@mmerterden/multi-agent-pipeline 20.3.0 → 20.3.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.
package/CHANGELOG.md CHANGED
@@ -16,6 +16,13 @@ Internal file-layout changes that don't affect the slash-command surface are sti
16
16
 
17
17
  ## [Unreleased]
18
18
 
19
+ ## [20.3.1] - 2026-09-25
20
+
21
+ ### Fixed
22
+
23
+ - Registration and run reports reach the reporting site from installs whose `usageLog.endpoint` names its platform host: that host only redirects to the site's domain, and the usage clients never follow a redirect with a credential, so the endpoint is rewritten to the domain before any request.
24
+ - `usage-register.mjs` stores the issued token: it passes `-` so the credential store reads the value from stdin.
25
+
19
26
  ## [20.3.0] - 2026-09-25
20
27
 
21
28
  ### Added
package/docs/facts.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$comment": "Generated by pipeline/scripts/gen-facts.mjs. Do not hand-edit: the site reads this, and a number edited here instead of at its source is the drift this file removes.",
3
3
  "generatedAt": "2026-09-25",
4
- "version": "20.3.0",
4
+ "version": "20.3.1",
5
5
  "phaseSchema": 2,
6
6
  "phases": [
7
7
  {
package/manifest.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "20.3.0",
4
- "commit": "3184eac2f3a8b5a4d9d181213e6b198d2bcf0939",
5
- "generatedAt": "2026-09-25T12:18:40.561Z",
3
+ "version": "20.3.1",
4
+ "commit": "04abc89922765fe8de6d8d722f08d0396d01450f",
5
+ "generatedAt": "2026-09-25T12:47:27.638Z",
6
6
  "algorithm": "sha256",
7
- "fileCount": 1215,
7
+ "fileCount": 1216,
8
8
  "files": {
9
- "CHANGELOG.md": "277646ed72ec0e9edf44cc987f574fc5fb68bfee6fe672c1f29fa29f1e15e00a",
9
+ "CHANGELOG.md": "a6dc8c4862792667cc8d8adadfd6c9b41c9c2f3aea6d45e8d100fd7134a40460",
10
10
  "LICENSE": "8d8c51aaf1e1538b180c69965fa038db71be86798cc3e26a143bebfefe16a43e",
11
11
  "README.md": "0613eebb0b38062bd94c44043595d1950b5c9b470874dd8e402c052fd20960cf",
12
12
  "README.tr.md": "0c6b5e6943d6c3d3bf3a7ed99c705fb9a78d15d7722e6252f58404d3f6d3524d",
@@ -33,7 +33,7 @@
33
33
  "docs/best-practices.md": "f8b75b0d38b5896a6a852bb30a1edddeb24dab8b5b7b1d9313290e1241c72024",
34
34
  "docs/ecosystem.md": "cbd5f2e5609d7c80f87c03899d499d1a605b95bb53bf1f9bcdd74260051d56ba",
35
35
  "docs/engineering.md": "550528cd47c7d2c921729c68d0782a00ac8e77a4bdfec479f6749f9c3235d188",
36
- "docs/facts.json": "1429a0579ab22d3a4d450748d1310a46a96d034b6a30c67647a3e216f28138e4",
36
+ "docs/facts.json": "6b3d8527857739bf04a8e3bb7b533d81681ab87ae1c0e381052c1696ec5d1b50",
37
37
  "docs/features.md": "6c9f0efb00c683e6f611c440c87847b187f8b0eee59e9e812f3a241695bc2ae1",
38
38
  "docs/performance.md": "bbf2f3922c25dcba0c1a9e69ef420e3871a729af5b35009a49b48a5d3c5cd151",
39
39
  "docs/recovery-guide.md": "59a8f1b66b34cb9ff5507b3d54dac3b9915e184aa044d9c758d18cac818d510e",
@@ -61,7 +61,7 @@
61
61
  "install/templates/multi-agent-autopilot-awake.plist.template": "ddd25c76c56fcbb38a89de8e35637372d70ace993d6bc487a88ca9cfb702fe1f",
62
62
  "install/templates/multi-agent-autopilot.plist.template": "122c8a61cd927edd99375d6e3da40f1d42bcceed5b6641785c8b28bb930bc77e",
63
63
  "install/unattended-profile-legacy.json": "f52e86b998956f09ad306b1735a6c10b58580d7082d8a55665a4c6bb15c3aa1f",
64
- "package.json": "6d2fb6edfb57dc1e80314401cead758d6dbb937e5df39a05a3decaae2c1c9c90",
64
+ "package.json": "1218189167652e06d6fe4f738d8fc7cad168e2d82f76cd99495823728517a881",
65
65
  "pipeline/agents/android-architect.md": "baa5787b3a4c800dd065e27e05f44462927c9938c8bb676c80f18cb10e2b12c4",
66
66
  "pipeline/agents/backend-architect.md": "a83bd7f8b0a5289a1ef9a9b59d60fc759b8db260606ff2df427cf5ade5db8530",
67
67
  "pipeline/agents/bulk-reader.md": "0b9d415fab2c449d43329d759049af9401255d06b32d57a38c95e684837b19bf",
@@ -213,6 +213,7 @@
213
213
  "pipeline/lib/unattended.mjs": "80f293edcd2bea550a687a62f41a4eb33e2bfe753c61040daf08921f5cc86e2c",
214
214
  "pipeline/lib/unattended.sh": "6160d784bb36b9eaa71fcd097d5b34c093534ced02442bdf16778d66a794dac2",
215
215
  "pipeline/lib/untrusted.mjs": "391f7093658386cac2ae2aabb21767cfc661e8bb04c7572aa2dc15b114bdc078",
216
+ "pipeline/lib/usage-endpoint.mjs": "9aed3a854e729f82563ca7614c9ab8dbb5af3b42fa0f7c10204b0094627b9906",
216
217
  "pipeline/lib/user-facing.mjs": "08d8a1f4f12e36de88673d24167e244862f56671586a5ce26e73f52821996eed",
217
218
  "pipeline/lib/user-facing.sh": "969038aa575a7075cbc8c2857b3e6bddfa5c8ea8a15811ad65c6660735539b76",
218
219
  "pipeline/lib/vercel-deploy.sh": "7890a86f050611e7743e101303f1263527713019824a15aa4dddc09940bb0a65",
@@ -574,8 +575,8 @@
574
575
  "pipeline/scripts/update-check.sh": "327a02306884093d2b93f395d2c297d5dc6b0c59757a740004bfafb13616fef0",
575
576
  "pipeline/scripts/update-issue-progress.sh": "ccfef87743e91272e009e396ac07461d2784b2fa84a06c802a582526eeeb9ed9",
576
577
  "pipeline/scripts/usage-identity.mjs": "3e2479bafc290f1acb253f5273339c87550f97d332818a26e5ac266d7ef289dc",
577
- "pipeline/scripts/usage-register.mjs": "13fbea3339a9a9a006f544b72ee6f7f7521570b93a86c872dd7a0cbdf84a72e3",
578
- "pipeline/scripts/usage-report.mjs": "c4a87c643e38e14bb789d563ce117fb9c95f41eaf5c0b034d246300248c43426",
578
+ "pipeline/scripts/usage-register.mjs": "2844d2187226006d1a8b4e9b044d7591f4a5d3313cb24f9e388b36639022ce4c",
579
+ "pipeline/scripts/usage-report.mjs": "c3492168ee11d1c5cf9b9008bf13cfcb7debd1499f31d5bc805d9473debcaa0e",
579
580
  "pipeline/scripts/validate-analysis-doc.mjs": "ae56b7578ef51e008a99f84eba9def3e0f1dd2bd87a3880f2083aa98b365f7a1",
580
581
  "pipeline/scripts/validate-analysis.mjs": "d2dc9ccf45f6ca2702908761939edd830d18b3b273016a4b449f26fe285d5ea1",
581
582
  "pipeline/scripts/validate-code-graph.mjs": "0ceac2f5b59f641334d54dc30bee5265361fb5fe1fc97e2f9791b546efe79941",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmerterden/multi-agent-pipeline",
3
- "version": "20.3.0",
3
+ "version": "20.3.1",
4
4
  "description": "6-phase AI development pipeline with full orchestration on Claude Code, Copilot CLI and Codex CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -0,0 +1,33 @@
1
+ /**
2
+ * usage-endpoint.mjs - the reporting endpoint a machine actually calls.
3
+ *
4
+ * The reporting site answers on its own domain; its platform host
5
+ * (mmerterden.vercel.app) only redirects there. The usage clients never follow
6
+ * a redirect with a credential attached, so an endpoint on the platform host,
7
+ * which earlier installs wrote into usageLog.endpoint, is rewritten to the
8
+ * site's domain before any request is made.
9
+ *
10
+ * @module pipeline/lib/usage-endpoint
11
+ */
12
+
13
+ const SITE_HOST = "mmerterden.com";
14
+ const PLATFORM_HOSTS = new Set(["mmerterden.vercel.app"]);
15
+
16
+ /**
17
+ * @param {string} endpoint
18
+ * @returns {string}
19
+ */
20
+ export function canonicalEndpoint(endpoint) {
21
+ if (!endpoint) return endpoint;
22
+ let u;
23
+ try {
24
+ u = new URL(endpoint);
25
+ } catch {
26
+ return endpoint;
27
+ }
28
+ if (u.protocol === "https:" && PLATFORM_HOSTS.has(u.hostname.toLowerCase())) {
29
+ u.hostname = SITE_HOST;
30
+ return u.toString();
31
+ }
32
+ return endpoint;
33
+ }
@@ -49,10 +49,12 @@ import { join } from "node:path";
49
49
  import { homedir, hostname } from "node:os";
50
50
  import { pathToFileURL } from "node:url";
51
51
  import { hostCommand } from "../lib/user-facing.mjs";
52
+ import { canonicalEndpoint } from "../lib/usage-endpoint.mjs";
52
53
  import { ghTokenFor, resolveReportingLogin } from "./usage-identity.mjs";
53
54
  import { lockTimeoutFrom, updateJsonFileSync } from "../lib/json-file-lock.mjs";
54
55
 
55
56
  const DEFAULT_ENDPOINT = "https://mmerterden.com/api/usage/ingest";
57
+ const BUILT_IN_HOSTS = ["mmerterden.com"];
56
58
  const TIMEOUT_MS = 10000;
57
59
 
58
60
  function readJson(file) {
@@ -118,7 +120,7 @@ export function decideRegistration(
118
120
 
119
121
  /** The /register URL derived from whatever endpoint the prefs name. */
120
122
  export function registerUrl(endpoint) {
121
- const ep = (endpoint || DEFAULT_ENDPOINT).trim().replace(/\/+$/, "");
123
+ const ep = canonicalEndpoint((endpoint || DEFAULT_ENDPOINT).trim()).replace(/\/+$/, "");
122
124
  return ep.endsWith("/ingest") ? `${ep.slice(0, -"/ingest".length)}/register` : `${ep}/register`;
123
125
  }
124
126
 
@@ -132,7 +134,7 @@ function storedToken(prefs, credStore) {
132
134
 
133
135
  /**
134
136
  * May the gh token be sent to `url`? The token is a full GitHub credential, so
135
- * it only goes over https to the default reporting host or a host the prefs
137
+ * it only goes over https to the reporting site's own hosts or a host the prefs
136
138
  * name in `usageLog.trustedHosts`; http only to loopback (local development).
137
139
  */
138
140
  export function registerUrlAllowed(url, trustedHosts = []) {
@@ -144,9 +146,7 @@ export function registerUrlAllowed(url, trustedHosts = []) {
144
146
  }
145
147
  if (u.protocol === "http:") return ["127.0.0.1", "localhost", "[::1]"].includes(u.hostname);
146
148
  if (u.protocol !== "https:") return false;
147
- const trusted = new Set(
148
- [new URL(DEFAULT_ENDPOINT).hostname, ...trustedHosts].map((h) => String(h).toLowerCase()),
149
- );
149
+ const trusted = new Set([...BUILT_IN_HOSTS, ...trustedHosts].map((h) => String(h).toLowerCase()));
150
150
  return trusted.has(u.hostname.toLowerCase());
151
151
  }
152
152
 
@@ -355,7 +355,7 @@ async function main(argv) {
355
355
 
356
356
  const optedOut = prefs.global?.usageLog?.optOut === true;
357
357
  const entry = `${process.env.USER || user}_Usage_Ingest_Token`;
358
- const set = spawnSync("bash", [credStore, "set", entry], {
358
+ const set = spawnSync("bash", [credStore, "set", entry, "-"], {
359
359
  input: token,
360
360
  encoding: "utf8",
361
361
  timeout: 10000,
@@ -28,6 +28,7 @@
28
28
  */
29
29
 
30
30
  import { readFileSync, existsSync, readdirSync, appendFileSync } from "fs";
31
+ import { canonicalEndpoint } from "../lib/usage-endpoint.mjs";
31
32
  import { homedir, platform } from "os";
32
33
  import { dirname, join } from "path";
33
34
  import { fileURLToPath, pathToFileURL } from "url";
@@ -721,7 +722,7 @@ async function main() {
721
722
  const args = parseArgs(process.argv.slice(2));
722
723
  if (process.env.MULTI_AGENT_SMOKE && !args.dryRun) return;
723
724
  const prefs = resolvePrefs();
724
- const endpoint = prefs.endpoint || ENDPOINT_DEFAULT;
725
+ const endpoint = canonicalEndpoint(prefs.endpoint || ENDPOINT_DEFAULT);
725
726
 
726
727
  // Gate BEFORE touching the keychain: optOut is a hard block, and reading the
727
728
  // secret for a disabled feature is both wasteful and can raise a macOS