@freecodecamp/universe-cli 0.7.2 → 0.8.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.
package/dist/index.cjs CHANGED
@@ -2434,8 +2434,8 @@ var require_stringifyNumber = __commonJS({
2434
2434
  i = n.length;
2435
2435
  n += ".";
2436
2436
  }
2437
- let d2 = minFractionDigits - (n.length - i - 1);
2438
- while (d2-- > 0)
2437
+ let d3 = minFractionDigits - (n.length - i - 1);
2438
+ while (d3-- > 0)
2439
2439
  n += "0";
2440
2440
  }
2441
2441
  return n;
@@ -3174,10 +3174,10 @@ var require_timestamp = __commonJS({
3174
3174
  let date5 = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec);
3175
3175
  const tz = match[8];
3176
3176
  if (tz && tz !== "Z") {
3177
- let d2 = parseSexagesimal(tz, false);
3178
- if (Math.abs(d2) < 30)
3179
- d2 *= 60;
3180
- date5 -= 6e4 * d2;
3177
+ let d3 = parseSexagesimal(tz, false);
3178
+ if (Math.abs(d3) < 30)
3179
+ d3 *= 60;
3180
+ date5 -= 6e4 * d3;
3181
3181
  }
3182
3182
  return new Date(date5);
3183
3183
  },
@@ -4702,12 +4702,12 @@ var require_resolve_flow_scalar = __commonJS({
4702
4702
  };
4703
4703
  }
4704
4704
  const valueEnd = offset + source.length;
4705
- const re = resolveEnd.resolveEnd(end, valueEnd, strict, onError);
4705
+ const re2 = resolveEnd.resolveEnd(end, valueEnd, strict, onError);
4706
4706
  return {
4707
4707
  value,
4708
4708
  type: _type,
4709
- comment: re.comment,
4710
- range: [offset, valueEnd, re.offset]
4709
+ comment: re2.comment,
4710
+ range: [offset, valueEnd, re2.offset]
4711
4711
  };
4712
4712
  }
4713
4713
  function plainValue(source, onError) {
@@ -5092,10 +5092,10 @@ var require_compose_node = __commonJS({
5092
5092
  if (alias.source.endsWith(":"))
5093
5093
  onError(offset + source.length - 1, "BAD_ALIAS", "Alias ending in : is ambiguous", true);
5094
5094
  const valueEnd = offset + source.length;
5095
- const re = resolveEnd.resolveEnd(end, valueEnd, options.strict, onError);
5096
- alias.range = [offset, valueEnd, re.offset];
5097
- if (re.comment)
5098
- alias.comment = re.comment;
5095
+ const re2 = resolveEnd.resolveEnd(end, valueEnd, options.strict, onError);
5096
+ alias.range = [offset, valueEnd, re2.offset];
5097
+ if (re2.comment)
5098
+ alias.comment = re2.comment;
5099
5099
  return alias;
5100
5100
  }
5101
5101
  exports2.composeEmptyNode = composeEmptyNode;
@@ -5136,10 +5136,10 @@ var require_compose_doc = __commonJS({
5136
5136
  }
5137
5137
  doc.contents = value ? composeNode.composeNode(ctx, value, props, onError) : composeNode.composeEmptyNode(ctx, props.end, start, null, props, onError);
5138
5138
  const contentEnd = doc.contents.range[2];
5139
- const re = resolveEnd.resolveEnd(end, contentEnd, false, onError);
5140
- if (re.comment)
5141
- doc.comment = re.comment;
5142
- doc.range = [offset, contentEnd, re.offset];
5139
+ const re2 = resolveEnd.resolveEnd(end, contentEnd, false, onError);
5140
+ if (re2.comment)
5141
+ doc.comment = re2.comment;
5142
+ doc.range = [offset, contentEnd, re2.offset];
5143
5143
  return doc;
5144
5144
  }
5145
5145
  exports2.composeDoc = composeDoc;
@@ -8396,6 +8396,11 @@ function wrapAnsi(string4, columns, options) {
8396
8396
  // node_modules/.pnpm/@clack+core@1.2.0/node_modules/@clack/core/dist/index.mjs
8397
8397
  var import_sisteransi = __toESM(require_src(), 1);
8398
8398
  var import_node_tty = require("tty");
8399
+ function d(r, t2, e) {
8400
+ if (!e.some((o) => !o.disabled)) return r;
8401
+ const s = r + t2, i = Math.max(e.length - 1, 0), n = s < 0 ? i : s > i ? 0 : s;
8402
+ return e[n].disabled ? d(n, t2 < 0 ? -1 : 1, e) : n;
8403
+ }
8399
8404
  var E = ["up", "down", "left", "right", "space", "enter", "cancel"];
8400
8405
  var G = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
8401
8406
  var u = { actions: new Set(E), aliases: /* @__PURE__ */ new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"], ["", "cancel"], ["escape", "cancel"]]), messages: { cancel: "Canceled", error: "Something went wrong" }, withGuide: true, date: { monthNames: [...G], messages: { required: "Please enter a valid date", invalidMonth: "There are only 12 months in a year", invalidDay: (r, t2) => `There are only ${r} days in ${t2}`, afterMin: (r) => `Date must be on or after ${r.toISOString().slice(0, 10)}`, beforeMax: (r) => `Date must be on or before ${r.toISOString().slice(0, 10)}` } } };
@@ -8589,6 +8594,52 @@ var Q = class extends p {
8589
8594
  });
8590
8595
  }
8591
8596
  };
8597
+ var nt = class extends p {
8598
+ options;
8599
+ cursor = 0;
8600
+ get _selectedValue() {
8601
+ return this.options[this.cursor];
8602
+ }
8603
+ changeValue() {
8604
+ this.value = this._selectedValue.value;
8605
+ }
8606
+ constructor(t2) {
8607
+ super(t2, false), this.options = t2.options;
8608
+ const e = this.options.findIndex(({ value: i }) => i === t2.initialValue), s = e === -1 ? 0 : e;
8609
+ this.cursor = this.options[s].disabled ? d(s, 1, this.options) : s, this.changeValue(), this.on("cursor", (i) => {
8610
+ switch (i) {
8611
+ case "left":
8612
+ case "up":
8613
+ this.cursor = d(this.cursor, -1, this.options);
8614
+ break;
8615
+ case "down":
8616
+ case "right":
8617
+ this.cursor = d(this.cursor, 1, this.options);
8618
+ break;
8619
+ }
8620
+ this.changeValue();
8621
+ });
8622
+ }
8623
+ };
8624
+ var at = class extends p {
8625
+ get userInputWithCursor() {
8626
+ if (this.state === "submit") return this.userInput;
8627
+ const t2 = this.userInput;
8628
+ if (this.cursor >= t2.length) return `${this.userInput}\u2588`;
8629
+ const e = t2.slice(0, this.cursor), [s, ...i] = t2.slice(this.cursor);
8630
+ return `${e}${(0, import_node_util.styleText)("inverse", s)}${i.join("")}`;
8631
+ }
8632
+ get cursor() {
8633
+ return this._cursor;
8634
+ }
8635
+ constructor(t2) {
8636
+ super({ ...t2, initialUserInput: t2.initialUserInput ?? t2.initialValue }), this.on("userInput", (e) => {
8637
+ this._setValue(e);
8638
+ }), this.on("finalize", () => {
8639
+ this.value || (this.value = t2.defaultValue), this.value === void 0 && (this.value = "");
8640
+ });
8641
+ }
8642
+ };
8592
8643
 
8593
8644
  // node_modules/.pnpm/@clack+prompts@1.2.0/node_modules/@clack/prompts/dist/index.mjs
8594
8645
  var import_node_util2 = require("util");
@@ -8607,7 +8658,7 @@ var oe = w2("\u25A0", "x");
8607
8658
  var ue = w2("\u25B2", "x");
8608
8659
  var F = w2("\u25C7", "o");
8609
8660
  var le = w2("\u250C", "T");
8610
- var d = w2("\u2502", "|");
8661
+ var d2 = w2("\u2502", "|");
8611
8662
  var E2 = w2("\u2514", "\u2014");
8612
8663
  var Ie = w2("\u2510", "T");
8613
8664
  var Ee = w2("\u2518", "\u2014");
@@ -8640,26 +8691,71 @@ var V2 = (e) => {
8640
8691
  return (0, import_node_util2.styleText)("green", F);
8641
8692
  }
8642
8693
  };
