lopebase 0.3.1 → 0.4.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 (3) hide show
  1. package/README.md +5 -3
  2. package/dist/lopebase.js +291 -253
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -34,8 +34,10 @@ npx lopebase
34
34
  names, and the variable *names* in example env files, then shows your
35
35
  stack, the services you already use, and what it did not find (for example
36
36
  analytics or error logging). You see each thing it reads as it reads it.
37
- 2. **Connect this computer.** It opens your LopeBase workspace and shows a
38
- short code. Check it matches and press Connect.
37
+ 2. **Connect this computer.** It opens LopeBase in your browser and shows a
38
+ short code. Sign in, or create a free account if you are new, check the code
39
+ matches, and press Connect. No address to type: it finds your workspace.
40
+ For a custom domain or your own server, add `--url https://your-address`.
39
41
  3. **Pick what to connect.** Services already in your code are ticked; the
40
42
  ones you do not use yet are offered, never forced.
41
43
  4. **Hand over to your agent.** It finds the coding agents you have installed
@@ -76,7 +78,7 @@ Made for web products, and fine with anything else:
76
78
  | Command | What it does |
77
79
  | --- | --- |
78
80
  | `npx lopebase` | Scan, connect, choose, and hand over to your agent. |
79
- | `npx lopebase login` | Connect this computer to your workspace with a short code. `--paste` to paste an agent token instead. |
81
+ | `npx lopebase login` | Connect this computer to your workspace with a short code, creating an account if you need one. `--paste` to paste an agent token instead. |
80
82
  | `npx lopebase scan` | Show what the scan found. `--json` for scripts. |
81
83
  | `npx lopebase prompt` | Print the setup prompt instead of starting an agent. |
82
84
  | `npx lopebase mcp` | Run the LopeBase tools as a local MCP server (stdio) for any agent. |
package/dist/lopebase.js CHANGED
@@ -14,7 +14,7 @@ var __toESMCache_node, __toESMCache_esm, __toESM = (mod, isNodeMode, target) =>
14
14
  return cached;
15
15
  }
16
16
  target = mod != null ? __create(__getProtoOf(mod)) : {};
