clearotron 0.3.3 → 0.4.0-beta.0

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 (43) hide show
  1. package/INSTALL.md +1 -14
  2. package/bin/brandowner.mjs +5 -5
  3. package/bin/connect.mjs +4 -4
  4. package/bin/onboard.mjs +5 -7
  5. package/bin/start.mjs +20 -5
  6. package/build-info.json +2 -2
  7. package/driver/CHANGELOG.md +18 -0
  8. package/driver/company-bundle.mjs +11 -18
  9. package/driver/door-call-verdict.mjs +27 -0
  10. package/driver/driver.config.mjs +1 -2
  11. package/driver/package.json +1 -1
  12. package/driver/pipeline.mjs +14 -5
  13. package/driver/portal-mcp-client.mjs +1 -1
  14. package/driver/portal-request-origin.mjs +79 -0
  15. package/driver/portal-service.mjs +43 -15
  16. package/driver/profile-page.html +9 -13
  17. package/driver/profile-service.mjs +25 -13
  18. package/driver/profiles.mjs +17 -4
  19. package/driver/publish/index.mjs +13 -1
  20. package/driver/publish/render.mjs +13 -2
  21. package/driver/publish/search-depth.mjs +4 -2
  22. package/driver/run-economics.mjs +7 -18
  23. package/driver/stages.mjs +1 -1
  24. package/driver/suite-census.json +61 -31
  25. package/driver/tokens.mjs +18 -10
  26. package/mcp-server/CHANGELOG.md +4 -0
  27. package/mcp-server/lib/audit.mjs +9 -2
  28. package/mcp-server/lib/http-handler.mjs +7 -3
  29. package/mcp-server/mint-token.mjs +8 -6
  30. package/mcp-server/package.json +1 -1
  31. package/mcp-server/server.mjs +10 -0
  32. package/package.json +1 -1
  33. package/portal-ui/dist/assets/{index-GBbbyQxc.js → index-DVtz44vH.js} +57 -8
  34. package/portal-ui/dist/index.html +1 -1
  35. package/portal-ui/package.json +1 -1
  36. package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
  37. package/providers/oauth-mcp-bridge/package.json +1 -1
  38. package/scripts/freeze-example-run.mjs +1 -1
  39. package/scripts/live-surface-check.mjs +11 -2
  40. package/scripts/release-entry-catch-up.mjs +142 -0
  41. package/shared/client-door.mjs +15 -8
  42. package/shared/scope.mjs +25 -10
  43. package/shared/store-in-repo.mjs +38 -17