8694
+ var ye = (e) => {
8695
+ switch (e) {
8696
+ case "initial":
8697
+ case "active":
8698
+ return (0, import_node_util2.styleText)("cyan", d2);
8699
+ case "cancel":
8700
+ return (0, import_node_util2.styleText)("red", d2);
8701
+ case "error":
8702
+ return (0, import_node_util2.styleText)("yellow", d2);
8703
+ case "submit":
8704
+ return (0, import_node_util2.styleText)("green", d2);
8705
+ }
8706
+ };
8707
+ var et2 = (e, i, s, r, u2) => {
8708
+ let n = i, o = 0;
8709
+ for (let c2 = s; c2 < r; c2++) {
8710
+ const a = e[c2];
8711
+ if (n = n - a.length, o++, n <= u2) break;
8712
+ }
8713
+ return { lineCount: n, removals: o };
8714
+ };
8715
+ var Y2 = ({ cursor: e, options: i, style: s, output: r = process.stdout, maxItems: u2 = Number.POSITIVE_INFINITY, columnPadding: n = 0, rowPadding: o = 4 }) => {
8716
+ const c2 = O(r) - n, a = A(r), l = (0, import_node_util2.styleText)("dim", "..."), $2 = Math.max(a - o, 0), y2 = Math.max(Math.min(u2, $2), 5);
8717
+ let p2 = 0;
8718
+ e >= y2 - 3 && (p2 = Math.max(Math.min(e - y2 + 3, i.length - y2), 0));
8719
+ let m = y2 < i.length && p2 > 0, g = y2 < i.length && p2 + y2 < i.length;
8720
+ const S2 = Math.min(p2 + y2, i.length), h = [];
8721
+ let f = 0;
8722
+ m && f++, g && f++;
8723
+ const v = p2 + (m ? 1 : 0), T = S2 - (g ? 1 : 0);
8724
+ for (let b = v; b < T; b++) {
8725
+ const x = wrapAnsi(s(i[b], b === e), c2, { hard: true, trim: false }).split(`
8726
+ `);
8727
+ h.push(x), f += x.length;
8728
+ }
8729
+ if (f > $2) {
8730
+ let b = 0, x = 0, G2 = f;
8731
+ const M2 = e - v, R2 = (j2, D2) => et2(h, G2, j2, D2, $2);
8732
+ m ? ({ lineCount: G2, removals: b } = R2(0, M2), G2 > $2 && ({ lineCount: G2, removals: x } = R2(M2 + 1, h.length))) : ({ lineCount: G2, removals: x } = R2(M2 + 1, h.length), G2 > $2 && ({ lineCount: G2, removals: b } = R2(0, M2))), b > 0 && (m = true, h.splice(0, b)), x > 0 && (g = true, h.splice(h.length - x, x));
8733
+ }
8734
+ const C2 = [];
8735
+ m && C2.push(l);
8736
+ for (const b of h) for (const x of b) C2.push(x);
8737
+ return g && C2.push(l), C2;
8738
+ };
8643
8739
  var ot2 = (e) => {
8644
8740
  const i = e.active ?? "Yes", s = e.inactive ?? "No";
8645
8741
  return new Q({ active: i, inactive: s, signal: e.signal, input: e.input, output: e.output, initialValue: e.initialValue ?? true, render() {
8646
- const r = e.withGuide ?? u.withGuide, u2 = `${V2(this.state)} `, n = r ? `${(0, import_node_util2.styleText)("gray", d)} ` : "", o = R(e.output, e.message, n, u2), c2 = `${r ? `${(0, import_node_util2.styleText)("gray", d)}
8742
+ const r = e.withGuide ?? u.withGuide, u2 = `${V2(this.state)} `, n = r ? `${(0, import_node_util2.styleText)("gray", d2)} ` : "", o = R(e.output, e.message, n, u2), c2 = `${r ? `${(0, import_node_util2.styleText)("gray", d2)}
8647
8743
  ` : ""}${o}
8648
8744
  `, a = this.value ? i : s;
8649
8745
  switch (this.state) {
8650
8746
  case "submit": {
8651
- const l = r ? `${(0, import_node_util2.styleText)("gray", d)} ` : "";
8747
+ const l = r ? `${(0, import_node_util2.styleText)("gray", d2)} ` : "";
8652
8748
  return `${c2}${l}${(0, import_node_util2.styleText)("dim", a)}`;
8653
8749
  }
8654
8750
  case "cancel": {
8655
- const l = r ? `${(0, import_node_util2.styleText)("gray", d)} ` : "";
8751
+ const l = r ? `${(0, import_node_util2.styleText)("gray", d2)} ` : "";
8656
8752
  return `${c2}${l}${(0, import_node_util2.styleText)(["strikethrough", "dim"], a)}${r ? `
8657
- ${(0, import_node_util2.styleText)("gray", d)}` : ""}`;
8753
+ ${(0, import_node_util2.styleText)("gray", d2)}` : ""}`;
8658
8754
  }
8659
8755
  default: {
8660
- const l = r ? `${(0, import_node_util2.styleText)("cyan", d)} ` : "", $2 = r ? (0, import_node_util2.styleText)("cyan", E2) : "";
8756
+ const l = r ? `${(0, import_node_util2.styleText)("cyan", d2)} ` : "", $2 = r ? (0, import_node_util2.styleText)("cyan", E2) : "";
8661
8757
  return `${c2}${l}${this.value ? `${(0, import_node_util2.styleText)("green", z2)} ${i}` : `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", i)}`}${e.vertical ? r ? `
8662
- ${(0, import_node_util2.styleText)("cyan", d)} ` : `
8758
+ ${(0, import_node_util2.styleText)("cyan", d2)} ` : `
8663
8759
  ` : ` ${(0, import_node_util2.styleText)("dim", "/")} `}${this.value ? `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", s)}` : `${(0, import_node_util2.styleText)("green", z2)} ${s}`}
8664
8760
  ${$2}
8665
8761
  `;
@@ -8667,7 +8763,7 @@ ${$2}
8667
8763
  }
8668
8764
  } }).prompt();
8669
8765
  };
8670
- var O2 = { message: (e = [], { symbol: i = (0, import_node_util2.styleText)("gray", d), secondarySymbol: s = (0, import_node_util2.styleText)("gray", d), output: r = process.stdout, spacing: u2 = 1, withGuide: n } = {}) => {
8766
+ var O2 = { message: (e = [], { symbol: i = (0, import_node_util2.styleText)("gray", d2), secondarySymbol: s = (0, import_node_util2.styleText)("gray", d2), output: r = process.stdout, spacing: u2 = 1, withGuide: n } = {}) => {
8671
8767
  const o = [], c2 = n ?? u.withGuide, a = c2 ? s : "", l = c2 ? `${i} ` : "", $2 = c2 ? `${s} ` : "";
8672
8768
  for (let p2 = 0; p2 < u2; p2++) o.push(a);
8673
8769
  const y2 = Array.isArray(e) ? e : e.split(`
@@ -8715,7 +8811,7 @@ var fe = ({ indicator: e = "dots", onCancel: i, output: s = process.stdout, canc
8715
8811
  const A2 = (performance.now() - _2) / 1e3, k = Math.floor(A2 / 60), L = Math.floor(A2 % 60);
8716
8812
  return k > 0 ? `[${k}m ${L}s]` : `[${L}s]`;
8717
8813
  }, D2 = a.withGuide ?? u.withGuide, ie = (_2 = "") => {
8718
- p2 = true, $2 = z({ output: s }), g = R2(_2), h = performance.now(), D2 && s.write(`${(0, import_node_util2.styleText)("gray", d)}
8814
+ p2 = true, $2 = z({ output: s }), g = R2(_2), h = performance.now(), D2 && s.write(`${(0, import_node_util2.styleText)("gray", d2)}
8719
8815
  `);
8720
8816
  let A2 = 0, k = 0;
8721
8817
  x(), y2 = setInterval(() => {
@@ -8747,7 +8843,81 @@ var fe = ({ indicator: e = "dots", onCancel: i, output: s = process.stdout, canc
8747
8843
  } };
8748
8844
  };
8749
8845
  var Ve = { light: w2("\u2500", "-"), heavy: w2("\u2501", "="), block: w2("\u2588", "#") };
8750
- var je = `${(0, import_node_util2.styleText)("gray", d)} `;
8846
+ var re = (e, i) => e.includes(`
8847
+ `) ? e.split(`
8848
+ `).map((s) => i(s)).join(`
8849
+ `) : i(e);
8850
+ var _t = (e) => {
8851
+ const i = (s, r) => {
8852
+ const u2 = s.label ?? String(s.value);
8853
+ switch (r) {
8854
+ case "disabled":
8855
+ return `${(0, import_node_util2.styleText)("gray", H2)} ${re(u2, (n) => (0, import_node_util2.styleText)("gray", n))}${s.hint ? ` ${(0, import_node_util2.styleText)("dim", `(${s.hint ?? "disabled"})`)}` : ""}`;
8856
+ case "selected":
8857
+ return `${re(u2, (n) => (0, import_node_util2.styleText)("dim", n))}`;
8858
+ case "active":
8859
+ return `${(0, import_node_util2.styleText)("green", z2)} ${u2}${s.hint ? ` ${(0, import_node_util2.styleText)("dim", `(${s.hint})`)}` : ""}`;
8860
+ case "cancelled":
8861
+ return `${re(u2, (n) => (0, import_node_util2.styleText)(["strikethrough", "dim"], n))}`;
8862
+ default:
8863
+ return `${(0, import_node_util2.styleText)("dim", H2)} ${re(u2, (n) => (0, import_node_util2.styleText)("dim", n))}`;
8864
+ }
8865
+ };
8866
+ return new nt({ options: e.options, signal: e.signal, input: e.input, output: e.output, initialValue: e.initialValue, render() {
8867
+ const s = e.withGuide ?? u.withGuide, r = `${V2(this.state)} `, u2 = `${ye(this.state)} `, n = R(e.output, e.message, u2, r), o = `${s ? `${(0, import_node_util2.styleText)("gray", d2)}
8868
+ ` : ""}${n}
8869
+ `;
8870
+ switch (this.state) {
8871
+ case "submit": {
8872
+ const c2 = s ? `${(0, import_node_util2.styleText)("gray", d2)} ` : "", a = R(e.output, i(this.options[this.cursor], "selected"), c2);
8873
+ return `${o}${a}`;
8874
+ }
8875
+ case "cancel": {
8876
+ const c2 = s ? `${(0, import_node_util2.styleText)("gray", d2)} ` : "", a = R(e.output, i(this.options[this.cursor], "cancelled"), c2);
8877
+ return `${o}${a}${s ? `
8878
+ ${(0, import_node_util2.styleText)("gray", d2)}` : ""}`;
8879
+ }
8880
+ default: {
8881
+ const c2 = s ? `${(0, import_node_util2.styleText)("cyan", d2)} ` : "", a = s ? (0, import_node_util2.styleText)("cyan", E2) : "", l = o.split(`
8882
+ `).length, $2 = s ? 2 : 1;
8883
+ return `${o}${c2}${Y2({ output: e.output, cursor: this.cursor, options: this.options, maxItems: e.maxItems, columnPadding: c2.length, rowPadding: l + $2, style: (y2, p2) => i(y2, y2.disabled ? "disabled" : p2 ? "active" : "inactive") }).join(`
8884
+ ${c2}`)}
8885
+ ${a}
8886
+ `;
8887
+ }
8888
+ }
8889
+ } }).prompt();
8890
+ };
8891
+ var je = `${(0, import_node_util2.styleText)("gray", d2)} `;
8892
+ var Ot = (e) => new at({ validate: e.validate, placeholder: e.placeholder, defaultValue: e.defaultValue, initialValue: e.initialValue, output: e.output, signal: e.signal, input: e.input, render() {
8893
+ const i = e?.withGuide ?? u.withGuide, s = `${`${i ? `${(0, import_node_util2.styleText)("gray", d2)}
8894
+ ` : ""}${V2(this.state)} `}${e.message}
8895
+ `, r = e.placeholder ? (0, import_node_util2.styleText)("inverse", e.placeholder[0]) + (0, import_node_util2.styleText)("dim", e.placeholder.slice(1)) : (0, import_node_util2.styleText)(["inverse", "hidden"], "_"), u2 = this.userInput ? this.userInputWithCursor : r, n = this.value ?? "";
8896
+ switch (this.state) {
8897
+ case "error": {
8898
+ const o = this.error ? ` ${(0, import_node_util2.styleText)("yellow", this.error)}` : "", c2 = i ? `${(0, import_node_util2.styleText)("yellow", d2)} ` : "", a = i ? (0, import_node_util2.styleText)("yellow", E2) : "";
8899
+ return `${s.trim()}
8900
+ ${c2}${u2}
8901
+ ${a}${o}
8902
+ `;
8903
+ }
8904
+ case "submit": {
8905
+ const o = n ? ` ${(0, import_node_util2.styleText)("dim", n)}` : "", c2 = i ? (0, import_node_util2.styleText)("gray", d2) : "";
8906
+ return `${s}${c2}${o}`;
8907
+ }
8908
+ case "cancel": {
8909
+ const o = n ? ` ${(0, import_node_util2.styleText)(["strikethrough", "dim"], n)}` : "", c2 = i ? (0, import_node_util2.styleText)("gray", d2) : "";
8910
+ return `${s}${c2}${o}${n.trim() ? `
8911
+ ${c2}` : ""}`;
8912
+ }
8913
+ default: {
8914
+ const o = i ? `${(0, import_node_util2.styleText)("cyan", d2)} ` : "", c2 = i ? (0, import_node_util2.styleText)("cyan", E2) : "";
8915
+ return `${s}${o}${u2}
8916
+ ${c2}
8917
+ `;
8918
+ }
8919
+ }
8920
+ } }).prompt();
8751
8921
 
8752
8922
  // src/output/exit-codes.ts
8753
8923
  var EXIT_USAGE = 10;
@@ -8780,6 +8950,9 @@ var StorageError = class extends CliError {
8780
8950
  var GitError = class extends CliError {
8781
8951
  exitCode = EXIT_GIT;
8782
8952
  };
8953
+ var ConfirmError = class extends CliError {
8954
+ exitCode = EXIT_CONFIRM;
8955
+ };
8783
8956
  var PartialUploadError = class extends CliError {
8784
8957
  exitCode = EXIT_PARTIAL;
8785
8958
  };
@@ -8792,8 +8965,8 @@ var import_node_child_process = require("child_process");
8792
8965
  function getGitState() {
8793
8966
  try {
8794
8967
  const hash2 = (0, import_node_child_process.execSync)("git rev-parse HEAD", { encoding: "utf-8" }).trim();
8795
- const status = (0, import_node_child_process.execSync)("git status --porcelain", { encoding: "utf-8" });
8796
- return { hash: hash2, dirty: status.length > 0 };
8968
+ const status2 = (0, import_node_child_process.execSync)("git status --porcelain", { encoding: "utf-8" });
8969
+ return { hash: hash2, dirty: status2.length > 0 };
8797
8970
  } catch {
8798
8971
  return { hash: null, dirty: false, error: "not a git repository" };
8799
8972
  }
@@ -8842,13 +9015,13 @@ var import_node_child_process2 = require("child_process");
8842
9015
  var import_promises = require("fs/promises");
8843
9016
  var import_node_path3 = require("path");
8844
9017
  var defaultExec = async (req) => {
8845
- return new Promise((resolveExit, reject) => {
9018
+ return new Promise((resolveExit, reject2) => {
8846
9019
  const child = (0, import_node_child_process2.spawn)(req.command, {
8847
9020
  cwd: req.cwd,
8848
9021
  shell: true,
8849
9022
  stdio: "inherit"
8850
9023
  });
8851
- child.on("error", (err) => reject(err));
9024
+ child.on("error", (err) => reject2(err));
8852
9025
  child.on("exit", (code) => resolveExit(code ?? 1));
8853
9026
  });
8854
9027
  };
@@ -9005,15 +9178,15 @@ function compilePattern(pattern) {
9005
9178
  const anchored = pattern.includes("/");
9006
9179
  const body = globToRegexBody(pattern);
9007
9180
  if (anchored) {
9008
- const re2 = new RegExp(`^${body}$`);
9009
- return { test: (rel) => re2.test(rel) };
9181
+ const re3 = new RegExp(`^${body}$`);
9182
+ return { test: (rel) => re3.test(rel) };
9010
9183
  }
9011
- const re = new RegExp(`^${body}$`);
9184
+ const re2 = new RegExp(`^${body}$`);
9012
9185
  return {
9013
9186
  test: (rel) => {
9014
9187
  const idx = rel.lastIndexOf("/");
9015
9188
  const base = idx === -1 ? rel : rel.slice(idx + 1);
9016
- return re.test(base);
9189
+ return re2.test(base);
9017
9190
  }
9018
9191
  };
9019
9192
  }
@@ -22895,26 +23068,26 @@ function editDistance(a, b) {
22895
23068
  const n = b.length;
22896
23069
  if (m === 0) return n;
22897
23070
  if (n === 0) return m;
22898
- const d2 = Array.from(
23071
+ const d3 = Array.from(
22899
23072
  { length: m + 1 },
22900
23073
  () => new Array(n + 1).fill(0)
22901
23074
  );
22902
- for (let i = 0; i <= m; i++) d2[i][0] = i;
22903
- for (let j2 = 0; j2 <= n; j2++) d2[0][j2] = j2;
23075
+ for (let i = 0; i <= m; i++) d3[i][0] = i;
23076
+ for (let j2 = 0; j2 <= n; j2++) d3[0][j2] = j2;
22904
23077
  for (let i = 1; i <= m; i++) {
22905
23078
  for (let j2 = 1; j2 <= n; j2++) {
22906
23079
  const cost = a[i - 1] === b[j2 - 1] ? 0 : 1;
22907
- d2[i][j2] = Math.min(
22908
- d2[i - 1][j2] + 1,
22909
- d2[i][j2 - 1] + 1,
22910
- d2[i - 1][j2 - 1] + cost
23080
+ d3[i][j2] = Math.min(
23081
+ d3[i - 1][j2] + 1,
23082
+ d3[i][j2 - 1] + 1,
23083
+ d3[i - 1][j2 - 1] + cost
22911
23084
  );
22912
23085
  if (i > 1 && j2 > 1 && a[i - 1] === b[j2 - 2] && a[i - 2] === b[j2 - 1]) {
22913
- d2[i][j2] = Math.min(d2[i][j2], d2[i - 2][j2 - 2] + 1);
23086
+ d3[i][j2] = Math.min(d3[i][j2], d3[i - 2][j2 - 2] + 1);
22914
23087
  }
22915
23088
  }
22916
23089
  }
22917
- return d2[m][n];
23090
+ return d3[m][n];
22918
23091
  }
22919
23092
  function suggest(target, candidates, threshold = 2) {
22920
23093
  if (candidates.length === 0) return null;
@@ -22927,15 +23100,60 @@ function suggest(target, candidates, threshold = 2) {
22927
23100
  let best = null;
22928
23101
  let bestD = threshold + 1;
22929
23102
  for (const c2 of candidates) {
22930
- const d2 = editDistance(lc, c2.toLowerCase());
22931
- if (d2 < bestD) {
22932
- bestD = d2;
23103
+ const d3 = editDistance(lc, c2.toLowerCase());
23104
+ if (d3 < bestD) {
23105
+ bestD = d3;
22933
23106
  best = c2;
22934
23107
  }
22935
23108
  }
22936
23109
  return bestD <= threshold ? best : null;
22937
23110
  }
22938
23111
 
23112
+ // src/commands/repo/schema.ts
23113
+ var REPO_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,99}$/;
23114
+ var repoName = external_exports.string().regex(
23115
+ REPO_NAME_RE,
23116
+ "must start with a letter or digit, then letters, digits, '.', '_' or '-' (max 100 chars)"
23117
+ );
23118
+ var visibilitySchema = external_exports.enum(["public", "private"]);
23119
+ var repoStatusSchema = external_exports.enum([
23120
+ "pending",
23121
+ "approved",
23122
+ "active",
23123
+ "rejected",
23124
+ "failed"
23125
+ ]);
23126
+ var createRepoRequestSchema = external_exports.object({
23127
+ name: repoName,
23128
+ visibility: visibilitySchema.default("private"),
23129
+ description: external_exports.string().max(350).optional(),
23130
+ template: repoName.optional()
23131
+ }).strict();
23132
+ var repoRowSchema = external_exports.object({
23133
+ id: external_exports.string(),
23134
+ name: external_exports.string(),
23135
+ owner: external_exports.string(),
23136
+ visibility: visibilitySchema,
23137
+ description: external_exports.string().optional(),
23138
+ template: external_exports.string().optional(),
23139
+ status: repoStatusSchema,
23140
+ url: external_exports.string().optional(),
23141
+ error: external_exports.string().optional(),
23142
+ requestedBy: external_exports.string(),
23143
+ approver: external_exports.string().optional(),
23144
+ rejectReason: external_exports.string().optional(),
23145
+ createdAt: external_exports.string(),
23146
+ updatedAt: external_exports.string()
23147
+ });
23148
+ var repoRowArraySchema = external_exports.array(repoRowSchema);
23149
+ var repoApproveResultSchema = external_exports.object({
23150
+ outcome: external_exports.enum(["ok", "approved_failed"]),
23151
+ request: repoRowSchema
23152
+ });
23153
+ var repoTemplatesResponseSchema = external_exports.object({
23154
+ templates: external_exports.array(external_exports.string())
23155
+ });
23156
+
22939
23157
  // src/lib/proxy-client.ts
22940
23158
  var DEFAULT_FETCH_TIMEOUT_MS = 3e4;
22941
23159
  function parseFetchTimeoutMs(env) {
@@ -22949,11 +23167,13 @@ var ProxyError = class extends CliError {
22949
23167
  exitCode;
22950
23168
  status;
22951
23169
  code;
22952
- constructor(status, code, message) {
23170
+ requestId;
23171
+ constructor(status2, code, message, requestId) {
22953
23172
  super(message);
22954
- this.status = status;
23173
+ this.status = status2;
22955
23174
  this.code = code;
22956
- this.exitCode = mapExitCode(status);
23175
+ this.exitCode = mapExitCode(status2);
23176
+ this.requestId = requestId;
22957
23177
  }
22958
23178
  };
22959
23179
  var AliasDriftError = class extends ProxyError {
@@ -22963,16 +23183,22 @@ var AliasDriftError = class extends ProxyError {
22963
23183
  this.current = current;
22964
23184
  }
22965
23185
  };
22966
- function mapExitCode(status) {
22967
- if (status === 401 || status === 403) return EXIT_CREDENTIALS;
22968
- if (status === 422 || status === 0 || status >= 500) return EXIT_STORAGE;
23186
+ function mapExitCode(status2) {
23187
+ if (status2 === 401 || status2 === 403) return EXIT_CREDENTIALS;
23188
+ if (status2 === 422 || status2 === 0 || status2 >= 500) return EXIT_STORAGE;
22969
23189
  return EXIT_USAGE;
22970
23190
  }
22971
23191
  function wrapProxyError(command, err) {
22972
23192
  if (err instanceof ProxyError) {
23193
+ let message = `${command} failed (${err.code}): ${err.message}`;
23194
+ if (err.code === "user_unauthorized") {
23195
+ message += "\n hint: the active GitHub token may lack the read:org scope or SSO authorization for the org. $GITHUB_TOKEN / $GH_TOKEN override `gh auth token` \u2014 run `universe whoami` to check the active identity source, then unset them or re-authorize the token (Configure SSO).";
23196
+ }
22973
23197
  return {
22974
23198
  code: err.exitCode,
22975
- message: `${command} failed (${err.code}): ${err.message}`
23199
+ message,
23200
+ kind: err.code,
23201
+ requestId: err.requestId
22976
23202
  };
22977
23203
  }
22978
23204
  if (err instanceof CliError) {
@@ -22987,34 +23213,34 @@ function isProxyErrorEnvelope(value) {
22987
23213
  return typeof value === "object" && value !== null && !Array.isArray(value) && "error" in value;
22988
23214
  }
22989
23215
  async function readErrorEnvelope(response) {
22990
- const status = response.status;
23216
+ const status2 = response.status;
22991
23217
  let raw;
22992
23218
  try {
22993
23219
  raw = await response.json();
22994
23220
  } catch {
22995
23221
  return {
22996
- code: `http_${status}`,
23222
+ code: `http_${status2}`,
22997
23223
  message: response.statusText || "request failed"
22998
23224
  };
22999
23225
  }
23000
23226
  if (isProxyErrorEnvelope(raw) && raw.error) {
23001
23227
  const current = typeof raw.current === "string" ? raw.current : void 0;
23002
23228
  return {
23003
- code: raw.error.code ?? `http_${status}`,
23229
+ code: raw.error.code ?? `http_${status2}`,
23004
23230
  message: raw.error.message ?? response.statusText ?? "request failed",
23005
23231
  ...current === void 0 ? {} : { current }
23006
23232
  };
23007
23233
  }
23008
23234
  return {
23009
- code: `http_${status}`,
23235
+ code: `http_${status2}`,
23010
23236
  message: response.statusText || "request failed"
23011
23237
  };
23012
23238
  }
23013
- function throwProxyError(status, env) {
23014
- if (status === 409 && env.code === "alias_drift") {
23239
+ function throwProxyError(status2, env, requestId) {
23240
+ if (status2 === 409 && env.code === "alias_drift") {
23015
23241
  throw new AliasDriftError(env.message, env.current ?? "");
23016
23242
  }
23017
- throw new ProxyError(status, env.code, env.message);
23243
+ throw new ProxyError(status2, env.code, env.message, requestId);
23018
23244
  }
23019
23245
  function stripTrailingSlash(url2) {
23020
23246
  return url2.endsWith("/") ? url2.slice(0, -1) : url2;
@@ -23023,6 +23249,7 @@ function createProxyClient(cfg) {
23023
23249
  const base = stripTrailingSlash(cfg.baseUrl);
23024
23250
  const fetchImpl = cfg.fetch ?? globalThis.fetch.bind(globalThis);
23025
23251
  const timeoutMs = cfg.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
23252
+ const debug = cfg.debug ?? false;
23026
23253
  function withTimeoutSignal(init) {
23027
23254
  if (timeoutMs <= 0) return init;
23028
23255
  const timeoutSignal = AbortSignal.timeout(timeoutMs);
@@ -23034,31 +23261,65 @@ function createProxyClient(cfg) {
23034
23261
  throw new ProxyError(
23035
23262
  0,
23036
23263
  "timeout",
23037
- `proxy timed out after ${timeoutMs}ms`
23264
+ `proxy timed out after ${timeoutMs}ms (${base})`
23038
23265
  );
23039
23266
  }
23040
23267
  const message = err instanceof Error ? err.message : String(err);
23041
- throw new ProxyError(0, "network_error", `proxy unreachable: ${message}`);
23268
+ throw new ProxyError(
23269
+ 0,
23270
+ "network_error",
23271
+ `proxy unreachable at ${base}: ${message}`
23272
+ );
23042
23273
  }
23043
23274
  async function userBearer() {
23044
23275
  const tok = await cfg.getAuthToken();
23045
23276
  return `Bearer ${tok}`;
23046
23277
  }
23047
- async function call(url2, init) {
23278
+ async function call(url2, init, validate) {
23279
+ const startedAt = debug ? Date.now() : 0;
23048
23280
  let response;
23049
23281
  try {
23050
23282
  response = await fetchImpl(url2, withTimeoutSignal(init));
23051
23283
  } catch (err) {
23052
23284
  translateFetchError(err);
23053
23285
  }
23286
+ if (debug) {
23287
+ process.stderr.write(
23288
+ `[universe] ${init.method ?? "GET"} ${url2} -> ${response.status} (${Date.now() - startedAt}ms)
23289
+ `
23290
+ );
23291
+ }
23054
23292
  if (!response.ok) {
23293
+ const requestId = response.headers.get("x-request-id") ?? void 0;
23055
23294
  const env = await readErrorEnvelope(response);
23056
- throwProxyError(response.status, env);
23295
+ throwProxyError(response.status, env, requestId);
23057
23296
  }
23058
23297
  if (response.status === 204) {
23059
23298
  return void 0;
23060
23299
  }
23061
- return await response.json();
23300
+ let raw;
23301
+ try {
23302
+ raw = await response.json();
23303
+ } catch {
23304
+ throw new ProxyError(
23305
+ 0,
23306
+ "malformed_response",
23307
+ "proxy returned a non-JSON response body"
23308
+ );
23309
+ }
23310
+ if (validate) {
23311
+ try {
23312
+ validate(raw);
23313
+ } catch (err) {
23314
+ const detail = err instanceof Error ? err.message : "invalid shape";
23315
+ throw new ProxyError(
23316
+ 0,
23317
+ "malformed_response",
23318
+ `proxy returned an unexpected response shape: ${detail}`
23319
+ );
23320
+ }
23321
+ }
23322
+ return raw;
23062
23323
  }
23063
23324
  return {
23064
23325
  async whoami() {
@@ -23219,6 +23480,105 @@ function createProxyClient(cfg) {
23219
23480
  Accept: "application/json"
23220
23481
  }
23221
23482
  });
23483
+ },
23484
+ async createRepoRequest(req) {
23485
+ const body = { name: req.name };
23486
+ if (req.visibility !== void 0) body.visibility = req.visibility;
23487
+ if (req.description !== void 0) body.description = req.description;
23488
+ if (req.template !== void 0 && req.template !== "") {
23489
+ body.template = req.template;
23490
+ }
23491
+ return call(
23492
+ `${base}/api/repo`,
23493
+ {
23494
+ method: "POST",
23495
+ headers: {
23496
+ Authorization: await userBearer(),
23497
+ Accept: "application/json",
23498
+ "Content-Type": "application/json"
23499
+ },
23500
+ body: JSON.stringify(body)
23501
+ },
23502
+ (raw) => repoRowSchema.parse(raw)
23503
+ );
23504
+ },
23505
+ async listRepoRequests(req) {
23506
+ const params = new URLSearchParams();
23507
+ if (req?.status) params.set("status", req.status);
23508
+ if (req?.mine) params.set("mine", "1");
23509
+ const qs = params.toString();
23510
+ const url2 = `${base}/api/repos${qs ? `?${qs}` : ""}`;
23511
+ return call(
23512
+ url2,
23513
+ {
23514
+ method: "GET",
23515
+ headers: {
23516
+ Authorization: await userBearer(),
23517
+ Accept: "application/json"
23518
+ }
23519
+ },
23520
+ (raw) => repoRowArraySchema.parse(raw)
23521
+ );
23522
+ },
23523
+ async getRepoRequest(id) {
23524
+ const url2 = `${base}/api/repo/${encodeURIComponent(id)}`;
23525
+ return call(
23526
+ url2,
23527
+ {
23528
+ method: "GET",
23529
+ headers: {
23530
+ Authorization: await userBearer(),
23531
+ Accept: "application/json"
23532
+ }
23533
+ },
23534
+ (raw) => repoRowSchema.parse(raw)
23535
+ );
23536
+ },
23537
+ async approveRepoRequest(req) {
23538
+ const url2 = `${base}/api/repo/${encodeURIComponent(req.id)}/approve`;
23539
+ return call(
23540
+ url2,
23541
+ {
23542
+ method: "POST",
23543
+ headers: {
23544
+ Authorization: await userBearer(),
23545
+ Accept: "application/json"
23546
+ }
23547
+ },
23548
+ (raw) => repoApproveResultSchema.parse(raw)
23549
+ );
23550
+ },
23551
+ async rejectRepoRequest(req) {
23552
+ const url2 = `${base}/api/repo/${encodeURIComponent(req.id)}/reject`;
23553
+ const body = {};
23554
+ if (req.reason !== void 0) body.reason = req.reason;
23555
+ return call(
23556
+ url2,
23557
+ {
23558
+ method: "POST",
23559
+ headers: {
23560
+ Authorization: await userBearer(),
23561
+ Accept: "application/json",
23562
+ "Content-Type": "application/json"
23563
+ },
23564
+ body: JSON.stringify(body)
23565
+ },
23566
+ (raw) => repoRowSchema.parse(raw)
23567
+ );
23568
+ },
23569
+ async listRepoTemplates() {
23570
+ const res = await call(
23571
+ `${base}/api/repo/templates`,
23572
+ {
23573
+ method: "GET",
23574
+ headers: {
23575
+ Authorization: await userBearer(),
23576
+ Accept: "application/json"
23577
+ }
23578
+ },
23579
+ (raw) => repoTemplatesResponseSchema.parse(raw)
23580
+ );
23581
+ return res.templates ?? [];
23222
23582
  }
23223
23583
  };
23224
23584
  }
@@ -23359,11 +23719,17 @@ function buildEnvelope(command, success2, data) {
23359
23719
  ...data
23360
23720
  };
23361
23721
  }
23362
- function buildErrorEnvelope(command, code, message, issues) {
23722
+ function buildErrorEnvelope(command, code, message, issues, kind, requestId) {
23363
23723
  const error48 = {
23364
23724
  code,
23365
23725
  message
23366
23726
  };
23727
+ if (kind !== void 0) {
23728
+ error48.kind = kind;
23729
+ }
23730
+ if (requestId !== void 0) {
23731
+ error48.requestId = requestId;
23732
+ }
23367
23733
  if (issues !== void 0) {
23368
23734
  error48.issues = issues;
23369
23735
  }
@@ -23464,12 +23830,14 @@ function outputError(ctx, code, message, optsOrIssues) {
23464
23830
  ctx.command,
23465
23831
  code,
23466
23832
  redactedMessage,
23467
- redactedIssues
23833
+ redactedIssues,
23834
+ opts.kind,
23835
+ opts.requestId
23468
23836
  );
23469
23837
  const payload = opts.extras ? { ...envelope, ...redactObject(opts.extras) } : envelope;
23470
23838
  process.stdout.write(JSON.stringify(payload) + "\n");
23471
23839
  } else {
23472
- (opts.logError ?? ((m) => O2.error(m)))(redactedMessage);
23840
+ (opts.logError ?? ((m) => O2.error(m, { output: process.stderr })))(redactedMessage);
23473
23841
  }
23474
23842
  }
23475
23843
 
@@ -23994,10 +24362,10 @@ async function readSiteFromYaml(cwd, read) {
23994
24362
  }
23995
24363
  function formatTable(deploys) {
23996
24364
  const header = ["DEPLOY ID", "TIMESTAMP", "SHA"];
23997
- const rows = deploys.map((d2) => [
23998
- d2.deployId,
23999
- d2.timestamp ? d2.timestamp.replace("T", " ").replace("Z", "") : "\u2014",
24000
- d2.sha ?? "\u2014"
24365
+ const rows = deploys.map((d3) => [
24366
+ d3.deployId,
24367
+ d3.timestamp ? d3.timestamp.replace("T", " ").replace("Z", "") : "\u2014",
24368
+ d3.sha ?? "\u2014"
24001
24369
  ]);
24002
24370
  const widths = header.map(
24003
24371
  (h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length))
@@ -24041,7 +24409,7 @@ async function ls(options, deps = {}) {
24041
24409
  const sorted = [...raw].sort(
24042
24410
  (a, b) => b.deployId.localeCompare(a.deployId)
24043
24411
  );
24044
- const deploys = sorted.map((d2) => parseDeployId(d2.deployId));
24412
+ const deploys = sorted.map((d3) => parseDeployId(d3.deployId));
24045
24413
  if (options.json) {
24046
24414
  emitJson4(
24047
24415
  buildEnvelope("ls", true, {
@@ -24382,6 +24750,7 @@ async function whoami(options, deps = {}) {
24382
24750
  buildEnvelope("whoami", true, {
24383
24751
  login: result.login,
24384
24752
  identitySource: identity.source,
24753
+ proxyUrl: baseUrl,
24385
24754
  authorizedSitesCount: count
24386
24755
  })
24387
24756
  );
@@ -24391,6 +24760,7 @@ async function whoami(options, deps = {}) {
24391
24760
  [
24392
24761
  `Logged in as: ${result.login}`,
24393
24762
  `Identity source: ${identity.source}`,
24763
+ `Proxy: ${baseUrl}`,
24394
24764
  sitesLine
24395
24765
  ].join("\n")
24396
24766
  );
@@ -24621,6 +24991,495 @@ async function update(options, deps = {}) {
24621
24991
  }
24622
24992
  }
24623
24993
 
24994
+ // src/commands/repo/_shared.ts
24995
+ var defaultRepoPrompts = {
24996
+ text: Ot,
24997
+ select: _t,
24998
+ confirm: ot2,
24999
+ isCancel: q
25000
+ };
25001
+ function emitJson9(envelope) {
25002
+ process.stdout.write(JSON.stringify(envelope) + "\n");
25003
+ }
25004
+ async function setupClient2(deps) {
25005
+ const env = deps.env ?? process.env;
25006
+ const resolveId = deps.resolveIdentity ?? resolveIdentity;
25007
+ const mkClient = deps.createProxyClient ?? createProxyClient;
25008
+ const identity = await resolveId({ env });
25009
+ if (!identity) {
25010
+ throw new CredentialError(
25011
+ "No GitHub identity available. Run `universe login`, set $GITHUB_TOKEN, or install the gh CLI."
25012
+ );
25013
+ }
25014
+ const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL;
25015
+ const client = mkClient({
25016
+ baseUrl,
25017
+ getAuthToken: () => identity.token,
25018
+ timeoutMs: parseFetchTimeoutMs(env),
25019
+ debug: Boolean(env["UNIVERSE_DEBUG"])
25020
+ });
25021
+ return { client, identitySource: identity.source };
25022
+ }
25023
+ function formatRepoTable(rows, emptyMsg = "No repo requests.") {
25024
+ if (rows.length === 0) return emptyMsg;
25025
+ const headers = [
25026
+ "ID",
25027
+ "REPO",
25028
+ "VIS",
25029
+ "STATUS",
25030
+ "REQUESTED BY",
25031
+ "REQUESTED AT"
25032
+ ];
25033
+ const cells = rows.map((r) => [
25034
+ r.id,
25035
+ r.name,
25036
+ r.visibility,
25037
+ r.status,
25038
+ r.requestedBy,
25039
+ r.createdAt
25040
+ ]);
25041
+ const widths = headers.map(
25042
+ (h, i) => Math.max(h.length, ...cells.map((row) => row[i]?.length ?? 0))
25043
+ );
25044
+ const fmt = (row) => row.map((c2, i) => c2.padEnd(widths[i] ?? 0)).join(" ");
25045
+ return [fmt(headers), ...cells.map(fmt)].join("\n");
25046
+ }
25047
+
25048
+ // src/commands/repo/approve.ts
25049
+ async function approve(options, deps = {}) {
25050
+ const command = "repo approve";
25051
+ const success2 = deps.logSuccess ?? ((s) => O2.success(s));
25052
+ const error48 = deps.logError ?? ((s) => O2.error(s));
25053
+ const exit = deps.exit ?? exitWithCode;
25054
+ const prompts = deps.prompts ?? defaultRepoPrompts;
25055
+ const isTTY = deps.isTTY ?? Boolean(process.stdout.isTTY);
25056
+ let creationFailure;
25057
+ let identitySource;
25058
+ try {
25059
+ if (!options.id || options.id.trim().length === 0) {
25060
+ throw new UsageError("request id is required (positional argument)");
25061
+ }
25062
+ const setup = await setupClient2(deps);
25063
+ const client = setup.client;
25064
+ identitySource = setup.identitySource;
25065
+ if (!options.json && !options.yes) {
25066
+ if (!isTTY) {
25067
+ throw new UsageError(
25068
+ "non-interactive session: pass --yes to approve without confirmation"
25069
+ );
25070
+ }
25071
+ const cur = await client.getRepoRequest(options.id);
25072
+ const ok = await prompts.confirm({
25073
+ message: `Approve ${cur.visibility} repo "${cur.name}" requested by ${cur.requestedBy}? This creates the repository.`
25074
+ });
25075
+ if (prompts.isCancel(ok) || ok === false) {
25076
+ throw new ConfirmError("repo approve cancelled");
25077
+ }
25078
+ }
25079
+ const res = await client.approveRepoRequest({ id: options.id });
25080
+ const row = res.request;
25081
+ if (res.outcome === "approved_failed") {
25082
+ if (!options.json) {
25083
+ throw new StorageError(
25084
+ `approved, but repository creation failed: ${row.error ?? "unknown"} (${row.owner}/${row.name}, requested by ${row.requestedBy})`
25085
+ );
25086
+ }
25087
+ creationFailure = {
25088
+ outcome: res.outcome,
25089
+ id: row.id,
25090
+ repo: `${row.owner}/${row.name}`,
25091
+ status: row.status,
25092
+ creationError: row.error ?? "unknown",
25093
+ requestedBy: row.requestedBy,
25094
+ identitySource
25095
+ };
25096
+ } else if (options.json) {
25097
+ emitJson9(
25098
+ buildEnvelope(command, true, {
25099
+ id: row.id,
25100
+ outcome: res.outcome,
25101
+ repo: `${row.owner}/${row.name}`,
25102
+ url: row.url,
25103
+ visibility: row.visibility,
25104
+ approver: row.approver,
25105
+ identitySource
25106
+ })
25107
+ );
25108
+ } else {
25109
+ success2(
25110
+ [
25111
+ `Approved ${row.name}`,
25112
+ ``,
25113
+ ` Repository: ${row.url ?? `${row.owner}/${row.name}`}`,
25114
+ ` Visibility: ${row.visibility}`,
25115
+ ` Approved by: ${row.approver ?? "you"}`
25116
+ ].join("\n")
25117
+ );
25118
+ }
25119
+ } catch (err) {
25120
+ const { code, message, kind, requestId } = wrapProxyError(command, err);
25121
+ outputError({ json: options.json, command }, code, message, {
25122
+ logError: error48,
25123
+ kind,
25124
+ requestId,
25125
+ extras: identitySource ? { identitySource } : void 0
25126
+ });
25127
+ exit(code);
25128
+ return;
25129
+ }
25130
+ if (creationFailure) {
25131
+ outputError(
25132
+ { json: true, command },
25133
+ EXIT_STORAGE,
25134
+ "approved, but repository creation failed",
25135
+ { logError: error48, extras: creationFailure }
25136
+ );
25137
+ exit(EXIT_STORAGE);
25138
+ }
25139
+ }
25140
+
25141
+ // src/commands/repo/create.ts
25142
+ function blankToUndefined(s) {
25143
+ if (s === void 0 || s === null) return void 0;
25144
+ const t2 = String(s).trim();
25145
+ return t2 === "" ? void 0 : t2;
25146
+ }
25147
+ async function promptTemplate(client, prompts) {
25148
+ let templates = [];
25149
+ try {
25150
+ templates = await client.listRepoTemplates();
25151
+ } catch {
25152
+ templates = [];
25153
+ }
25154
+ if (templates.length === 0) {
25155
+ const v2 = await prompts.text({
25156
+ message: "Template (optional)",
25157
+ placeholder: "name of an org template repo; blank for an empty repo"
25158
+ });
25159
+ if (prompts.isCancel(v2)) throw new ConfirmError("repo create cancelled");
25160
+ return blankToUndefined(String(v2));
25161
+ }
25162
+ const v = await prompts.select({
25163
+ message: "Template",
25164
+ options: [
25165
+ { value: "", label: "None (blank repo)" },
25166
+ ...templates.map((t2) => ({ value: t2, label: t2 }))
25167
+ ],
25168
+ initialValue: ""
25169
+ });
25170
+ if (prompts.isCancel(v)) throw new ConfirmError("repo create cancelled");
25171
+ return blankToUndefined(String(v));
25172
+ }
25173
+ async function create(options, deps = {}) {
25174
+ const command = "repo create";
25175
+ const success2 = deps.logSuccess ?? ((s) => O2.success(s));
25176
+ const error48 = deps.logError ?? ((s) => O2.error(s));
25177
+ const exit = deps.exit ?? exitWithCode;
25178
+ const prompts = deps.prompts ?? defaultRepoPrompts;
25179
+ const isTTY = deps.isTTY ?? Boolean(process.stdout.isTTY);
25180
+ const canPrompt = !options.json && !options.yes && isTTY;
25181
+ let identitySource = "";
25182
+ try {
25183
+ let client;
25184
+ const ensureClient = async () => {
25185
+ if (!client) {
25186
+ const setup = await setupClient2(deps);
25187
+ client = setup.client;
25188
+ identitySource = setup.identitySource;
25189
+ }
25190
+ return client;
25191
+ };
25192
+ let name = blankToUndefined(options.name) ?? "";
25193
+ let visibility = options.visibility;
25194
+ let description = options.description;
25195
+ let template = options.template;
25196
+ if (canPrompt) {
25197
+ if (!name) {
25198
+ const v = await prompts.text({
25199
+ message: "Repository name",
25200
+ placeholder: "learn-python-rpg"
25201
+ });
25202
+ if (prompts.isCancel(v))
25203
+ throw new ConfirmError("repo create cancelled");
25204
+ name = String(v).trim();
25205
+ }
25206
+ if (visibility === void 0) {
25207
+ const v = await prompts.select({
25208
+ message: "Visibility",
25209
+ options: [
25210
+ { value: "private", label: "Private" },
25211
+ { value: "public", label: "Public" }
25212
+ ],
25213
+ initialValue: "private"
25214
+ });
25215
+ if (prompts.isCancel(v))
25216
+ throw new ConfirmError("repo create cancelled");
25217
+ visibility = String(v);
25218
+ }
25219
+ if (description === void 0) {
25220
+ const v = await prompts.text({
25221
+ message: "Description (optional)",
25222
+ placeholder: "What is this project about?"
25223
+ });
25224
+ if (prompts.isCancel(v))
25225
+ throw new ConfirmError("repo create cancelled");
25226
+ description = String(v);
25227
+ }
25228
+ if (template === void 0) {
25229
+ template = await promptTemplate(await ensureClient(), prompts);
25230
+ }
25231
+ }
25232
+ if (!name) {
25233
+ throw new UsageError("repo name is required");
25234
+ }
25235
+ const candidate = { name };
25236
+ if (visibility !== void 0 && visibility !== "") {
25237
+ candidate.visibility = visibility;
25238
+ }
25239
+ const desc = blankToUndefined(description);
25240
+ if (desc !== void 0) candidate.description = desc;
25241
+ const tmpl = blankToUndefined(template);
25242
+ if (tmpl !== void 0) candidate.template = tmpl;
25243
+ const parsed = createRepoRequestSchema.safeParse(candidate);
25244
+ if (!parsed.success) {
25245
+ const issues = parsed.error.issues.map(
25246
+ (i) => `${i.path.join(".") || "input"}: ${i.message}`
25247
+ );
25248
+ throw new UsageError(issues.join("; "));
25249
+ }
25250
+ const body = parsed.data;
25251
+ if (!options.json && !options.yes) {
25252
+ if (!isTTY) {
25253
+ throw new UsageError(
25254
+ "non-interactive session: pass --yes to submit without confirmation (or --json)"
25255
+ );
25256
+ }
25257
+ const ok = await prompts.confirm({
25258
+ message: `Submit request to create ${body.visibility} repo "${body.name}"${body.template ? ` from template ${body.template}` : ""}?`
25259
+ });
25260
+ if (prompts.isCancel(ok) || ok === false) {
25261
+ throw new ConfirmError("repo create cancelled");
25262
+ }
25263
+ }
25264
+ const activeClient = await ensureClient();
25265
+ const row = await activeClient.createRepoRequest({
25266
+ name: body.name,
25267
+ visibility: body.visibility,
25268
+ description: body.description,
25269
+ template: body.template
25270
+ });
25271
+ if (options.json) {
25272
+ emitJson9(
25273
+ buildEnvelope(command, true, {
25274
+ id: row.id,
25275
+ name: row.name,
25276
+ owner: row.owner,
25277
+ visibility: row.visibility,
25278
+ template: row.template,
25279
+ status: row.status,
25280
+ identitySource
25281
+ })
25282
+ );
25283
+ } else {
25284
+ success2(
25285
+ [
25286
+ `Request submitted`,
25287
+ ``,
25288
+ ` Request id: ${row.id}`,
25289
+ ` Repository: ${row.owner}/${row.name}`,
25290
+ ` Visibility: ${row.visibility}`,
25291
+ ...row.template ? [` Template: ${row.template}`] : [],
25292
+ ` Status: ${row.status} \u2014 run \`universe repo ls\` to review`
25293
+ ].join("\n")
25294
+ );
25295
+ }
25296
+ } catch (err) {
25297
+ const { code, message, kind, requestId } = wrapProxyError(command, err);
25298
+ outputError({ json: options.json, command }, code, message, {
25299
+ logError: error48,
25300
+ kind,
25301
+ requestId,
25302
+ extras: identitySource ? { identitySource } : void 0
25303
+ });
25304
+ exit(code);
25305
+ }
25306
+ }
25307
+
25308
+ // src/commands/repo/ls.ts
25309
+ var LS_STATUSES = [...repoStatusSchema.options, "all"];
25310
+ async function ls3(options, deps = {}) {
25311
+ const command = "repo ls";
25312
+ const message = deps.logMessage ?? ((s) => O2.message(s));
25313
+ const error48 = deps.logError ?? ((s) => O2.error(s));
25314
+ const exit = deps.exit ?? exitWithCode;
25315
+ let identitySource;
25316
+ try {
25317
+ if (options.status !== void 0 && !LS_STATUSES.includes(options.status)) {
25318
+ throw new UsageError(
25319
+ `invalid --status "${options.status}": must be one of ${LS_STATUSES.join(", ")}`
25320
+ );
25321
+ }
25322
+ const setup = await setupClient2(deps);
25323
+ const client = setup.client;
25324
+ identitySource = setup.identitySource;
25325
+ const rows = await client.listRepoRequests({
25326
+ status: options.status,
25327
+ mine: options.mine ?? false
25328
+ });
25329
+ const status2 = options.status ?? "pending";
25330
+ if (options.json) {
25331
+ emitJson9(
25332
+ buildEnvelope(command, true, {
25333
+ count: rows.length,
25334
+ status: status2,
25335
+ mine: options.mine ?? false,
25336
+ requests: rows,
25337
+ identitySource
25338
+ })
25339
+ );
25340
+ } else {
25341
+ const empty = status2 === "all" ? "No repo requests." : `No ${status2} repo requests.`;
25342
+ message(formatRepoTable(rows, empty));
25343
+ }
25344
+ } catch (err) {
25345
+ const {
25346
+ code,
25347
+ message: msg,
25348
+ kind,
25349
+ requestId
25350
+ } = wrapProxyError(command, err);
25351
+ outputError({ json: options.json, command }, code, msg, {
25352
+ logError: error48,
25353
+ kind,
25354
+ requestId,
25355
+ extras: identitySource ? { identitySource } : void 0
25356
+ });
25357
+ exit(code);
25358
+ }
25359
+ }
25360
+
25361
+ // src/commands/repo/reject.ts
25362
+ async function reject(options, deps = {}) {
25363
+ const command = "repo reject";
25364
+ const success2 = deps.logSuccess ?? ((s) => O2.success(s));
25365
+ const error48 = deps.logError ?? ((s) => O2.error(s));
25366
+ const exit = deps.exit ?? exitWithCode;
25367
+ const prompts = deps.prompts ?? defaultRepoPrompts;
25368
+ const isTTY = deps.isTTY ?? Boolean(process.stdout.isTTY);
25369
+ let identitySource;
25370
+ try {
25371
+ if (!options.id || options.id.trim().length === 0) {
25372
+ throw new UsageError("request id is required (positional argument)");
25373
+ }
25374
+ const setup = await setupClient2(deps);
25375
+ const client = setup.client;
25376
+ identitySource = setup.identitySource;
25377
+ if (!options.json && !options.yes) {
25378
+ if (!isTTY) {
25379
+ throw new UsageError(
25380
+ "non-interactive session: pass --yes to reject without confirmation"
25381
+ );
25382
+ }
25383
+ const cur = await client.getRepoRequest(options.id);
25384
+ const ok = await prompts.confirm({
25385
+ message: `Reject the request for "${cur.name}" by ${cur.requestedBy}?`
25386
+ });
25387
+ if (prompts.isCancel(ok) || ok === false) {
25388
+ throw new ConfirmError("repo reject cancelled");
25389
+ }
25390
+ }
25391
+ const reason = options.reason === void 0 ? void 0 : String(options.reason).trim() || void 0;
25392
+ const row = await client.rejectRepoRequest({
25393
+ id: options.id,
25394
+ reason
25395
+ });
25396
+ if (options.json) {
25397
+ emitJson9(
25398
+ buildEnvelope(command, true, {
25399
+ id: row.id,
25400
+ status: row.status,
25401
+ repo: `${row.owner}/${row.name}`,
25402
+ rejectReason: row.rejectReason,
25403
+ identitySource
25404
+ })
25405
+ );
25406
+ } else {
25407
+ success2(
25408
+ [
25409
+ `Rejected ${row.name}`,
25410
+ ``,
25411
+ ` Repository: ${row.owner}/${row.name}`,
25412
+ ...row.rejectReason ? [` Reason: ${row.rejectReason}`] : []
25413
+ ].join("\n")
25414
+ );
25415
+ }
25416
+ } catch (err) {
25417
+ const { code, message, kind, requestId } = wrapProxyError(command, err);
25418
+ outputError({ json: options.json, command }, code, message, {
25419
+ logError: error48,
25420
+ kind,
25421
+ requestId,
25422
+ extras: identitySource ? { identitySource } : void 0
25423
+ });
25424
+ exit(code);
25425
+ }
25426
+ }
25427
+
25428
+ // src/commands/repo/status.ts
25429
+ function humanRow(row) {
25430
+ const lines = [
25431
+ `Request ${row.id}`,
25432
+ ``,
25433
+ ` Repository: ${row.owner}/${row.name}`,
25434
+ ` Visibility: ${row.visibility}`,
25435
+ ` Status: ${row.status}`,
25436
+ ` Requested by: ${row.requestedBy}`
25437
+ ];
25438
+ if (row.template) lines.push(` Template: ${row.template}`);
25439
+ if (row.url) lines.push(` URL: ${row.url}`);
25440
+ if (row.approver) lines.push(` Approver: ${row.approver}`);
25441
+ if (row.rejectReason) lines.push(` Reason: ${row.rejectReason}`);
25442
+ if (row.error) lines.push(` Error: ${row.error}`);
25443
+ lines.push(` Created: ${row.createdAt}`);
25444
+ lines.push(` Updated: ${row.updatedAt}`);
25445
+ return lines.join("\n");
25446
+ }
25447
+ async function status(options, deps = {}) {
25448
+ const command = "repo status";
25449
+ const message = deps.logMessage ?? ((s) => O2.message(s));
25450
+ const error48 = deps.logError ?? ((s) => O2.error(s));
25451
+ const exit = deps.exit ?? exitWithCode;
25452
+ let identitySource;
25453
+ try {
25454
+ if (!options.id || options.id.trim().length === 0) {
25455
+ throw new UsageError("request id is required (positional argument)");
25456
+ }
25457
+ const setup = await setupClient2(deps);
25458
+ const client = setup.client;
25459
+ identitySource = setup.identitySource;
25460
+ const row = await client.getRepoRequest(options.id);
25461
+ if (options.json) {
25462
+ emitJson9(buildEnvelope(command, true, { request: row, identitySource }));
25463
+ } else {
25464
+ message(humanRow(row));
25465
+ }
25466
+ } catch (err) {
25467
+ const {
25468
+ code,
25469
+ message: msg,
25470
+ kind,
25471
+ requestId
25472
+ } = wrapProxyError(command, err);
25473
+ outputError({ json: options.json, command }, code, msg, {
25474
+ logError: error48,
25475
+ kind,
25476
+ requestId,
25477
+ extras: identitySource ? { identitySource } : void 0
25478
+ });
25479
+ exit(code);
25480
+ }
25481
+ }
25482
+
24624
25483
  // src/lib/update-notifier.ts
24625
25484
  var import_node_fs3 = require("fs");
24626
25485
  var import_promises8 = require("fs/promises");
@@ -24776,7 +25635,7 @@ function installExitNotice(current) {
24776
25635
  }
24777
25636
 
24778
25637
  // src/cli.ts
24779
- var version2 = true ? "0.7.2" : "0.0.0";
25638
+ var version2 = true ? "0.8.0" : "0.0.0";
24780
25639
  function handleActionError(command, json2, err) {
24781
25640
  const ctx = { json: json2, command };
24782
25641
  const message = err instanceof Error ? err.message : "unknown error";
@@ -24793,12 +25652,14 @@ function findFirstPositional(args) {
24793
25652
  }
24794
25653
  function run(argv = process.argv) {
24795
25654
  installExitNotice(version2);
24796
- void refreshIfStale();
25655
+ void refreshIfStale().catch(() => {
25656
+ });
24797
25657
  const args = argv.slice(2);
24798
25658
  const firstPosIdx = findFirstPositional(args);
24799
25659
  const namespace = firstPosIdx >= 0 ? args[firstPosIdx] : void 0;
24800
25660
  const isStatic = namespace === "static";
24801
25661
  const isSites = namespace === "sites";
25662
+ const isRepo = namespace === "repo";
24802
25663
  if (isSites) {
24803
25664
  const sitesArgs = [
24804
25665
  ...args.slice(0, firstPosIdx),
@@ -24865,6 +25726,136 @@ function run(argv = process.argv) {
24865
25726
  sitesCli.parse(["node", "universe-sites", ...sitesArgs]);
24866
25727
  return;
24867
25728
  }
25729
+ if (isRepo) {
25730
+ const repoArgs = [
25731
+ ...args.slice(0, firstPosIdx),
25732
+ ...args.slice(firstPosIdx + 1)
25733
+ ];
25734
+ const repoCli = cac("universe repo");
25735
+ repoCli.command(
25736
+ "create [name]",
25737
+ "Request a new repository under freeCodeCamp-Universe (staff only)"
25738
+ ).option("--json", "Output as JSON").option("--visibility <vis>", "public or private (default: private)").option("--description <text>", "Repository description").option(
25739
+ "--template <name>",
25740
+ "Org template repo to generate from; omit for a blank repo"
25741
+ ).option("--yes", "Skip confirmation prompts (required for non-TTY/CI)").example("universe repo create my-app --visibility private --yes").action(
25742
+ async (name, flags) => {
25743
+ try {
25744
+ await create({
25745
+ json: flags.json ?? false,
25746
+ name,
25747
+ visibility: flags.visibility,
25748
+ description: flags.description,
25749
+ template: flags.template,
25750
+ yes: flags.yes ?? false
25751
+ });
25752
+ } catch (err) {
25753
+ handleActionError("repo create", flags.json ?? false, err);
25754
+ }
25755
+ }
25756
+ );
25757
+ repoCli.command("ls", "List repo requests (default: pending)").option("--json", "Output as JSON").option(
25758
+ "--status <status>",
25759
+ "pending | approved | active | rejected | failed | all"
25760
+ ).option("--mine", "Only requests you submitted").action(
25761
+ async (flags) => {
25762
+ try {
25763
+ await ls3({
25764
+ json: flags.json ?? false,
25765
+ status: flags.status,
25766
+ mine: flags.mine ?? false
25767
+ });
25768
+ } catch (err) {
25769
+ handleActionError("repo ls", flags.json ?? false, err);
25770
+ }
25771
+ }
25772
+ );
25773
+ repoCli.command(
25774
+ "approve <id>",
25775
+ "Approve a pending request \u2014 creates the repo (admin only)"
25776
+ ).option("--json", "Output as JSON").option("--yes", "Skip confirmation prompts (required for non-TTY/CI)").example("universe repo approve req_abc123 --yes --json").action(async (id, flags) => {
25777
+ try {
25778
+ await approve({
25779
+ json: flags.json ?? false,
25780
+ id,
25781
+ yes: flags.yes ?? false
25782
+ });
25783
+ } catch (err) {
25784
+ handleActionError("repo approve", flags.json ?? false, err);
25785
+ }
25786
+ });
25787
+ repoCli.command("reject <id>", "Reject a pending request (admin only)").option("--json", "Output as JSON").option("--reason <text>", "Reason shown to the requester").option("--yes", "Skip confirmation prompts (required for non-TTY/CI)").example('universe repo reject req_abc123 --reason "out of scope" --yes').action(
25788
+ async (id, flags) => {
25789
+ try {
25790
+ await reject({
25791
+ json: flags.json ?? false,
25792
+ id,
25793
+ reason: flags.reason,
25794
+ yes: flags.yes ?? false
25795
+ });
25796
+ } catch (err) {
25797
+ handleActionError("repo reject", flags.json ?? false, err);
25798
+ }
25799
+ }
25800
+ );
25801
+ repoCli.command("status <id>", "Show a request's current state").option("--json", "Output as JSON").action(async (id, flags) => {
25802
+ try {
25803
+ await status({ json: flags.json ?? false, id });
25804
+ } catch (err) {
25805
+ handleActionError("repo status", flags.json ?? false, err);
25806
+ }
25807
+ });
25808
+ repoCli.help();
25809
+ repoCli.version(version2);
25810
+ const knownRepoSubs = /* @__PURE__ */ new Set([
25811
+ "create",
25812
+ "ls",
25813
+ "approve",
25814
+ "reject",
25815
+ "status"
25816
+ ]);
25817
+ const repoValueFlags = /* @__PURE__ */ new Set([
25818
+ "--visibility",
25819
+ "--description",
25820
+ "--template",
25821
+ "--status",
25822
+ "--reason"
25823
+ ]);
25824
+ let repoSub;
25825
+ for (let i = 0; i < repoArgs.length; i += 1) {
25826
+ const a = repoArgs[i];
25827
+ if (a === void 0) continue;
25828
+ if (repoValueFlags.has(a)) {
25829
+ i += 1;
25830
+ continue;
25831
+ }
25832
+ if (!a.startsWith("-")) {
25833
+ repoSub = a;
25834
+ break;
25835
+ }
25836
+ }
25837
+ const repoJson = repoArgs.includes("--json");
25838
+ const repoWantsHelp = repoArgs.includes("--help") || repoArgs.includes("-h") || repoArgs.includes("--version");
25839
+ if (repoSub === void 0 ? !repoWantsHelp : !knownRepoSubs.has(repoSub)) {
25840
+ if (repoSub === void 0 && !repoJson) {
25841
+ repoCli.outputHelp();
25842
+ } else {
25843
+ outputError(
25844
+ { json: repoJson, command: "repo" },
25845
+ EXIT_USAGE,
25846
+ repoSub === void 0 ? "missing repo subcommand \u2014 run `universe repo --help`" : `unknown repo subcommand "${repoSub}" \u2014 run \`universe repo --help\``
25847
+ );
25848
+ }
25849
+ exitWithCode(EXIT_USAGE);
25850
+ return;
25851
+ }
25852
+ try {
25853
+ repoCli.parse(["node", "universe-repo", ...repoArgs]);
25854
+ } catch (err) {
25855
+ handleActionError("repo", repoJson, err);
25856
+ }
25857
+ return;
25858
+ }
24868
25859
  if (isStatic) {
24869
25860
  const staticArgs = [
24870
25861
  ...args.slice(0, firstPosIdx),
@@ -24948,11 +25939,30 @@ function run(argv = process.argv) {
24948
25939
  });
24949
25940
  cli.command("static <subcommand>", "Static site deployment commands");
24950
25941
  cli.command("sites <subcommand>", "Static site registry commands");
25942
+ cli.command(
25943
+ "repo <subcommand>",
25944
+ "Repository creation + approval queue commands"
25945
+ );
24951
25946
  cli.help();
24952
25947
  cli.version(version2);
24953
25948
  cli.parse(argv);
24954
25949
  }
24955
25950
  }
24956
25951
 
25952
+ // src/lib/fatal.ts
25953
+ function formatFatal(err) {
25954
+ const message = err instanceof Error ? err.message : String(err);
25955
+ return `universe: ${redact(message)}`;
25956
+ }
25957
+ function defaultOnFatal(err) {
25958
+ process.stderr.write(formatFatal(err) + "\n");
25959
+ process.exit(EXIT_USAGE);
25960
+ }
25961
+ function installFatalHandlers(onFatal = defaultOnFatal) {
25962
+ process.on("unhandledRejection", onFatal);
25963
+ process.on("uncaughtException", onFatal);
25964
+ }
25965
+
24957
25966
  // src/index.ts
25967
+ installFatalHandlers();
24958
25968
  run();