17
- let to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: !0 }) : target;
17
+ let to = isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", { value: mod, enumerable: !0 }) : target;
18
18
  if (mod && typeof mod === "object" || typeof mod === "function") {
19
19
  for (let key of __getOwnPropNames(mod))
20
20
  if (!__hasOwnProp.call(to, key))
@@ -157,8 +157,8 @@ var require_windows = __commonJS(function(exports, module) {
157
157
  if (pathext = pathext.split(";"), pathext.indexOf("") !== -1)
158
158
  return !0;
159
159
  for (var i = 0;i < pathext.length; i++) {
160
- var p2 = pathext[i].toLowerCase();
161
- if (p2 && path.substr(-p2.length).toLowerCase() === p2)
160
+ var p = pathext[i].toLowerCase();
161
+ if (p && path.substr(-p.length).toLowerCase() === p)
162
162
  return !0;
163
163
  }
164
164
  return !1;
@@ -195,7 +195,7 @@ var require_mode = __commonJS(function(exports, module) {
195
195
  return stat.isFile() && checkMode(stat, options);
196
196
  }
197
197
  function checkMode(stat, options) {
198
- var { mode: mod, uid, gid } = stat, myUid = options.uid !== void 0 ? options.uid : process.getuid && process.getuid(), myGid = options.gid !== void 0 ? options.gid : process.getgid && process.getgid(), u2 = parseInt("100", 8), g2 = parseInt("010", 8), o2 = parseInt("001", 8), ug = u2 | g2, ret = mod & o2 || mod & g2 && gid === myGid || mod & u2 && uid === myUid || mod & ug && myUid === 0;
198
+ var { mode: mod, uid, gid } = stat, myUid = options.uid !== void 0 ? options.uid : process.getuid && process.getuid(), myGid = options.gid !== void 0 ? options.gid : process.getgid && process.getgid(), u = parseInt("100", 8), g = parseInt("010", 8), o = parseInt("001", 8), ug = u | g, ret = mod & o || mod & g && gid === myGid || mod & u && uid === myUid || mod & ug && myUid === 0;
199
199
  return ret;
200
200
  }
201
201
  });
@@ -268,19 +268,19 @@ var require_which = __commonJS(function(exports, module) {
268
268
  let { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt), found = [], step = (i) => new Promise((resolve, reject) => {
269
269
  if (i === pathEnv.length)
270
270
  return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd));
271
- let ppRaw = pathEnv[i], pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw, pCmd = path.join(pathPart, cmd), p2 = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
272
- resolve(subStep(p2, i, 0));
273
- }), subStep = (p2, i, ii) => new Promise((resolve, reject) => {
271
+ let ppRaw = pathEnv[i], pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw, pCmd = path.join(pathPart, cmd), p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
272
+ resolve(subStep(p, i, 0));
273
+ }), subStep = (p, i, ii) => new Promise((resolve, reject) => {
274
274
  if (ii === pathExt.length)
275
275
  return resolve(step(i + 1));
276
276
  let ext = pathExt[ii];
277
- isexe(p2 + ext, { pathExt: pathExtExe }, (er, is) => {
277
+ isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
278
278
  if (!er && is)
279
279
  if (opt.all)
280
- found.push(p2 + ext);
280
+ found.push(p + ext);
281
281
  else
282
- return resolve(p2 + ext);
283
- return resolve(subStep(p2, i, ii + 1));
282
+ return resolve(p + ext);
283
+ return resolve(subStep(p, i, ii + 1));
284
284
  });
285
285
  });
286
286
  return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
@@ -288,9 +288,9 @@ var require_which = __commonJS(function(exports, module) {
288
288
  opt = opt || {};
289
289
  let { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt), found = [];
290
290
  for (let i = 0;i < pathEnv.length; i++) {
291
- let ppRaw = pathEnv[i], pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw, pCmd = path.join(pathPart, cmd), p2 = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
292
- for (let j2 = 0;j2 < pathExt.length; j2++) {
293
- let cur = p2 + pathExt[j2];
291
+ let ppRaw = pathEnv[i], pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw, pCmd = path.join(pathPart, cmd), p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
292
+ for (let j = 0;j < pathExt.length; j++) {
293
+ let cur = p + pathExt[j];
294
294
  try {
295
295
  if (isexe.sync(cur, { pathExt: pathExtExe }))
296
296
  if (opt.all)
@@ -337,7 +337,7 @@ var require_resolveCommand = __commonJS(function(exports, module) {
337
337
  path: env[getPathKey({ env })],
338
338
  pathExt: withoutPathExt ? path.delimiter : void 0
339
339
  });
340
- } catch (e2) {} finally {
340
+ } catch (e) {} finally {
341
341
  if (shouldSwitchCwd)
342
342
  process.chdir(cwd);
343
343
  }
@@ -392,7 +392,7 @@ var require_readShebang = __commonJS(function(exports, module) {
392
392
  let buffer = Buffer.alloc(150), fd;
393
393
  try {
394
394
  fd = fs.openSync(command, "r"), fs.readSync(fd, buffer, 0, 150, 0), fs.closeSync(fd);
395
- } catch (e2) {}
395
+ } catch (e) {}
396
396
  return shebangCommand(buffer.toString());
397
397
  }
398
398
  module.exports = readShebang;
@@ -968,17 +968,17 @@ var V2 = ce(), u = (t, n) => V2 ? t : n, le = u("◆", "*"), L2 = u("■", "x"),
968
968
  return import_picocolors2.default.green(C);
969
969
  }
970
970
  }, G2 = (t) => {
971
- let { cursor: n, options: r2, style: i } = t, s = t.maxItems ?? Number.POSITIVE_INFINITY, c = Math.max(process.stdout.rows - 4, 0), a = Math.min(c, Math.max(s, 5)), l2 = 0;
972
- n >= l2 + a - 3 ? l2 = Math.max(Math.min(n - a + 3, r2.length - a), 0) : n < l2 + 2 && (l2 = Math.max(n - 2, 0));
973
- let $2 = a < r2.length && l2 > 0, g2 = a < r2.length && l2 + a < r2.length;
974
- return r2.slice(l2, l2 + a).map((p2, v2, f) => {
975
- let j2 = v2 === 0 && $2, E = v2 === f.length - 1 && g2;
976
- return j2 || E ? import_picocolors2.default.dim("...") : i(p2, v2 + l2 === n);
971
+ let { cursor: n, options: r, style: i } = t, s = t.maxItems ?? Number.POSITIVE_INFINITY, c = Math.max(process.stdout.rows - 4, 0), a = Math.min(c, Math.max(s, 5)), l = 0;
972
+ n >= l + a - 3 ? l = Math.max(Math.min(n - a + 3, r.length - a), 0) : n < l + 2 && (l = Math.max(n - 2, 0));
973
+ let $ = a < r.length && l > 0, g = a < r.length && l + a < r.length;
974
+ return r.slice(l, l + a).map((p, v, f) => {
975
+ let j = v === 0 && $, E = v === f.length - 1 && g;
976
+ return j || E ? import_picocolors2.default.dim("...") : i(p, v + l === n);
977
977
  });
978
978
  }, he = (t) => new RD({ validate: t.validate, placeholder: t.placeholder, defaultValue: t.defaultValue, initialValue: t.initialValue, render() {
979
979
  let n = `${import_picocolors2.default.gray(o)}
980
980
  ${b2(this.state)} ${t.message}
981
- `, r2 = t.placeholder ? import_picocolors2.default.inverse(t.placeholder[0]) + import_picocolors2.default.dim(t.placeholder.slice(1)) : import_picocolors2.default.inverse(import_picocolors2.default.hidden("_")), i = this.value ? this.valueWithCursor : r2;
981
+ `, r = t.placeholder ? import_picocolors2.default.inverse(t.placeholder[0]) + import_picocolors2.default.dim(t.placeholder.slice(1)) : import_picocolors2.default.inverse(import_picocolors2.default.hidden("_")), i = this.value ? this.valueWithCursor : r;
982
982
  switch (this.state) {
983
983
  case "error":
984
984
  return `${n.trim()}
@@ -998,7 +998,7 @@ ${import_picocolors2.default.cyan(d2)}
998
998
  } }).prompt(), ge = (t) => new MD({ validate: t.validate, mask: t.mask ?? $e, render() {
999
999
  let n = `${import_picocolors2.default.gray(o)}
1000
1000
  ${b2(this.state)} ${t.message}
1001
- `, r2 = this.valueWithCursor, i = this.masked;
1001
+ `, r = this.valueWithCursor, i = this.masked;
1002
1002
  switch (this.state) {
1003
1003
  case "error":
1004
1004
  return `${n.trim()}
@@ -1011,16 +1011,16 @@ ${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(th
1011
1011
  return `${n}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(i ?? ""))}${i ? `
1012
1012
  ${import_picocolors2.default.gray(o)}` : ""}`;
1013
1013
  default:
1014
- return `${n}${import_picocolors2.default.cyan(o)} ${r2}
1014
+ return `${n}${import_picocolors2.default.cyan(o)} ${r}
1015
1015
  ${import_picocolors2.default.cyan(d2)}
1016
1016
  `;
1017
1017
  }
1018
1018
  } }).prompt(), ye = (t) => {
1019
- let n = t.active ?? "Yes", r2 = t.inactive ?? "No";
1020
- return new dD({ active: n, inactive: r2, initialValue: t.initialValue ?? !0, render() {
1019
+ let n = t.active ?? "Yes", r = t.inactive ?? "No";
1020
+ return new dD({ active: n, inactive: r, initialValue: t.initialValue ?? !0, render() {
1021
1021
  let i = `${import_picocolors2.default.gray(o)}
1022
1022
  ${b2(this.state)} ${t.message}
1023
- `, s = this.value ? n : r2;
1023
+ `, s = this.value ? n : r;
1024
1024
  switch (this.state) {
1025
1025
  case "submit":
1026
1026
  return `${i}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.dim(s)}`;
@@ -1028,19 +1028,19 @@ ${b2(this.state)} ${t.message}
1028
1028
  return `${i}${import_picocolors2.default.gray(o)} ${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(s))}
1029
1029
  ${import_picocolors2.default.gray(o)}`;
1030
1030
  default:
1031
- return `${i}${import_picocolors2.default.cyan(o)} ${this.value ? `${import_picocolors2.default.green(k2)} ${n}` : `${import_picocolors2.default.dim(P2)} ${import_picocolors2.default.dim(n)}`} ${import_picocolors2.default.dim("/")} ${this.value ? `${import_picocolors2.default.dim(P2)} ${import_picocolors2.default.dim(r2)}` : `${import_picocolors2.default.green(k2)} ${r2}`}
1031
+ return `${i}${import_picocolors2.default.cyan(o)} ${this.value ? `${import_picocolors2.default.green(k2)} ${n}` : `${import_picocolors2.default.dim(P2)} ${import_picocolors2.default.dim(n)}`} ${import_picocolors2.default.dim("/")} ${this.value ? `${import_picocolors2.default.dim(P2)} ${import_picocolors2.default.dim(r)}` : `${import_picocolors2.default.green(k2)} ${r}`}
1032
1032
  ${import_picocolors2.default.cyan(d2)}
1033
1033
  `;
1034
1034
  }
1035
1035
  } }).prompt();
1036
1036
  }, ve = (t) => {
1037
- let n = (r2, i) => {
1038
- let s = r2.label ?? String(r2.value);
1037
+ let n = (r, i) => {
1038
+ let s = r.label ?? String(r.value);
1039
1039
  switch (i) {
1040
1040
  case "selected":
1041
1041
  return `${import_picocolors2.default.dim(s)}`;
1042
1042
  case "active":
1043
- return `${import_picocolors2.default.green(k2)} ${s} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}`;
1043
+ return `${import_picocolors2.default.green(k2)} ${s} ${r.hint ? import_picocolors2.default.dim(`(${r.hint})`) : ""}`;
1044
1044
  case "cancelled":
1045
1045
  return `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(s))}`;
1046
1046
  default:
@@ -1048,17 +1048,17 @@ ${import_picocolors2.default.cyan(d2)}
1048
1048
  }
1049
1049
  };
1050
1050
  return new LD({ options: t.options, initialValue: t.initialValue, render() {
1051
- let r2 = `${import_picocolors2.default.gray(o)}
1051
+ let r = `${import_picocolors2.default.gray(o)}
1052
1052
  ${b2(this.state)} ${t.message}
1053
1053
  `;
1054
1054
  switch (this.state) {
1055
1055
  case "submit":
1056
- return `${r2}${import_picocolors2.default.gray(o)} ${n(this.options[this.cursor], "selected")}`;
1056
+ return `${r}${import_picocolors2.default.gray(o)} ${n(this.options[this.cursor], "selected")}`;
1057
1057
  case "cancel":
1058
- return `${r2}${import_picocolors2.default.gray(o)} ${n(this.options[this.cursor], "cancelled")}
1058
+ return `${r}${import_picocolors2.default.gray(o)} ${n(this.options[this.cursor], "cancelled")}
1059
1059
  ${import_picocolors2.default.gray(o)}`;
1060
1060
  default:
1061
- return `${r2}${import_picocolors2.default.cyan(o)} ${G2({ cursor: this.cursor, options: this.options, maxItems: t.maxItems, style: (i, s) => n(i, s ? "active" : "inactive") }).join(`
1061
+ return `${r}${import_picocolors2.default.cyan(o)} ${G2({ cursor: this.cursor, options: this.options, maxItems: t.maxItems, style: (i, s) => n(i, s ? "active" : "inactive") }).join(`
1062
1062
  ${import_picocolors2.default.cyan(o)} `)}
1063
1063
  ${import_picocolors2.default.cyan(d2)}
1064
1064
  `;
@@ -1066,16 +1066,16 @@ ${import_picocolors2.default.cyan(d2)}
1066
1066
  } }).prompt();
1067
1067
  };
1068
1068
  var fe = (t) => {
1069
- let n = (r2, i) => {
1070
- let s = r2.label ?? String(r2.value);
1071
- return i === "active" ? `${import_picocolors2.default.cyan(A2)} ${s} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "selected" ? `${import_picocolors2.default.green(T)} ${import_picocolors2.default.dim(s)} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "cancelled" ? `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(s))}` : i === "active-selected" ? `${import_picocolors2.default.green(T)} ${s} ${r2.hint ? import_picocolors2.default.dim(`(${r2.hint})`) : ""}` : i === "submitted" ? `${import_picocolors2.default.dim(s)}` : `${import_picocolors2.default.dim(F)} ${import_picocolors2.default.dim(s)}`;
1069
+ let n = (r, i) => {
1070
+ let s = r.label ?? String(r.value);
1071
+ return i === "active" ? `${import_picocolors2.default.cyan(A2)} ${s} ${r.hint ? import_picocolors2.default.dim(`(${r.hint})`) : ""}` : i === "selected" ? `${import_picocolors2.default.green(T)} ${import_picocolors2.default.dim(s)} ${r.hint ? import_picocolors2.default.dim(`(${r.hint})`) : ""}` : i === "cancelled" ? `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(s))}` : i === "active-selected" ? `${import_picocolors2.default.green(T)} ${s} ${r.hint ? import_picocolors2.default.dim(`(${r.hint})`) : ""}` : i === "submitted" ? `${import_picocolors2.default.dim(s)}` : `${import_picocolors2.default.dim(F)} ${import_picocolors2.default.dim(s)}`;
1072
1072
  };
1073
- return new SD({ options: t.options, initialValues: t.initialValues, required: t.required ?? !0, cursorAt: t.cursorAt, validate(r2) {
1074
- if (this.required && r2.length === 0)
1073
+ return new SD({ options: t.options, initialValues: t.initialValues, required: t.required ?? !0, cursorAt: t.cursorAt, validate(r) {
1074
+ if (this.required && r.length === 0)
1075
1075
  return `Please select at least one option.
1076
1076
  ${import_picocolors2.default.reset(import_picocolors2.default.dim(`Press ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" space ")))} to select, ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" enter ")))} to submit`))}`;
1077
1077
  }, render() {
1078
- let r2 = `${import_picocolors2.default.gray(o)}
1078
+ let r = `${import_picocolors2.default.gray(o)}
1079
1079
  ${b2(this.state)} ${t.message}
1080
1080
  `, i = (s, c) => {
1081
1081
  let a = this.value.includes(s.value);
@@ -1083,23 +1083,23 @@ ${b2(this.state)} ${t.message}
1083
1083
  };
1084
1084
  switch (this.state) {
1085
1085
  case "submit":
1086
- return `${r2}${import_picocolors2.default.gray(o)} ${this.options.filter(({ value: s }) => this.value.includes(s)).map((s) => n(s, "submitted")).join(import_picocolors2.default.dim(", ")) || import_picocolors2.default.dim("none")}`;
1086
+ return `${r}${import_picocolors2.default.gray(o)} ${this.options.filter(({ value: s }) => this.value.includes(s)).map((s) => n(s, "submitted")).join(import_picocolors2.default.dim(", ")) || import_picocolors2.default.dim("none")}`;
1087
1087
  case "cancel": {
1088
1088
  let s = this.options.filter(({ value: c }) => this.value.includes(c)).map((c) => n(c, "cancelled")).join(import_picocolors2.default.dim(", "));
1089
- return `${r2}${import_picocolors2.default.gray(o)} ${s.trim() ? `${s}
1089
+ return `${r}${import_picocolors2.default.gray(o)} ${s.trim() ? `${s}
1090
1090
  ${import_picocolors2.default.gray(o)}` : ""}`;
1091
1091
  }
1092
1092
  case "error": {
1093
1093
  let s = this.error.split(`
1094
1094
  `).map((c, a) => a === 0 ? `${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(c)}` : ` ${c}`).join(`
1095
1095
  `);
1096
- return `${r2 + import_picocolors2.default.yellow(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
1096
+ return `${r + import_picocolors2.default.yellow(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
1097
1097
  ${import_picocolors2.default.yellow(o)} `)}
1098
1098
  ${s}
1099
1099
  `;
1100
1100
  }
1101
1101
  default:
1102
- return `${r2}${import_picocolors2.default.cyan(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
1102
+ return `${r}${import_picocolors2.default.cyan(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
1103
1103
  ${import_picocolors2.default.cyan(o)} `)}
1104
1104
  ${import_picocolors2.default.cyan(d2)}
1105
1105
  `;
@@ -1107,13 +1107,13 @@ ${import_picocolors2.default.cyan(d2)}
1107
1107
  } }).prompt();
1108
1108
  };
1109
1109
  var Me = (t = "", n = "") => {
1110
- let r2 = `
1110
+ let r = `
1111
1111
  ${t}
1112
1112
  `.split(`
1113
- `), i = S2(n).length, s = Math.max(r2.reduce((a, l2) => {
1114
- let $2 = S2(l2);
1115
- return $2.length > a ? $2.length : a;
1116
- }, 0), i) + 2, c = r2.map((a) => `${import_picocolors2.default.gray(o)} ${import_picocolors2.default.dim(a)}${" ".repeat(s - S2(a).length)}${import_picocolors2.default.gray(o)}`).join(`
1113
+ `), i = S2(n).length, s = Math.max(r.reduce((a, l) => {
1114
+ let $ = S2(l);
1115
+ return $.length > a ? $.length : a;
1116
+ }, 0), i) + 2, c = r.map((a) => `${import_picocolors2.default.gray(o)} ${import_picocolors2.default.dim(a)}${" ".repeat(s - S2(a).length)}${import_picocolors2.default.gray(o)}`).join(`
1117
1117
  `);
1118
1118
  process.stdout.write(`${import_picocolors2.default.gray(o)}
1119
1119
  ${import_picocolors2.default.green(C)} ${import_picocolors2.default.reset(n)} ${import_picocolors2.default.gray(_2.repeat(Math.max(s - i - 1, 1)) + me)}
@@ -1133,13 +1133,13 @@ ${import_picocolors2.default.gray(d2)} ${t}
1133
1133
 
1134
1134
  `);
1135
1135
  }, M2 = { message: (t = "", { symbol: n = import_picocolors2.default.gray(o) } = {}) => {
1136
- let r2 = [`${import_picocolors2.default.gray(o)}`];
1136
+ let r = [`${import_picocolors2.default.gray(o)}`];
1137
1137
  if (t) {
1138
1138
  let [i, ...s] = t.split(`
1139
1139
  `);
1140
- r2.push(`${n} ${i}`, ...s.map((c) => `${import_picocolors2.default.gray(o)} ${c}`));
1140
+ r.push(`${n} ${i}`, ...s.map((c) => `${import_picocolors2.default.gray(o)} ${c}`));
1141
1141
  }
1142
- process.stdout.write(`${r2.join(`
1142
+ process.stdout.write(`${r.join(`
1143
1143
  `)}
1144
1144
  `);
1145
1145
  }, info: (t) => {
@@ -1156,52 +1156,52 @@ ${import_picocolors2.default.gray(d2)} ${t}
1156
1156
  M2.message(t, { symbol: import_picocolors2.default.red(K2) });
1157
1157
  } }, J2 = `${import_picocolors2.default.gray(o)} `;
1158
1158
  var Y2 = ({ indicator: t = "dots" } = {}) => {
1159
- let n = V2 ? ["◒", "◐", "◓", "◑"] : ["•", "o", "O", "0"], r2 = V2 ? 80 : 120, i = process.env.CI === "true", s, c, a = !1, l2 = "", $2, g2 = performance.now(), p2 = (m2) => {
1160
- let h2 = m2 > 1 ? "Something went wrong" : "Canceled";
1161
- a && N2(h2, m2);
1162
- }, v2 = () => p2(2), f = () => p2(1), j2 = () => {
1163
- process.on("uncaughtExceptionMonitor", v2), process.on("unhandledRejection", v2), process.on("SIGINT", f), process.on("SIGTERM", f), process.on("exit", p2);
1159
+ let n = V2 ? ["◒", "◐", "◓", "◑"] : ["•", "o", "O", "0"], r = V2 ? 80 : 120, i = process.env.CI === "true", s, c, a = !1, l = "", $, g = performance.now(), p = (m) => {
1160
+ let h = m > 1 ? "Something went wrong" : "Canceled";
1161
+ a && N(h, m);
1162
+ }, v = () => p(2), f = () => p(1), j = () => {
1163
+ process.on("uncaughtExceptionMonitor", v), process.on("unhandledRejection", v), process.on("SIGINT", f), process.on("SIGTERM", f), process.on("exit", p);
1164
1164
  }, E = () => {
1165
- process.removeListener("uncaughtExceptionMonitor", v2), process.removeListener("unhandledRejection", v2), process.removeListener("SIGINT", f), process.removeListener("SIGTERM", f), process.removeListener("exit", p2);
1166
- }, B2 = () => {
1167
- if ($2 === void 0)
1165
+ process.removeListener("uncaughtExceptionMonitor", v), process.removeListener("unhandledRejection", v), process.removeListener("SIGINT", f), process.removeListener("SIGTERM", f), process.removeListener("exit", p);
1166
+ }, B = () => {
1167
+ if ($ === void 0)
1168
1168
  return;
1169
1169
  i && process.stdout.write(`
1170
1170
  `);
1171
- let m2 = $2.split(`
1171
+ let m = $.split(`
1172
1172
  `);
1173
- process.stdout.write(import_sisteransi2.cursor.move(-999, m2.length - 1)), process.stdout.write(import_sisteransi2.erase.down(m2.length));
1174
- }, R2 = (m2) => m2.replace(/\.+$/, ""), O2 = (m2) => {
1175
- let h2 = (performance.now() - m2) / 1000, w2 = Math.floor(h2 / 60), I2 = Math.floor(h2 % 60);
1176
- return w2 > 0 ? `[${w2}m ${I2}s]` : `[${I2}s]`;
1177
- }, H2 = (m2 = "") => {
1178
- a = !0, s = fD(), l2 = R2(m2), g2 = performance.now(), process.stdout.write(`${import_picocolors2.default.gray(o)}
1173
+ process.stdout.write(import_sisteransi2.cursor.move(-999, m.length - 1)), process.stdout.write(import_sisteransi2.erase.down(m.length));
1174
+ }, R = (m) => m.replace(/\.+$/, ""), O = (m) => {
1175
+ let h = (performance.now() - m) / 1000, w = Math.floor(h / 60), I = Math.floor(h % 60);
1176
+ return w > 0 ? `[${w}m ${I}s]` : `[${I}s]`;
1177
+ }, H = (m = "") => {
1178
+ a = !0, s = fD(), l = R(m), g = performance.now(), process.stdout.write(`${import_picocolors2.default.gray(o)}
1179
1179
  `);
1180
- let h2 = 0, w2 = 0;
1181
- j2(), c = setInterval(() => {
1182
- if (i && l2 === $2)
1180
+ let h = 0, w = 0;
1181
+ j(), c = setInterval(() => {
1182
+ if (i && l === $)
1183
1183
  return;
1184
- B2(), $2 = l2;
1185
- let I2 = import_picocolors2.default.magenta(n[h2]);
1184
+ B(), $ = l;
1185
+ let I = import_picocolors2.default.magenta(n[h]);
1186
1186
  if (i)
1187
- process.stdout.write(`${I2} ${l2}...`);
1187
+ process.stdout.write(`${I} ${l}...`);
1188
1188
  else if (t === "timer")
1189
- process.stdout.write(`${I2} ${l2} ${O2(g2)}`);
1189
+ process.stdout.write(`${I} ${l} ${O(g)}`);
1190
1190
  else {
1191
- let z2 = ".".repeat(Math.floor(w2)).slice(0, 3);
1192
- process.stdout.write(`${I2} ${l2}${z2}`);
1191
+ let z = ".".repeat(Math.floor(w)).slice(0, 3);
1192
+ process.stdout.write(`${I} ${l}${z}`);
1193
1193
  }
1194
- h2 = h2 + 1 < n.length ? h2 + 1 : 0, w2 = w2 < n.length ? w2 + 0.125 : 0;
1195
- }, r2);
1196
- }, N2 = (m2 = "", h2 = 0) => {
1197
- a = !1, clearInterval(c), B2();
1198
- let w2 = h2 === 0 ? import_picocolors2.default.green(C) : h2 === 1 ? import_picocolors2.default.red(L2) : import_picocolors2.default.red(W2);
1199
- l2 = R2(m2 ?? l2), t === "timer" ? process.stdout.write(`${w2} ${l2} ${O2(g2)}
1200
- `) : process.stdout.write(`${w2} ${l2}
1194
+ h = h + 1 < n.length ? h + 1 : 0, w = w < n.length ? w + 0.125 : 0;
1195
+ }, r);
1196
+ }, N = (m = "", h = 0) => {
1197
+ a = !1, clearInterval(c), B();
1198
+ let w = h === 0 ? import_picocolors2.default.green(C) : h === 1 ? import_picocolors2.default.red(L2) : import_picocolors2.default.red(W2);
1199
+ l = R(m ?? l), t === "timer" ? process.stdout.write(`${w} ${l} ${O(g)}
1200
+ `) : process.stdout.write(`${w} ${l}
1201
1201
  `), E(), s();
1202
1202
  };
1203
- return { start: H2, stop: N2, message: (m2 = "") => {
1204
- l2 = R2(m2 ?? l2);
1203
+ return { start: H, stop: N, message: (m = "") => {
1204
+ l = R(m ?? l);
1205
1205
  } };
1206
1206
  };
1207
1207
 
@@ -1209,7 +1209,7 @@ var Y2 = ({ indicator: t = "dots" } = {}) => {
1209
1209
  var SIGNATURE_HEADER = "lopebase-signature";
1210
1210
  var enc = /* @__PURE__ */ new TextEncoder;
1211
1211
  function hex(bytes) {
1212
- return [...new Uint8Array(bytes)].map((b3) => b3.toString(16).padStart(2, "0")).join("");
1212
+ return [...new Uint8Array(bytes)].map((b) => b.toString(16).padStart(2, "0")).join("");
1213
1213
  }
1214
1214
  async function sha256Hex(data) {
1215
1215
  return hex(await crypto.subtle.digest("SHA-256", data));
@@ -1220,8 +1220,8 @@ async function hmacHex(secret, message) {
1220
1220
  }
1221
1221
  function newSigningSecret() {
1222
1222
  let bytes = crypto.getRandomValues(new Uint8Array(32)), bin = "";
1223
- for (let b3 of bytes)
1224
- bin += String.fromCharCode(b3);
1223
+ for (let b of bytes)
1224
+ bin += String.fromCharCode(b);
1225
1225
  return "lbs_" + btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
1226
1226
  }
1227
1227
  function payload(t, method, pathAndQuery, bodyHash) {
@@ -1258,7 +1258,7 @@ var SENSITIVE_PARTS = /* @__PURE__ */ new Set([
1258
1258
  ]), SENSITIVE_JOINED = /apikey|privatekey|secretkey|accesskey|accountnumber|routingnumber|stripecustomer|paymentmethod|recoverycode/;
1259
1259
  function isSensitiveFieldName(name) {
1260
1260
  let parts = name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
1261
- if (parts.some((p2) => SENSITIVE_PARTS.has(p2)))
1261
+ if (parts.some((p) => SENSITIVE_PARTS.has(p)))
1262
1262
  return !0;
1263
1263
  return SENSITIVE_JOINED.test(parts.join(""));
1264
1264
  }
@@ -1277,12 +1277,12 @@ function conformanceChecks(opts) {
1277
1277
  let remote = !opts.adapter, base = opts.baseUrl ? opts.baseUrl.replace(/\/$/, "") : `${opts.origin ?? "https://property.test"}${(opts.basePath ?? opts.adapter?.config.basePath ?? DEFAULT_BASE_PATH).replace(/\/$/, "")}`, send = (req) => opts.adapter ? opts.adapter.handle(req) : fetch(req);
1278
1278
  async function signedHeaders(url, method, bodyText, timestamp) {
1279
1279
  if (opts.secret) {
1280
- let u2 = new URL(url);
1280
+ let u = new URL(url);
1281
1281
  return {
1282
1282
  [SIGNATURE_HEADER]: await signRequest({
1283
1283
  secret: opts.secret,
1284
1284
  method,
1285
- pathAndQuery: u2.pathname + u2.search,
1285
+ pathAndQuery: u.pathname + u.search,
1286
1286
  body: bodyText,
1287
1287
  timestamp
1288
1288
  })
@@ -1300,14 +1300,14 @@ function conformanceChecks(opts) {
1300
1300
  ...init?.headers
1301
1301
  }
1302
1302
  }));
1303
- }, resources = () => authed("/resources").then((r2) => body(r2)), checks = [
1303
+ }, resources = () => authed("/resources").then((r) => body(r)), checks = [
1304
1304
  {
1305
1305
  name: "GET /health returns { ok } without auth",
1306
1306
  async run() {
1307
1307
  let res = await send(new Request(`${base}/health`));
1308
1308
  assert.equal(res.status, 200);
1309
- let b3 = await body(res);
1310
- assert.equal(typeof b3.ok, "boolean");
1309
+ let b = await body(res);
1310
+ assert.equal(typeof b.ok, "boolean");
1311
1311
  }
1312
1312
  },
1313
1313
  {
@@ -1322,10 +1322,10 @@ function conformanceChecks(opts) {
1322
1322
  async run() {
1323
1323
  let res = await authed("/resources");
1324
1324
  assert.equal(res.status, 200);
1325
- let b3 = await body(res);
1326
- assert.ok(Array.isArray(b3.resources));
1327
- for (let r2 of b3.resources)
1328
- assert.ok(r2.name && r2.label && r2.readCapability), assert.ok(Array.isArray(r2.fields));
1325
+ let b = await body(res);
1326
+ assert.ok(Array.isArray(b.resources));
1327
+ for (let r of b.resources)
1328
+ assert.ok(r.name && r.label && r.readCapability), assert.ok(Array.isArray(r.fields));
1329
1329
  }
1330
1330
  },
1331
1331
  {
@@ -1333,9 +1333,9 @@ function conformanceChecks(opts) {
1333
1333
  async run() {
1334
1334
  let res = await authed("/actions");
1335
1335
  assert.equal(res.status, 200);
1336
- let b3 = await body(res);
1337
- assert.ok(Array.isArray(b3.actions));
1338
- for (let a of b3.actions)
1336
+ let b = await body(res);
1337
+ assert.ok(Array.isArray(b.actions));
1338
+ for (let a of b.actions)
1339
1339
  assert.ok(a.name && a.label && a.capability), assert.ok(Array.isArray(a.params));
1340
1340
  }
1341
1341
  },
@@ -1354,7 +1354,7 @@ function conformanceChecks(opts) {
1354
1354
  {
1355
1355
  name: "GET /resources/:name/:id returns a row or 404-null",
1356
1356
  async run() {
1357
- let cat = await resources(), meta = opts.sampleResource ? cat.resources.find((r2) => r2.name === opts.sampleResource) : cat.resources[0];
1357
+ let cat = await resources(), meta = opts.sampleResource ? cat.resources.find((r) => r.name === opts.sampleResource) : cat.resources[0];
1358
1358
  if (!meta)
1359
1359
  return;
1360
1360
  let first = (await body(await authed(`/resources/${meta.name}?limit=1`))).rows[0];
@@ -1369,8 +1369,8 @@ function conformanceChecks(opts) {
1369
1369
  async run() {
1370
1370
  let res = await authed("/audit");
1371
1371
  assert.equal(res.status, 200);
1372
- let b3 = await body(res);
1373
- assert.ok(Array.isArray(b3.rows));
1372
+ let b = await body(res);
1373
+ assert.ok(Array.isArray(b.rows));
1374
1374
  }
1375
1375
  },
1376
1376
  {
@@ -1384,24 +1384,24 @@ function conformanceChecks(opts) {
1384
1384
  name: "no field in the catalog looks like a secret unless marked safe",
1385
1385
  async run() {
1386
1386
  let cat = await resources();
1387
- for (let r2 of cat.resources)
1388
- for (let f of r2.fields)
1389
- assert.ok(!isSensitiveFieldName(f.name) || f.sensitive === "allow", `${r2.name}.${f.name} looks sensitive`);
1387
+ for (let r of cat.resources)
1388
+ for (let f of r.fields)
1389
+ assert.ok(!isSensitiveFieldName(f.name) || f.sensitive === "allow", `${r.name}.${f.name} looks sensitive`);
1390
1390
  }
1391
1391
  },
1392
1392
  {
1393
1393
  name: "rows contain only declared fields, and a page is at most 100",
1394
1394
  async run() {
1395
1395
  let cat = await resources();
1396
- for (let r2 of cat.resources) {
1397
- let res = await authed(`/resources/${r2.name}?limit=1000`);
1396
+ for (let r of cat.resources) {
1397
+ let res = await authed(`/resources/${r.name}?limit=1000`);
1398
1398
  assert.equal(res.status, 200);
1399
1399
  let page = await body(res);
1400
- assert.ok(page.rows.length <= 100, `${r2.name} returned ${page.rows.length} rows`);
1401
- let declared = /* @__PURE__ */ new Set(["id", ...r2.fields.map((f) => f.name)]);
1400
+ assert.ok(page.rows.length <= 100, `${r.name} returned ${page.rows.length} rows`);
1401
+ let declared = /* @__PURE__ */ new Set(["id", ...r.fields.map((f) => f.name)]);
1402
1402
  for (let row of page.rows)
1403
- for (let k3 of Object.keys(row))
1404
- assert.ok(declared.has(k3), `${r2.name} returned undeclared field ${k3}`);
1403
+ for (let k of Object.keys(row))
1404
+ assert.ok(declared.has(k), `${r.name} returned undeclared field ${k}`);
1405
1405
  }
1406
1406
  }
1407
1407
  },
@@ -1419,7 +1419,7 @@ function conformanceChecks(opts) {
1419
1419
  {
1420
1420
  name: "a mutation writes an audit row (PATCH round-trips into /audit)",
1421
1421
  async run() {
1422
- let writable = (await resources()).resources.find((r2) => r2.writeCapability && r2.fields.some((f) => f.editable));
1422
+ let writable = (await resources()).resources.find((r) => r.writeCapability && r.fields.some((f) => f.editable));
1423
1423
  if (!writable || remote)
1424
1424
  return;
1425
1425
  let row = (await body(await authed(`/resources/${writable.name}?limit=1`))).rows[0];
@@ -1455,8 +1455,8 @@ async function checkAdapter(input) {
1455
1455
  })
1456
1456
  } : { authorization: `Bearer ${input.token ?? ""}` };
1457
1457
  status = (await fetch(url, { headers })).status;
1458
- } catch (e2) {
1459
- let error = `Could not reach ${base} (${e2 instanceof Error ? e2.message : String(e2)}).`;
1458
+ } catch (e) {
1459
+ let error = `Could not reach ${base} (${e instanceof Error ? e.message : String(e)}).`;
1460
1460
  return log(`FAIL ${error}`), { passed, failed: [{ name: "reachable", error }] };
1461
1461
  }
1462
1462
  if (status === 401) {
@@ -1474,9 +1474,9 @@ async function checkAdapter(input) {
1474
1474
  }))
1475
1475
  try {
1476
1476
  await c.run(), passed++, log(`pass ${c.name}`);
1477
- } catch (e2) {
1478
- let error = e2 instanceof Error ? e2.message.split(`
1479
- `)[0] : String(e2);
1477
+ } catch (e) {
1478
+ let error = e instanceof Error ? e.message.split(`
1479
+ `)[0] : String(e);
1480
1480
  failed.push({ name: c.name, error }), log(`FAIL ${c.name}: ${error}`);
1481
1481
  }
1482
1482
  return { passed, failed };
@@ -1490,7 +1490,7 @@ var AGENTS = [
1490
1490
  id: "claude",
1491
1491
  label: "Claude Code",
1492
1492
  bin: "claude",
1493
- args: (p2) => [p2],
1493
+ args: (p) => [p],
1494
1494
  mcpAdd: (argv) => [
1495
1495
  "mcp",
1496
1496
  "add",
@@ -1505,27 +1505,27 @@ var AGENTS = [
1505
1505
  id: "codex",
1506
1506
  label: "Codex",
1507
1507
  bin: "codex",
1508
- args: (p2) => [p2],
1508
+ args: (p) => [p],
1509
1509
  mcpAdd: (argv) => ["mcp", "add", "lopebase", "--", ...argv]
1510
1510
  },
1511
1511
  {
1512
1512
  id: "gemini",
1513
1513
  label: "Gemini CLI",
1514
1514
  bin: "gemini",
1515
- args: (p2) => ["-i", p2],
1515
+ args: (p) => ["-i", p],
1516
1516
  mcpAdd: (argv) => ["mcp", "add", "lopebase", ...argv]
1517
1517
  },
1518
1518
  {
1519
1519
  id: "opencode",
1520
1520
  label: "opencode",
1521
1521
  bin: "opencode",
1522
- args: (p2) => ["--prompt", p2]
1522
+ args: (p) => ["--prompt", p]
1523
1523
  },
1524
1524
  {
1525
1525
  id: "cursor-agent",
1526
1526
  label: "Cursor Agent",
1527
1527
  bin: "cursor-agent",
1528
- args: (p2) => [p2]
1528
+ args: (p) => [p]
1529
1529
  }
1530
1530
  ];
1531
1531
  function isInstalled(bin) {
@@ -1564,21 +1564,33 @@ import os from "node:os";
1564
1564
  import path from "node:path";
1565
1565
  var DEFAULT_URL = "https://app.lopebase.com";
1566
1566
  function workspaceUrl(input) {
1567
- let v2 = input.trim().replace(/\/+$/, "");
1568
- if (!v2)
1567
+ let v = input.trim().replace(/\/+$/, "");
1568
+ if (!v)
1569
1569
  return null;
1570
- if (/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(v2))
1571
- return `https://${v2.toLowerCase()}.lopebase.com`;
1572
- let withScheme = /^https?:\/\//i.test(v2) ? v2 : `https://${v2}`;
1570
+ if (/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(v))
1571
+ return `https://${v.toLowerCase()}.lopebase.com`;
1572
+ let withScheme = /^https?:\/\//i.test(v) ? v : `https://${v}`;
1573
1573
  try {
1574
- let u2 = new URL(withScheme);
1575
- if (!u2.hostname.includes(".") && u2.hostname !== "localhost")
1574
+ let u = new URL(withScheme);
1575
+ if (!u.hostname.includes(".") && u.hostname !== "localhost")
1576
1576
  return null;
1577
- return `${u2.origin}${u2.pathname.replace(/\/+$/, "")}`;
1577
+ return `${u.origin}${u.pathname.replace(/\/+$/, "")}`;
1578
1578
  } catch {
1579
1579
  return null;
1580
1580
  }
1581
1581
  }
1582
+ function signInUrl(input) {
1583
+ for (let [raw, from] of [
1584
+ [input.flag, "--url"],
1585
+ [input.env, "LOPEBASE_URL"]
1586
+ ]) {
1587
+ if (raw === void 0 || raw.trim() === "")
1588
+ continue;
1589
+ let url = workspaceUrl(raw);
1590
+ return url ? { url } : { error: `${from} is not an address: ${raw}` };
1591
+ }
1592
+ return { url: input.saved && workspaceUrl(input.saved) || DEFAULT_URL };
1593
+ }
1582
1594
  function configDir() {
1583
1595
  let base = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
1584
1596
  return path.join(base, "lopebase");
@@ -1612,7 +1624,7 @@ var APP_SIDE_ENV = /^(NEXT_PUBLIC_|PUBLIC_|VITE_|NUXT_PUBLIC_|EXPO_PUBLIC_|REACT
1612
1624
  function evidenceFor(facts, rule) {
1613
1625
  let out = [], weak = [];
1614
1626
  for (let re of rule.deps ?? []) {
1615
- let hit = facts.dependencies.find((d3) => re.test(d3));
1627
+ let hit = facts.dependencies.find((d) => re.test(d));
1616
1628
  if (hit)
1617
1629
  out.push(`uses ${hit.replace(/^(py|gem|composer|go|cargo):/, "")}`);
1618
1630
  }
@@ -1622,7 +1634,7 @@ function evidenceFor(facts, rule) {
1622
1634
  out.push(`has ${hit.replace(/#.*$/, "")}`);
1623
1635
  }
1624
1636
  for (let re of rule.env ?? []) {
1625
- let hit = facts.envKeys.find((k3) => re.test(k3));
1637
+ let hit = facts.envKeys.find((k) => re.test(k));
1626
1638
  if (hit)
1627
1639
  (APP_SIDE_ENV.test(hit) ? out : weak).push(`env ${hit}`);
1628
1640
  }
@@ -1661,6 +1673,21 @@ var SOURCE_RULES = {
1661
1673
  },
1662
1674
  why: "Traffic and sources on your dashboard."
1663
1675
  },
1676
+ plausible: {
1677
+ label: "Plausible",
1678
+ rule: {
1679
+ deps: [
1680
+ /^next-plausible$/,
1681
+ /^plausible-tracker$/,
1682
+ /^@plausible-analytics\/tracker$/,
1683
+ /^@nuxtjs\/plausible$/,
1684
+ /^gatsby-plugin-plausible$/,
1685
+ /^vue-plausible$/
1686
+ ],
1687
+ env: [/PLAUSIBLE/]
1688
+ },
1689
+ why: "Pageviews and visitors on your dashboard."
1690
+ },
1664
1691
  sentry: {
1665
1692
  label: "Sentry",
1666
1693
  rule: {
@@ -2119,7 +2146,7 @@ function detectStack(facts) {
2119
2146
  evidence: [],
2120
2147
  why: "Know the moment the site goes down. Nothing to install."
2121
2148
  });
2122
- if (isWebsite && !has("posthog") && !has("ga4")) {
2149
+ if (isWebsite && !has("posthog") && !has("ga4") && !has("plausible")) {
2123
2150
  if (gaps.push("No product analytics found."), !listed("posthog"))
2124
2151
  sources.push({
2125
2152
  source: "posthog",
@@ -2156,27 +2183,27 @@ async function startDeviceLogin(url, opts = {}) {
2156
2183
  });
2157
2184
  if (!res.ok)
2158
2185
  return null;
2159
- let b3 = await res.json();
2160
- if (typeof b3.device_code !== "string" || typeof b3.user_code !== "string")
2186
+ let b = await res.json();
2187
+ if (typeof b.device_code !== "string" || typeof b.user_code !== "string")
2161
2188
  return null;
2162
2189
  return {
2163
- deviceCode: b3.device_code,
2164
- userCode: b3.user_code,
2165
- verifyUrl: String(b3.verification_uri_complete ?? b3.verification_uri),
2166
- interval: Number(b3.interval) || 5,
2167
- expiresIn: Number(b3.expires_in) || 600
2190
+ deviceCode: b.device_code,
2191
+ userCode: b.user_code,
2192
+ verifyUrl: String(b.verification_uri_complete ?? b.verification_uri),
2193
+ interval: Number(b.interval) || 5,
2194
+ expiresIn: Number(b.expires_in) || 600
2168
2195
  };
2169
2196
  } catch {
2170
2197
  return null;
2171
2198
  }
2172
2199
  }
2173
2200
  async function pollDeviceLogin(url, start, opts = {}) {
2174
- let f = opts.fetchImpl ?? fetch, sleep = opts.sleep ?? ((ms) => new Promise((r2) => setTimeout(r2, ms))), interval = start.interval, deadline = Date.now() + start.expiresIn * 1000;
2201
+ let f = opts.fetchImpl ?? fetch, sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms))), interval = start.interval, deadline = Date.now() + start.expiresIn * 1000;
2175
2202
  while (Date.now() < deadline) {
2176
2203
  await sleep(interval * 1000);
2177
- let body2;
2204
+ let body;
2178
2205
  try {
2179
- body2 = await (await f(`${url}/api/oauth/token`, {
2206
+ body = await (await f(`${url}/api/oauth/token`, {
2180
2207
  method: "POST",
2181
2208
  headers: { "content-type": "application/x-www-form-urlencoded" },
2182
2209
  body: new URLSearchParams({
@@ -2187,14 +2214,14 @@ async function pollDeviceLogin(url, start, opts = {}) {
2187
2214
  } catch {
2188
2215
  continue;
2189
2216
  }
2190
- if (typeof body2.access_token === "string")
2217
+ if (typeof body.access_token === "string")
2191
2218
  return {
2192
2219
  ok: !0,
2193
- token: body2.access_token,
2194
- url: typeof body2.workspace_url === "string" ? body2.workspace_url : url,
2195
- scope: String(body2.scope ?? "")
2220
+ token: body.access_token,
2221
+ url: typeof body.workspace_url === "string" ? body.workspace_url : url,
2222
+ scope: String(body.scope ?? "")
2196
2223
  };
2197
- switch (body2.error) {
2224
+ switch (body.error) {
2198
2225
  case "authorization_pending":
2199
2226
  continue;
2200
2227
  case "slow_down":
@@ -2216,7 +2243,7 @@ async function pollDeviceLogin(url, start, opts = {}) {
2216
2243
  return {
2217
2244
  ok: !1,
2218
2245
  reason: "error",
2219
- message: String(body2.error_description ?? body2.error ?? "Sign-in failed.")
2246
+ message: String(body.error_description ?? body.error ?? "Sign-in failed.")
2220
2247
  };
2221
2248
  }
2222
2249
  }
@@ -2285,7 +2312,7 @@ async function runMcpProxy(auth, fetchImpl = fetch) {
2285
2312
  }
2286
2313
  process.stdout.write(`${text}
2287
2314
  `);
2288
- } catch (e2) {
2315
+ } catch (e) {
2289
2316
  if (id === null)
2290
2317
  return;
2291
2318
  write({
@@ -2293,7 +2320,7 @@ async function runMcpProxy(auth, fetchImpl = fetch) {
2293
2320
  id,
2294
2321
  error: {
2295
2322
  code: -32000,
2296
- message: `Could not reach LopeBase at ${endpoint}: ${String(e2)}`
2323
+ message: `Could not reach LopeBase at ${endpoint}: ${String(e)}`
2297
2324
  }
2298
2325
  });
2299
2326
  }
@@ -2381,15 +2408,15 @@ var MONOREPO_DIRS = ["apps", "packages", "services", "sites", "web", "site"], SK
2381
2408
  ".github/workflows",
2382
2409
  "content/config.ts"
2383
2410
  ], EXAMPLE_ENV = /^(\.env\.(example|sample|template|dist|local\.example|development\.example|production\.example)|example\.env|\.dev\.vars\.example)$/;
2384
- function readText(file2) {
2411
+ function readText(file) {
2385
2412
  try {
2386
- return readFileSync2(file2, "utf8");
2413
+ return readFileSync2(file, "utf8");
2387
2414
  } catch {
2388
2415
  return null;
2389
2416
  }
2390
2417
  }
2391
- function readJson(file2) {
2392
- let text = readText(file2);
2418
+ function readJson(file) {
2419
+ let text = readText(file);
2393
2420
  if (text === null)
2394
2421
  return null;
2395
2422
  try {
@@ -2398,9 +2425,9 @@ function readJson(file2) {
2398
2425
  return null;
2399
2426
  }
2400
2427
  }
2401
- function isDir(p2) {
2428
+ function isDir(p) {
2402
2429
  try {
2403
- return statSync(p2).isDirectory();
2430
+ return statSync(p).isDirectory();
2404
2431
  } catch {
2405
2432
  return !1;
2406
2433
  }
@@ -2425,14 +2452,14 @@ function isPackage(dir) {
2425
2452
  }
2426
2453
  function workspaceGlobs(root) {
2427
2454
  let globs = [], ws = readJson(path2.join(root, "package.json"))?.workspaces, list = Array.isArray(ws) ? ws : ws && typeof ws === "object" && Array.isArray(ws.packages) ? ws.packages : [];
2428
- for (let g2 of list)
2429
- if (typeof g2 === "string")
2430
- globs.push(g2);
2455
+ for (let g of list)
2456
+ if (typeof g === "string")
2457
+ globs.push(g);
2431
2458
  let pnpm = readText(path2.join(root, "pnpm-workspace.yaml"));
2432
2459
  if (pnpm)
2433
- for (let m2 of pnpm.matchAll(/^\s*-\s*["']?([^"'\s#]+)["']?/gm))
2434
- globs.push(m2[1]);
2435
- return globs.filter((g2) => !g2.startsWith("!"));
2460
+ for (let m of pnpm.matchAll(/^\s*-\s*["']?([^"'\s#]+)["']?/gm))
2461
+ globs.push(m[1]);
2462
+ return globs.filter((g) => !g.startsWith("!"));
2436
2463
  }
2437
2464
  function expandGlob(root, glob) {
2438
2465
  let clean = glob.replace(/\/\*\*$/, "/*").replace(/\/$/, "");
@@ -2441,24 +2468,24 @@ function expandGlob(root, glob) {
2441
2468
  return isDir(dir) ? [dir] : [];
2442
2469
  }
2443
2470
  let [base] = clean.split("*"), parent = path2.join(root, base);
2444
- return listDir(parent).filter((e2) => !SKIP_DIRS.has(e2) && !e2.startsWith(".")).map((e2) => path2.join(parent, e2)).filter(isDir);
2471
+ return listDir(parent).filter((e) => !SKIP_DIRS.has(e) && !e.startsWith(".")).map((e) => path2.join(parent, e)).filter(isDir);
2445
2472
  }
2446
2473
  function packageDirs(root) {
2447
2474
  let dirs = /* @__PURE__ */ new Set([root]);
2448
- for (let g2 of workspaceGlobs(root))
2449
- for (let d3 of expandGlob(root, g2))
2450
- if (isPackage(d3))
2451
- dirs.add(d3);
2475
+ for (let g of workspaceGlobs(root))
2476
+ for (let d of expandGlob(root, g))
2477
+ if (isPackage(d))
2478
+ dirs.add(d);
2452
2479
  for (let group of MONOREPO_DIRS) {
2453
- let g2 = path2.join(root, group);
2454
- if (!isDir(g2))
2480
+ let g = path2.join(root, group);
2481
+ if (!isDir(g))
2455
2482
  continue;
2456
- if (isPackage(g2))
2457
- dirs.add(g2);
2458
- for (let entry of listDir(g2)) {
2459
- let d3 = path2.join(g2, entry);
2460
- if (!SKIP_DIRS.has(entry) && isDir(d3) && isPackage(d3))
2461
- dirs.add(d3);
2483
+ if (isPackage(g))
2484
+ dirs.add(g);
2485
+ for (let entry of listDir(g)) {
2486
+ let d = path2.join(g, entry);
2487
+ if (!SKIP_DIRS.has(entry) && isDir(d) && isPackage(d))
2488
+ dirs.add(d);
2462
2489
  }
2463
2490
  }
2464
2491
  return [...dirs].slice(0, MAX_PACKAGES);
@@ -2466,36 +2493,36 @@ function packageDirs(root) {
2466
2493
  function pythonRequirements(text) {
2467
2494
  let out = [];
2468
2495
  for (let line of text.split(/\r?\n/)) {
2469
- let m2 = /^\s*([A-Za-z0-9][A-Za-z0-9._-]*)/.exec(line.replace(/#.*/, ""));
2470
- if (m2 && !line.trim().startsWith("-"))
2471
- out.push(m2[1].toLowerCase());
2496
+ let m = /^\s*([A-Za-z0-9][A-Za-z0-9._-]*)/.exec(line.replace(/#.*/, ""));
2497
+ if (m && !line.trim().startsWith("-"))
2498
+ out.push(m[1].toLowerCase());
2472
2499
  }
2473
2500
  return out;
2474
2501
  }
2475
2502
  function pythonProjectDeps(text) {
2476
2503
  let out = /* @__PURE__ */ new Set;
2477
2504
  for (let block of text.matchAll(/(?:^|\n)\s*dependencies\s*=\s*\[([\s\S]*?)\]/g))
2478
- for (let m2 of block[1].matchAll(/["']([A-Za-z0-9][A-Za-z0-9._-]*)/g))
2479
- out.add(m2[1].toLowerCase());
2505
+ for (let m of block[1].matchAll(/["']([A-Za-z0-9][A-Za-z0-9._-]*)/g))
2506
+ out.add(m[1].toLowerCase());
2480
2507
  for (let table of text.matchAll(/\[(?:tool\.poetry\.(?:group\.[a-z]+\.)?dependencies|(?:dev-)?packages)\]([\s\S]*?)(?=\n\[|$)/g))
2481
- for (let m2 of table[1].matchAll(/^\s*([A-Za-z0-9][A-Za-z0-9._-]*)\s*=/gm))
2482
- out.add(m2[1].toLowerCase());
2508
+ for (let m of table[1].matchAll(/^\s*([A-Za-z0-9][A-Za-z0-9._-]*)\s*=/gm))
2509
+ out.add(m[1].toLowerCase());
2483
2510
  return out.delete("python"), [...out];
2484
2511
  }
2485
2512
  function gemfileDeps(text) {
2486
- return [...text.matchAll(/^\s*gem\s+["']([^"']+)["']/gm)].map((m2) => m2[1]);
2513
+ return [...text.matchAll(/^\s*gem\s+["']([^"']+)["']/gm)].map((m) => m[1]);
2487
2514
  }
2488
2515
  function goModDeps(text) {
2489
2516
  let out = [];
2490
- for (let m2 of text.matchAll(/^\s*(?:require\s+)?([a-z0-9.-]+\.[a-z]+\/[^\s]+)\s+v[0-9]/gm))
2491
- out.push(m2[1]);
2517
+ for (let m of text.matchAll(/^\s*(?:require\s+)?([a-z0-9.-]+\.[a-z]+\/[^\s]+)\s+v[0-9]/gm))
2518
+ out.push(m[1]);
2492
2519
  return out;
2493
2520
  }
2494
2521
  function cargoDeps(text) {
2495
2522
  let out = [];
2496
2523
  for (let table of text.matchAll(/\[(?:dev-|build-)?dependencies\]([\s\S]*?)(?=\n\[|$)/g))
2497
- for (let m2 of table[1].matchAll(/^\s*([A-Za-z0-9_-]+)\s*=/gm))
2498
- out.push(m2[1]);
2524
+ for (let m of table[1].matchAll(/^\s*([A-Za-z0-9_-]+)\s*=/gm))
2525
+ out.push(m[1]);
2499
2526
  return out;
2500
2527
  }
2501
2528
  function manifestDeps(dir) {
@@ -2514,10 +2541,10 @@ function manifestDeps(dir) {
2514
2541
  if (gem)
2515
2542
  add("gem", gemfileDeps(gem));
2516
2543
  let composer = readJson(path2.join(dir, "composer.json"));
2517
- for (let k3 of ["require", "require-dev"]) {
2518
- let d3 = composer?.[k3];
2519
- if (d3 && typeof d3 === "object")
2520
- add("composer", Object.keys(d3));
2544
+ for (let k of ["require", "require-dev"]) {
2545
+ let d = composer?.[k];
2546
+ if (d && typeof d === "object")
2547
+ add("composer", Object.keys(d));
2521
2548
  }
2522
2549
  let gomod = readText(path2.join(dir, "go.mod"));
2523
2550
  if (gomod)
@@ -2531,7 +2558,7 @@ function siteFromWrangler(text) {
2531
2558
  let first = [
2532
2559
  ...text.matchAll(/(?:pattern|route)["']?\s*[:=]\s*["']([^"'*/\s]+)/g),
2533
2560
  ...text.matchAll(/custom_domain["']?\s*[:=]\s*["']([^"'*/\s]+)/g)
2534
- ].map((m2) => m2[1].toLowerCase()).filter((h2) => h2.includes(".") && !h2.endsWith("workers.dev"))[0];
2561
+ ].map((m) => m[1].toLowerCase()).filter((h) => h.includes(".") && !h.endsWith("workers.dev"))[0];
2535
2562
  if (!first)
2536
2563
  return;
2537
2564
  return `https://${first.replace(/^(app|www|api|console)\./, "")}`;
@@ -2539,26 +2566,26 @@ function siteFromWrangler(text) {
2539
2566
  var NOT_A_SITE = /(^|\.)(localhost|example\.(com|org)|your-?(site|domain)\.[a-z]+|vercel\.app|netlify\.app|pages\.dev|workers\.dev|github\.com|gitlab\.com|npmjs\.com)$/;
2540
2567
  function publicSite(url) {
2541
2568
  try {
2542
- let u2 = new URL(url.startsWith("http") ? url : `https://${url}`);
2543
- if (!u2.hostname.includes(".") || NOT_A_SITE.test(u2.hostname))
2569
+ let u = new URL(url.startsWith("http") ? url : `https://${url}`);
2570
+ if (!u.hostname.includes(".") || NOT_A_SITE.test(u.hostname))
2544
2571
  return;
2545
- return `https://${u2.hostname.replace(/^www\./, "")}`;
2572
+ return `https://${u.hostname.replace(/^www\./, "")}`;
2546
2573
  } catch {
2547
2574
  return;
2548
2575
  }
2549
2576
  }
2550
2577
  function siteFromConfig(text) {
2551
- let m2 = /\b(?:site|siteUrl|baseURL|baseUrl|url)\s*[:=]\s*["'`](https?:\/\/[^"'`\s]+)["'`]/.exec(text);
2552
- return m2 ? publicSite(m2[1]) : void 0;
2578
+ let m = /\b(?:site|siteUrl|baseURL|baseUrl|url)\s*[:=]\s*["'`](https?:\/\/[^"'`\s]+)["'`]/.exec(text);
2579
+ return m ? publicSite(m[1]) : void 0;
2553
2580
  }
2554
2581
  function githubRepo(gitConfig) {
2555
2582
  let remotes = [
2556
2583
  ...gitConfig.matchAll(/\[remote "([^"]+)"\][^[]*?url\s*=\s*(\S+)/g)
2557
- ].sort((a, b3) => Number(b3[1] === "origin") - Number(a[1] === "origin"));
2584
+ ].sort((a, b) => Number(b[1] === "origin") - Number(a[1] === "origin"));
2558
2585
  for (let [, , url] of remotes) {
2559
- let m2 = /github\.com[:/]([\w.-]+)\/([\w.-]+?)(?:\.git)?\/?$/.exec(url);
2560
- if (m2)
2561
- return `${m2[1]}/${m2[2]}`;
2586
+ let m = /github\.com[:/]([\w.-]+)\/([\w.-]+?)(?:\.git)?\/?$/.exec(url);
2587
+ if (m)
2588
+ return `${m[1]}/${m[2]}`;
2562
2589
  }
2563
2590
  return;
2564
2591
  }
@@ -2573,21 +2600,21 @@ function readGitConfig(root) {
2573
2600
  return readText(path2.join(gitDir, "config")) ?? readText(path2.join(gitDir, "..", "..", "config"));
2574
2601
  }
2575
2602
  function siteFromHtml(html) {
2576
- let m2 = /<link[^>]+rel=["']canonical["'][^>]+href=["'](https?:\/\/[^"']+)/i.exec(html) ?? /<link[^>]+href=["'](https?:\/\/[^"']+)["'][^>]+rel=["']canonical["']/i.exec(html) ?? /<meta[^>]+property=["']og:url["'][^>]+content=["'](https?:\/\/[^"']+)/i.exec(html);
2577
- return m2 ? publicSite(m2[1]) : void 0;
2603
+ let m = /<link[^>]+rel=["']canonical["'][^>]+href=["'](https?:\/\/[^"']+)/i.exec(html) ?? /<link[^>]+href=["'](https?:\/\/[^"']+)["'][^>]+rel=["']canonical["']/i.exec(html) ?? /<meta[^>]+property=["']og:url["'][^>]+content=["'](https?:\/\/[^"']+)/i.exec(html);
2604
+ return m ? publicSite(m[1]) : void 0;
2578
2605
  }
2579
2606
  function envKeyNames(text) {
2580
2607
  let out = [];
2581
2608
  for (let line of text.split(/\r?\n/)) {
2582
- let m2 = /^\s*(?:#\s*)?(?:export\s+)?([A-Z][A-Z0-9_]*)\s*=/.exec(line);
2583
- if (m2)
2584
- out.push(m2[1]);
2609
+ let m = /^\s*(?:#\s*)?(?:export\s+)?([A-Z][A-Z0-9_]*)\s*=/.exec(line);
2610
+ if (m)
2611
+ out.push(m[1]);
2585
2612
  }
2586
2613
  return out;
2587
2614
  }
2588
2615
  var SITE_CONFIGS = /^(astro\.config\.\w+|next-sitemap\.config\.\w+|gatsby-config\.\w+|docusaurus\.config\.\w+|hugo\.toml|config\.toml|_config\.ya?ml|nuxt\.config\.\w+)$/;
2589
2616
  function scanProject(root, onProgress) {
2590
- let deps = /* @__PURE__ */ new Set, files = /* @__PURE__ */ new Set, envKeys = /* @__PURE__ */ new Set, name, siteUrl, dirs = packageDirs(root), relDir = (d3) => path2.relative(root, d3).split(path2.sep).join("/") || ".";
2617
+ let deps = /* @__PURE__ */ new Set, files = /* @__PURE__ */ new Set, envKeys = /* @__PURE__ */ new Set, name, siteUrl, dirs = packageDirs(root), relDir = (d) => path2.relative(root, d).split(path2.sep).join("/") || ".";
2591
2618
  onProgress?.({
2592
2619
  label: `Found ${dirs.length} ${dirs.length === 1 ? "package" : "packages"}`,
2593
2620
  detail: dirs.length > 1 ? dirs.map(relDir).join(", ") : void 0
@@ -2600,22 +2627,22 @@ function scanProject(root, onProgress) {
2600
2627
  name = pkg.name;
2601
2628
  if (!siteUrl && typeof pkg.homepage === "string")
2602
2629
  siteUrl = publicSite(pkg.homepage);
2603
- for (let k3 of ["dependencies", "devDependencies"]) {
2604
- let d3 = pkg[k3];
2605
- if (d3 && typeof d3 === "object")
2606
- for (let n of Object.keys(d3))
2630
+ for (let k of ["dependencies", "devDependencies"]) {
2631
+ let d = pkg[k];
2632
+ if (d && typeof d === "object")
2633
+ for (let n of Object.keys(d))
2607
2634
  deps.add(n);
2608
2635
  }
2609
2636
  }
2610
- for (let d3 of manifestDeps(dir))
2611
- deps.add(d3);
2637
+ for (let d of manifestDeps(dir))
2638
+ deps.add(d);
2612
2639
  for (let entry of listDir(dir)) {
2613
2640
  if (EXAMPLE_ENV.test(entry)) {
2614
2641
  let text = readText(path2.join(dir, entry));
2615
2642
  if (text) {
2616
2643
  envFiles++;
2617
- for (let k3 of envKeyNames(text))
2618
- envKeys.add(k3);
2644
+ for (let k of envKeyNames(text))
2645
+ envKeys.add(k);
2619
2646
  }
2620
2647
  }
2621
2648
  if (!MARKERS.some((re) => re.test(entry)))
@@ -2681,7 +2708,7 @@ var import_picocolors3 = __toESM(require_picocolors(), 1), MARK = [
2681
2708
  function motionAllowed(argv = process.argv, env = process.env, isTTY = Boolean(process.stdout.isTTY)) {
2682
2709
  return isTTY && !env.CI && !env.LOPEBASE_NO_MOTION && env.TERM !== "dumb" && !argv.includes("--no-motion");
2683
2710
  }
2684
- var sleep = (ms) => new Promise((r2) => setTimeout(r2, ms));
2711
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
2685
2712
  function logoLines(opts) {
2686
2713
  let right = {
2687
2714
  3: `${import_picocolors3.default.bold("Lope")}${import_picocolors3.default.dim("Base")}${opts.version ? import_picocolors3.default.dim(` v${opts.version}`) : ""}`,
@@ -2702,7 +2729,7 @@ ${lines.join(`
2702
2729
  `);
2703
2730
  return;
2704
2731
  }
2705
- let blank = MARK.map((r2) => " ".repeat(r2.length)), frame = (shown) => MARK.map((row, i) => shown.has(i) ? HORIZON.has(i) ? import_picocolors3.default.yellow(import_picocolors3.default.dim(row)) : import_picocolors3.default.yellow(row) : blank[i]), up = `\x1B[${MARK.length}A`, onInterrupt = () => {
2732
+ let blank = MARK.map((r) => " ".repeat(r.length)), frame = (shown) => MARK.map((row, i) => shown.has(i) ? HORIZON.has(i) ? import_picocolors3.default.yellow(import_picocolors3.default.dim(row)) : import_picocolors3.default.yellow(row) : blank[i]), up = `\x1B[${MARK.length}A`, onInterrupt = () => {
2706
2733
  write(`\x1B[?25h
2707
2734
  `), process.exit(130);
2708
2735
  };
@@ -2756,8 +2783,8 @@ function stackRows(detection, facts) {
2756
2783
  rows.push(["Repo", facts.repo]);
2757
2784
  if (!rows.length)
2758
2785
  return [import_picocolors3.default.dim("Nothing recognized yet. Your agent will look closer.")];
2759
- let width = Math.max(...rows.map(([k3]) => k3.length));
2760
- return rows.map(([k3, v2]) => `${import_picocolors3.default.dim(k3.padEnd(width))} ${v2}`);
2786
+ let width = Math.max(...rows.map(([k]) => k.length));
2787
+ return rows.map(([k, v]) => `${import_picocolors3.default.dim(k.padEnd(width))} ${v}`);
2761
2788
  }
2762
2789
  var PLAN = [
2763
2790
  {
@@ -2800,6 +2827,7 @@ ${import_picocolors4.default.bold("Usage")}
2800
2827
 
2801
2828
  ${import_picocolors4.default.bold("Options")}
2802
2829
  --print print the agent prompt instead of starting an agent
2830
+ --url <address> sign in to this workspace or server (also LOPEBASE_URL)
2803
2831
  --no-motion no animation (also LOPEBASE_NO_MOTION=1)
2804
2832
 
2805
2833
  ${import_picocolors4.default.bold("Learn more")}
@@ -2848,45 +2876,55 @@ async function checkToken(auth) {
2848
2876
  });
2849
2877
  if (res.status === 401)
2850
2878
  return { ok: !1, message: "That token is not valid or has expired." };
2851
- let body2 = await res.json();
2852
- return { ok: !0, products: JSON.parse(body2.result?.content?.[0]?.text ?? "[]").length };
2853
- } catch (e2) {
2879
+ let body = await res.json();
2880
+ return { ok: !0, products: JSON.parse(body.result?.content?.[0]?.text ?? "[]").length };
2881
+ } catch (e) {
2854
2882
  return {
2855
2883
  ok: !1,
2856
- message: `Could not reach ${auth.url} (${String(e2)}).`
2884
+ message: `Could not reach ${auth.url} (${String(e)}).`
2857
2885
  };
2858
2886
  }
2859
2887
  }
2860
2888
  async function login() {
2861
- let known = option("url") ?? loadAuth()?.url, url = workspaceUrl(guard(await he({
2862
- message: "Your LopeBase workspace (name or address)",
2863
- placeholder: "yourteam or https://yourteam.lopebase.com",
2864
- initialValue: known ?? "",
2865
- validate: (v2) => workspaceUrl(v2 ?? "") ? void 0 : "Type your workspace name, like yourteam, or its full address"
2866
- })));
2889
+ let start = signInUrl({
2890
+ flag: option("url"),
2891
+ env: process.env.LOPEBASE_URL,
2892
+ saved: loadAuth()?.url
2893
+ });
2894
+ if ("error" in start)
2895
+ bail(start.error);
2896
+ let url = start.url;
2867
2897
  if (!flag("paste")) {
2868
2898
  let device = await startDeviceLogin(url);
2869
2899
  if (device) {
2870
2900
  Me([
2871
2901
  `Your code: ${import_picocolors4.default.bold(import_picocolors4.default.yellow(device.userCode))}`,
2872
2902
  "",
2873
- "We opened your workspace in the browser. Sign in if asked,",
2874
- "check the code matches, and press Connect."
2903
+ "We opened LopeBase in your browser. Sign in, or create a free",
2904
+ "account if you are new. Then check the code matches and press",
2905
+ "Connect."
2875
2906
  ].join(`
2876
2907
  `), "Connect this computer"), openBrowser(device.verifyUrl), M2.info(import_picocolors4.default.dim(device.verifyUrl));
2877
2908
  let s = Y2();
2878
2909
  s.start("Waiting for you to approve in the browser");
2879
2910
  let res = await pollDeviceLogin(url, device);
2880
2911
  if (res.ok) {
2881
- let file2 = saveAuth({ url: res.url, token: res.token });
2882
- return s.stop(`Connected (${res.scope}). Saved to ${import_picocolors4.default.dim(file2)}`), { url: res.url, token: res.token };
2912
+ let file = saveAuth({ url: res.url, token: res.token });
2913
+ return s.stop(`Connected to ${import_picocolors4.default.bold(res.url)}. Saved to ${import_picocolors4.default.dim(file)}`), { url: res.url, token: res.token };
2883
2914
  }
2884
2915
  if (s.stop(import_picocolors4.default.red(res.message)), res.reason === "denied")
2885
2916
  bail("Nothing was connected.");
2886
2917
  M2.warn("Falling back to pasting a token.");
2887
2918
  }
2888
2919
  }
2889
- return pasteLogin(url);
2920
+ return pasteLogin(url === DEFAULT_URL ? await askWorkspace() : url);
2921
+ }
2922
+ async function askWorkspace() {
2923
+ return workspaceUrl(guard(await he({
2924
+ message: "Your LopeBase workspace (name or address)",
2925
+ placeholder: "yourteam or https://yourteam.lopebase.com",
2926
+ validate: (v) => workspaceUrl(v ?? "") ? void 0 : "Type your workspace name, like yourteam, or its full address"
2927
+ })));
2890
2928
  }
2891
2929
  async function pasteLogin(url) {
2892
2930
  let settings = `${url}/settings#agent-access`;
@@ -2899,29 +2937,29 @@ async function pasteLogin(url) {
2899
2937
  for (;; ) {
2900
2938
  let token = guard(await ge({
2901
2939
  message: "Paste the agent token",
2902
- validate: (v2) => v2?.startsWith("lb_agent_") ? void 0 : "Tokens start with lb_agent_"
2940
+ validate: (v) => v?.startsWith("lb_agent_") ? void 0 : "Tokens start with lb_agent_"
2903
2941
  })).trim(), s = Y2();
2904
2942
  s.start("Checking the token");
2905
2943
  let check = await checkToken({ url, token });
2906
2944
  if (check.ok) {
2907
- let file2 = saveAuth({ url, token });
2908
- return s.stop(`Signed in. ${check.products} product(s) in this workspace. Saved to ${import_picocolors4.default.dim(file2)}`), { url, token };
2945
+ let file = saveAuth({ url, token });
2946
+ return s.stop(`Signed in. ${check.products} product(s) in this workspace. Saved to ${import_picocolors4.default.dim(file)}`), { url, token };
2909
2947
  }
2910
2948
  s.stop(import_picocolors4.default.red(check.message ?? "That did not work."));
2911
2949
  }
2912
2950
  }
2913
- async function printDetection(d3, facts) {
2914
- Me(stackRows(d3, facts).join(`
2951
+ async function printDetection(d, facts) {
2952
+ Me(stackRows(d, facts).join(`
2915
2953
  `), "Your stack"), await pace(motion, 150);
2916
- let found = d3.sources.filter((s) => s.status === "found");
2954
+ let found = d.sources.filter((s) => s.status === "found");
2917
2955
  if (found.length)
2918
2956
  Me(found.map((s) => `${import_picocolors4.default.green("●")} ${import_picocolors4.default.bold(s.label)} ${import_picocolors4.default.dim(s.evidence.join(", "))}`).join(`
2919
2957
  `), "Already in your code, ready to connect"), await pace(motion, 150);
2920
- let possible = d3.sources.filter((s) => s.status === "possible");
2958
+ let possible = d.sources.filter((s) => s.status === "possible");
2921
2959
  if (possible.length)
2922
2960
  Me(possible.map((s) => `${import_picocolors4.default.yellow("?")} ${import_picocolors4.default.bold(s.label)} ${import_picocolors4.default.dim(`only ${s.evidence.join(", ")}`)}`).join(`
2923
2961
  `), "Maybe in use, your agent will check"), await pace(motion, 150);
2924
- for (let gap of d3.gaps)
2962
+ for (let gap of d.gaps)
2925
2963
  M2.info(import_picocolors4.default.dim(gap));
2926
2964
  }
2927
2965
  async function scanWithProgress() {
@@ -2963,13 +3001,13 @@ async function init() {
2963
3001
  await printDetection(detection, facts), M2.step(stepTitle(2));
2964
3002
  let auth = loadAuth();
2965
3003
  if (auth) {
2966
- let s2 = Y2();
2967
- s2.start(`Checking your connection to ${auth.url}`);
3004
+ let s = Y2();
3005
+ s.start(`Checking your connection to ${auth.url}`);
2968
3006
  let check = await checkToken(auth);
2969
3007
  if (check.ok)
2970
- s2.stop(`Connected to ${import_picocolors4.default.bold(auth.url)}. ${check.products} ${check.products === 1 ? "product" : "products"} so far.`);
3008
+ s.stop(`Connected to ${import_picocolors4.default.bold(auth.url)}. ${check.products} ${check.products === 1 ? "product" : "products"} so far.`);
2971
3009
  else
2972
- s2.stop(import_picocolors4.default.yellow("Your saved connection no longer works.")), auth = null;
3010
+ s.stop(import_picocolors4.default.yellow("Your saved connection no longer works.")), auth = null;
2973
3011
  }
2974
3012
  auth ??= await login(), M2.step(stepTitle(3));
2975
3013
  let productName = guard(await he({
@@ -3024,7 +3062,7 @@ async function init() {
3024
3062
  stdio: "inherit",
3025
3063
  cwd
3026
3064
  });
3027
- child.on("error", (e2) => bail(`Could not start ${agent.label}: ${e2.message}`)), child.on("exit", (code) => process.exit(code ?? 0));
3065
+ child.on("error", (e) => bail(`Could not start ${agent.label}: ${e.message}`)), child.on("exit", (code) => process.exit(code ?? 0));
3028
3066
  }
3029
3067
  async function main() {
3030
3068
  switch (command) {
@@ -3056,7 +3094,7 @@ async function main() {
3056
3094
  baseUrl: rest[1],
3057
3095
  secret,
3058
3096
  token: secret ? void 0 : token,
3059
- log: (l2) => console.log(l2.startsWith("FAIL") ? import_picocolors4.default.red(l2) : import_picocolors4.default.green(l2))
3097
+ log: (l) => console.log(l.startsWith("FAIL") ? import_picocolors4.default.red(l) : import_picocolors4.default.green(l))
3060
3098
  });
3061
3099
  console.log(res.failed.length ? import_picocolors4.default.red(`${res.failed.length} failed, ${res.passed} passed`) : import_picocolors4.default.green(`All ${res.passed} checks passed. Ready to connect.`)), process.exit(res.failed.length ? 1 : 0);
3062
3100
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lopebase",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "Set up LopeBase from your project's folder: scan your stack, connect your workspace, and let your coding agent do the rest.",
5
5
  "type": "module",
6
6
  "bin": {