@@ -282,6 +282,7 @@ export function commitWithAuditRow({ audit, gitCommit, files, message, by, row }
282
282
  // That makes the failure resumable, and it makes "a concurrent editor's staged work survives" true by
283
283
  // construction rather than by a careful reset that has to guess whose paths are whose.
284
284
  import { execFileSync } from "node:child_process";
285
+ import { BRAND } from "./brand.mjs";
285
286
 
286
287
  // Lock contention is TRANSIENT and worth retrying. A hook rejection and a full disk are not: retrying
287
288
  // them turns one named failure into several and delays the report. The distinction is the reason this
@@ -289,17 +290,39 @@ import { execFileSync } from "node:child_process";
289
290
  export const isTransientGitFault = (detail) =>
290
291
  /index\.lock|another git process seems to be running|Unable to create/i.test(String(detail ?? ""));
291
292
 
293
+ // ── THE PRODUCT NAMES ITS OWN COMMITTER WHEN GIT HAS NONE ───────────────────────────────────────────
294
+ //
295
+ // A save names its author, the person who asked for it. Git also records a COMMITTER, taken from the
296
+ // repository's or the account's configuration, and refuses the commit when it finds none. That is the
297
+ // default state of a service account nobody ran `git config` for, and of a fresh Windows install: every
298
+ // company created in the portal was refused there, with a message telling the person to run git. The
299
+ // person creating a company cannot act on that, and nothing about the save needs them to.
300
+ //
301
+ // So a commit into the store is given the product's own committer whenever git cannot name one, on every
302
+ // commit rather than once: `-c` or an environment variable configures one invocation, not the repository.
303
+ // A store or account that already has an identity keeps it, so a configured install records exactly what
304
+ // it recorded before, signing included.
305
+ export const STORE_COMMITTER = Object.freeze({ name: BRAND.name, email: "store@localhost" });
306
+
307
+ /** The environment a commit into `repoRoot` runs with: `env` itself when git can name a committer, else `env` plus the product's. */
308
+ export function storeCommitEnv(repoRoot, { env = process.env } = {}) {
309
+ requireRepoRootPath(repoRoot, "storeCommitEnv");
310
+ try {
311
+ execFileSync("git", ["-C", repoRoot, "var", "GIT_COMMITTER_IDENT"], { stdio: ["ignore", "pipe", "pipe"], env });
312
+ return env;
313
+ } catch {
314
+ return { ...env, GIT_COMMITTER_NAME: STORE_COMMITTER.name, GIT_COMMITTER_EMAIL: STORE_COMMITTER.email };
315
+ }
316
+ }
317
+
292
318
  /**
293
319
  * Why a commit into `repoRoot` would be refused, asked BEFORE anything is written; null when it would not
294
- * be. Read-only: `rev-parse` and `git var` change nothing. `{ code, detail, message }`, where `message` names
295
- * the store and the command that clears it, in terms an operator acts on rather than git's own words:
320
+ * be. Read-only: `rev-parse` changes nothing. `{ code, detail, message }`, where `message` names the store
321
+ * and the command that clears it, in terms an operator acts on rather than git's own words:
296
322
  *
297
- * not-a-repository — the store is not inside a git repository this process can use;
298
- * no-identity — git has no committer identity here. That is the default state of any machine where
299
- * nobody ran `git config user.email`, a fresh Windows install among them. A save names
300
- * its author; the COMMITTER is the machine's, and git refuses a commit it cannot name
301
- * one for. `git -c user.email=…` on a one-off seed commit does not help: `-c` configures
302
- * that invocation, not the repository, so every save after it fails the same way.
323
+ * not-a-repository — the store is not inside a git repository this process can use.
324
+ *
325
+ * A missing committer identity is not a refusal: every commit into the store supplies one (storeCommitEnv).
303
326
  */
304
327
  export function storeCommitRefusal(repoRoot, { env = process.env } = {}) {
305
328
  requireRepoRootPath(repoRoot, "storeCommitRefusal");
@@ -313,18 +336,13 @@ export function storeCommitRefusal(repoRoot, { env = process.env } = {}) {
313
336
  : `run \`git init\` in ${repoRoot}, or point PROFILE_REPO_ROOT at the repository that holds the store`;
314
337
  return { code: "not-a-repository", detail, message: `the store at ${repoRoot} is not a git repository this install can record into (${detail}) — ${fix}` };
315
338
  }
316
- try { ask("var", "GIT_COMMITTER_IDENT"); }
317
- catch (e) {
318
- return { code: "no-identity", detail: said(e).pop()?.slice(0, 200) ?? "",
319
- message: `the store at ${repoRoot} has no git identity, so nothing saved to it can be recorded — run `
320
- + `\`git -C ${repoRoot} config user.email "you@example.com"\` and \`git -C ${repoRoot} config user.name "Your Name"\`` };
321
- }
322
339
  return null;
323
340
  }
324
341
 
325
342
  export function makeStoreCommit({ repoRoot, log = () => {}, what = "store", retries = 3, waitMs = 50 }) {
326
343
  requireRepoRootPath(repoRoot, "makeStoreCommit");
327
- const git = (...args) => execFileSync("git", ["-C", repoRoot, ...args], { encoding: "utf8" }).toString().trim();
344
+ const run = (env, args) => execFileSync("git", ["-C", repoRoot, ...args], { encoding: "utf8", ...(env ? { env } : {}) }).toString().trim();
345
+ const git = (...args) => run(null, args);
328
346
  // ── IS THIS A REPOSITORY WE CAN USE? ASKED FIRST ─────────────────────────────
329
347
  //
330
348
  // Outside a repository git falls back to `--no-index` mode, which has no `--cached` — so the very
@@ -351,13 +369,16 @@ export function makeStoreCommit({ repoRoot, log = () => {}, what = "store", retr
351
369
  log(`${what}: the store is not a usable git repository, so this save CANNOT be committed — ${refusal}`);
352
370
  throw new Error(`the store at ${repoRoot} is not a usable git repository: ${refusal}`);
353
371
  }
372
+ // Asked per commit, so an identity somebody configures while the service runs is used from then on.
373
+ const committing = storeCommitEnv(repoRoot);
374
+ const gitCommit = (...args) => run(committing, args);
354
375
  // (1) Complete what an earlier failed save left staged. BEST EFFORT: if this fails too — the same
355
376
  // hook is still rejecting, the disk is still full — it must not stop the save that is happening now,
356
377
  // and it must not touch the index either. The paths simply stay staged for the next attempt.
357
378
  try {
358
379
  const found = git("diff", "--cached", "--name-only").split("\n").filter(Boolean);
359
380
  if (found.length) {
360
- git("commit", "-m", `Complete a store save left staged by an earlier failure (${found.length} path(s))`,
381
+ gitCommit("commit", "-m", `Complete a store save left staged by an earlier failure (${found.length} path(s))`,
361
382
  "--author", `${author} <${author}>`);
362
383
  log(`${what}: completed a commit left staged by an earlier failed save — ${found.join(", ")}`);
363
384
  }
@@ -369,7 +390,7 @@ export function makeStoreCommit({ repoRoot, log = () => {}, what = "store", retr
369
390
  git("add", ...files);
370
391
  for (let attempt = 1; ; attempt++) {
371
392
  try {
372
- git("commit", "-m", message, "--author", `${author} <${author}>`);
393
+ gitCommit("commit", "-m", message, "--author", `${author} <${author}>`);
373
394
  return git("rev-parse", "HEAD");
374
395
  } catch (e) {
375
396
  const detail = detailOf(e);