lopebase 0.6.5 → 0.6.6
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/README.md +29 -0
- package/dist/lopebase.js +566 -281
- package/package.json +2 -1
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
|
|
17
|
+
let to = isNodeMode || !mod || !mod.__esModule ? __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
|
|
161
|
-
if (
|
|
160
|
+
var p2 = pathext[i].toLowerCase();
|
|
161
|
+
if (p2 && path.substr(-p2.length).toLowerCase() === p2)
|
|
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(),
|
|
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;
|
|
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),
|
|
272
|
-
resolve(subStep(
|
|
273
|
-
}), subStep = (
|
|
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) => {
|
|
274
274
|
if (ii === pathExt.length)
|
|
275
275
|
return resolve(step(i + 1));
|
|
276
276
|
let ext = pathExt[ii];
|
|
277
|
-
isexe(
|
|
277
|
+
isexe(p2 + ext, { pathExt: pathExtExe }, (er, is) => {
|
|
278
278
|
if (!er && is)
|
|
279
279
|
if (opt.all)
|
|
280
|
-
found.push(
|
|
280
|
+
found.push(p2 + ext);
|
|
281
281
|
else
|
|
282
|
-
return resolve(
|
|
283
|
-
return resolve(subStep(
|
|
282
|
+
return resolve(p2 + ext);
|
|
283
|
+
return resolve(subStep(p2, 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),
|
|
292
|
-
for (let
|
|
293
|
-
let cur =
|
|
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];
|
|
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 (
|
|
340
|
+
} catch (e2) {} 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 (
|
|
395
|
+
} catch (e2) {}
|
|
396
396
|
return shebangCommand(buffer.toString());
|
|
397
397
|
}
|
|
398
398
|
module.exports = readShebang;
|
|
@@ -502,7 +502,7 @@ var require_cross_spawn = __commonJS(function(exports, module) {
|
|
|
502
502
|
|
|
503
503
|
// bin/lopebase.ts
|
|
504
504
|
import { spawn as spawn2 } from "node:child_process";
|
|
505
|
-
import { readFileSync as
|
|
505
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
506
506
|
|
|
507
507
|
// ../../node_modules/.bun/@clack+prompts@0.11.0/node_modules/@clack/prompts/dist/index.mjs
|
|
508
508
|
import { stripVTControlCharacters as S2 } from "node:util";
|
|
@@ -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:
|
|
972
|
-
n >=
|
|
973
|
-
let $ = a <
|
|
974
|
-
return
|
|
975
|
-
let
|
|
976
|
-
return
|
|
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);
|
|
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
|
-
`,
|
|
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;
|
|
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
|
-
`,
|
|
1001
|
+
`, r2 = 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)} ${
|
|
1014
|
+
return `${n}${import_picocolors2.default.cyan(o)} ${r2}
|
|
1015
1015
|
${import_picocolors2.default.cyan(d2)}
|
|
1016
1016
|
`;
|
|
1017
1017
|
}
|
|
1018
1018
|
} }).prompt(), ye = (t) => {
|
|
1019
|
-
let n = t.active ?? "Yes",
|
|
1020
|
-
return new dD({ active: n, inactive:
|
|
1019
|
+
let n = t.active ?? "Yes", r2 = t.inactive ?? "No";
|
|
1020
|
+
return new dD({ active: n, inactive: r2, 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 :
|
|
1023
|
+
`, s = this.value ? n : r2;
|
|
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(
|
|
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}`}
|
|
1032
1032
|
${import_picocolors2.default.cyan(d2)}
|
|
1033
1033
|
`;
|
|
1034
1034
|
}
|
|
1035
1035
|
} }).prompt();
|
|
1036
1036
|
}, ve = (t) => {
|
|
1037
|
-
let n = (
|
|
1038
|
-
let s =
|
|
1037
|
+
let n = (r2, i) => {
|
|
1038
|
+
let s = r2.label ?? String(r2.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} ${
|
|
1043
|
+
return `${import_picocolors2.default.green(k2)} ${s} ${r2.hint ? import_picocolors2.default.dim(`(${r2.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
|
|
1051
|
+
let r2 = `${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 `${
|
|
1056
|
+
return `${r2}${import_picocolors2.default.gray(o)} ${n(this.options[this.cursor], "selected")}`;
|
|
1057
1057
|
case "cancel":
|
|
1058
|
-
return `${
|
|
1058
|
+
return `${r2}${import_picocolors2.default.gray(o)} ${n(this.options[this.cursor], "cancelled")}
|
|
1059
1059
|
${import_picocolors2.default.gray(o)}`;
|
|
1060
1060
|
default:
|
|
1061
|
-
return `${
|
|
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(`
|
|
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 = (
|
|
1070
|
-
let s =
|
|
1071
|
-
return i === "active" ? `${import_picocolors2.default.cyan(A2)} ${s} ${
|
|
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)}`;
|
|
1072
1072
|
};
|
|
1073
|
-
return new SD({ options: t.options, initialValues: t.initialValues, required: t.required ?? !0, cursorAt: t.cursorAt, validate(
|
|
1074
|
-
if (this.required &&
|
|
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)
|
|
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
|
|
1078
|
+
let r2 = `${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 `${
|
|
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")}`;
|
|
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 `${
|
|
1089
|
+
return `${r2}${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 `${
|
|
1096
|
+
return `${r2 + 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 `${
|
|
1102
|
+
return `${r2}${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
|
|
1110
|
+
let r2 = `
|
|
1111
1111
|
${t}
|
|
1112
1112
|
`.split(`
|
|
1113
|
-
`), i = S2(n).length, s = Math.max(
|
|
1114
|
-
let $ = S2(
|
|
1115
|
-
return
|
|
1116
|
-
}, 0), i) + 2, c =
|
|
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(`
|
|
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
|
|
1136
|
+
let r2 = [`${import_picocolors2.default.gray(o)}`];
|
|
1137
1137
|
if (t) {
|
|
1138
1138
|
let [i, ...s] = t.split(`
|
|
1139
1139
|
`);
|
|
1140
|
-
|
|
1140
|
+
r2.push(`${n} ${i}`, ...s.map((c) => `${import_picocolors2.default.gray(o)} ${c}`));
|
|
1141
1141
|
}
|
|
1142
|
-
process.stdout.write(`${
|
|
1142
|
+
process.stdout.write(`${r2.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"],
|
|
1160
|
-
let
|
|
1161
|
-
a &&
|
|
1162
|
-
},
|
|
1163
|
-
process.on("uncaughtExceptionMonitor",
|
|
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);
|
|
1164
1164
|
}, E = () => {
|
|
1165
|
-
process.removeListener("uncaughtExceptionMonitor",
|
|
1166
|
-
},
|
|
1167
|
-
if ($ === void 0)
|
|
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)
|
|
1168
1168
|
return;
|
|
1169
1169
|
i && process.stdout.write(`
|
|
1170
1170
|
`);
|
|
1171
|
-
let
|
|
1171
|
+
let m2 = $2.split(`
|
|
1172
1172
|
`);
|
|
1173
|
-
process.stdout.write(import_sisteransi2.cursor.move(-999,
|
|
1174
|
-
},
|
|
1175
|
-
let
|
|
1176
|
-
return
|
|
1177
|
-
},
|
|
1178
|
-
a = !0, s = fD(),
|
|
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)}
|
|
1179
1179
|
`);
|
|
1180
|
-
let
|
|
1181
|
-
|
|
1182
|
-
if (i &&
|
|
1180
|
+
let h2 = 0, w2 = 0;
|
|
1181
|
+
j2(), c = setInterval(() => {
|
|
1182
|
+
if (i && l2 === $2)
|
|
1183
1183
|
return;
|
|
1184
|
-
|
|
1185
|
-
let
|
|
1184
|
+
B2(), $2 = l2;
|
|
1185
|
+
let I2 = import_picocolors2.default.magenta(n[h2]);
|
|
1186
1186
|
if (i)
|
|
1187
|
-
process.stdout.write(`${
|
|
1187
|
+
process.stdout.write(`${I2} ${l2}...`);
|
|
1188
1188
|
else if (t === "timer")
|
|
1189
|
-
process.stdout.write(`${
|
|
1189
|
+
process.stdout.write(`${I2} ${l2} ${O2(g2)}`);
|
|
1190
1190
|
else {
|
|
1191
|
-
let
|
|
1192
|
-
process.stdout.write(`${
|
|
1191
|
+
let z2 = ".".repeat(Math.floor(w2)).slice(0, 3);
|
|
1192
|
+
process.stdout.write(`${I2} ${l2}${z2}`);
|
|
1193
1193
|
}
|
|
1194
|
-
|
|
1195
|
-
},
|
|
1196
|
-
},
|
|
1197
|
-
a = !1, clearInterval(c),
|
|
1198
|
-
let
|
|
1199
|
-
|
|
1200
|
-
`) : process.stdout.write(`${
|
|
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}
|
|
1201
1201
|
`), E(), s();
|
|
1202
1202
|
};
|
|
1203
|
-
return { start:
|
|
1204
|
-
|
|
1203
|
+
return { start: H2, stop: N2, message: (m2 = "") => {
|
|
1204
|
+
l2 = R2(m2 ?? l2);
|
|
1205
1205
|
} };
|
|
1206
1206
|
};
|
|
1207
1207
|
|
|
@@ -1284,29 +1284,29 @@ var ADAPTER_VERSION = "0.4.2", ADAPTER_RELEASES = [
|
|
|
1284
1284
|
steps: []
|
|
1285
1285
|
}
|
|
1286
1286
|
];
|
|
1287
|
-
function compareVersions(a,
|
|
1288
|
-
let parts = (
|
|
1289
|
-
if (
|
|
1290
|
-
return
|
|
1291
|
-
if (
|
|
1287
|
+
function compareVersions(a, b3) {
|
|
1288
|
+
let parts = (v2) => (v2.match(/^\D*(\d+)\.(\d+)\.(\d+)/) ?? []).slice(1).map(Number), [x2, y3] = [parts(a), parts(b3)];
|
|
1289
|
+
if (x2.length !== 3)
|
|
1290
|
+
return y3.length === 3 ? -1 : 0;
|
|
1291
|
+
if (y3.length !== 3)
|
|
1292
1292
|
return 1;
|
|
1293
1293
|
for (let i = 0;i < 3; i++)
|
|
1294
|
-
if (
|
|
1295
|
-
return
|
|
1294
|
+
if (x2[i] !== y3[i])
|
|
1295
|
+
return x2[i] < y3[i] ? -1 : 1;
|
|
1296
1296
|
return 0;
|
|
1297
1297
|
}
|
|
1298
1298
|
function releasesSince(version) {
|
|
1299
|
-
return ADAPTER_RELEASES.filter((
|
|
1299
|
+
return ADAPTER_RELEASES.filter((r2) => !version || compareVersions(r2.version, version) > 0).reverse();
|
|
1300
1300
|
}
|
|
1301
1301
|
function upgradePrompt(fromVersion) {
|
|
1302
|
-
let todo = releasesSince(fromVersion), breaking = todo.flatMap((
|
|
1302
|
+
let todo = releasesSince(fromVersion), breaking = todo.flatMap((r2) => r2.breaking ?? []), steps = todo.flatMap((r2) => r2.steps);
|
|
1303
1303
|
return [
|
|
1304
1304
|
`Update this app's LopeBase adapter (@lopebase/adapter) from ${fromVersion ?? "its current version"} to 0.4.2.`,
|
|
1305
1305
|
"",
|
|
1306
1306
|
"1. Install it with this repo's package manager, for example `npm install @lopebase/adapter@latest`. A 0.x range like ^0.1.2 does not pick up 0.2 or later on its own.",
|
|
1307
1307
|
...breaking.length ? [
|
|
1308
1308
|
"2. These changes would stop the adapter from starting; handle each one:",
|
|
1309
|
-
...breaking.map((
|
|
1309
|
+
...breaking.map((b3) => ` - ${b3}`)
|
|
1310
1310
|
] : ["2. Nothing in this update stops an existing adapter from starting."],
|
|
1311
1311
|
...steps.length ? [
|
|
1312
1312
|
"3. Adopt what is new, asking the human before each change:",
|
|
@@ -1322,7 +1322,7 @@ function upgradePrompt(fromVersion) {
|
|
|
1322
1322
|
var SIGNATURE_HEADER = "lopebase-signature";
|
|
1323
1323
|
var enc = /* @__PURE__ */ new TextEncoder;
|
|
1324
1324
|
function hex(bytes) {
|
|
1325
|
-
return [...new Uint8Array(bytes)].map((
|
|
1325
|
+
return [...new Uint8Array(bytes)].map((b3) => b3.toString(16).padStart(2, "0")).join("");
|
|
1326
1326
|
}
|
|
1327
1327
|
async function sha256Hex(data) {
|
|
1328
1328
|
return hex(await crypto.subtle.digest("SHA-256", data));
|
|
@@ -1333,8 +1333,8 @@ async function hmacHex(secret, message) {
|
|
|
1333
1333
|
}
|
|
1334
1334
|
function newSigningSecret() {
|
|
1335
1335
|
let bytes = crypto.getRandomValues(new Uint8Array(32)), bin = "";
|
|
1336
|
-
for (let
|
|
1337
|
-
bin += String.fromCharCode(
|
|
1336
|
+
for (let b3 of bytes)
|
|
1337
|
+
bin += String.fromCharCode(b3);
|
|
1338
1338
|
return "lbs_" + btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1339
1339
|
}
|
|
1340
1340
|
function payload(t, method, pathAndQuery, bodyHash) {
|
|
@@ -1371,7 +1371,7 @@ var SENSITIVE_PARTS = /* @__PURE__ */ new Set([
|
|
|
1371
1371
|
]), SENSITIVE_JOINED = /apikey|privatekey|secretkey|accesskey|accountnumber|routingnumber|stripecustomer|paymentmethod|recoverycode/;
|
|
1372
1372
|
function isSensitiveFieldName(name) {
|
|
1373
1373
|
let parts = name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
1374
|
-
if (parts.some((
|
|
1374
|
+
if (parts.some((p2) => SENSITIVE_PARTS.has(p2)))
|
|
1375
1375
|
return !0;
|
|
1376
1376
|
return SENSITIVE_JOINED.test(parts.join(""));
|
|
1377
1377
|
}
|
|
@@ -1390,12 +1390,12 @@ function conformanceChecks(opts) {
|
|
|
1390
1390
|
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);
|
|
1391
1391
|
async function signedHeaders(url, method, bodyText, timestamp) {
|
|
1392
1392
|
if (opts.secret) {
|
|
1393
|
-
let
|
|
1393
|
+
let u2 = new URL(url);
|
|
1394
1394
|
return {
|
|
1395
1395
|
[SIGNATURE_HEADER]: await signRequest({
|
|
1396
1396
|
secret: opts.secret,
|
|
1397
1397
|
method,
|
|
1398
|
-
pathAndQuery:
|
|
1398
|
+
pathAndQuery: u2.pathname + u2.search,
|
|
1399
1399
|
body: bodyText,
|
|
1400
1400
|
timestamp
|
|
1401
1401
|
})
|
|
@@ -1413,14 +1413,14 @@ function conformanceChecks(opts) {
|
|
|
1413
1413
|
...init?.headers
|
|
1414
1414
|
}
|
|
1415
1415
|
}));
|
|
1416
|
-
}, resources = () => authed("/resources").then((
|
|
1416
|
+
}, resources = () => authed("/resources").then((r2) => body(r2)), checks = [
|
|
1417
1417
|
{
|
|
1418
1418
|
name: "GET /health returns { ok } without auth",
|
|
1419
1419
|
async run() {
|
|
1420
1420
|
let res = await send(new Request(`${base}/health`));
|
|
1421
1421
|
assert.equal(res.status, 200);
|
|
1422
|
-
let
|
|
1423
|
-
assert.equal(typeof
|
|
1422
|
+
let b3 = await body(res);
|
|
1423
|
+
assert.equal(typeof b3.ok, "boolean");
|
|
1424
1424
|
}
|
|
1425
1425
|
},
|
|
1426
1426
|
{
|
|
@@ -1435,10 +1435,10 @@ function conformanceChecks(opts) {
|
|
|
1435
1435
|
async run() {
|
|
1436
1436
|
let res = await authed("/resources");
|
|
1437
1437
|
assert.equal(res.status, 200);
|
|
1438
|
-
let
|
|
1439
|
-
assert.ok(Array.isArray(
|
|
1440
|
-
for (let
|
|
1441
|
-
assert.ok(
|
|
1438
|
+
let b3 = await body(res);
|
|
1439
|
+
assert.ok(Array.isArray(b3.resources));
|
|
1440
|
+
for (let r2 of b3.resources)
|
|
1441
|
+
assert.ok(r2.name && r2.label && r2.readCapability), assert.ok(Array.isArray(r2.fields));
|
|
1442
1442
|
}
|
|
1443
1443
|
},
|
|
1444
1444
|
{
|
|
@@ -1446,9 +1446,9 @@ function conformanceChecks(opts) {
|
|
|
1446
1446
|
async run() {
|
|
1447
1447
|
let res = await authed("/actions");
|
|
1448
1448
|
assert.equal(res.status, 200);
|
|
1449
|
-
let
|
|
1450
|
-
assert.ok(Array.isArray(
|
|
1451
|
-
for (let a of
|
|
1449
|
+
let b3 = await body(res);
|
|
1450
|
+
assert.ok(Array.isArray(b3.actions));
|
|
1451
|
+
for (let a of b3.actions)
|
|
1452
1452
|
assert.ok(a.name && a.label && a.capability), assert.ok(Array.isArray(a.params));
|
|
1453
1453
|
}
|
|
1454
1454
|
},
|
|
@@ -1467,7 +1467,7 @@ function conformanceChecks(opts) {
|
|
|
1467
1467
|
{
|
|
1468
1468
|
name: "GET /resources/:name/:id returns a row or 404-null",
|
|
1469
1469
|
async run() {
|
|
1470
|
-
let cat = await resources(), meta = opts.sampleResource ? cat.resources.find((
|
|
1470
|
+
let cat = await resources(), meta = opts.sampleResource ? cat.resources.find((r2) => r2.name === opts.sampleResource) : cat.resources[0];
|
|
1471
1471
|
if (!meta)
|
|
1472
1472
|
return;
|
|
1473
1473
|
let first = (await body(await authed(`/resources/${meta.name}?limit=1`))).rows[0];
|
|
@@ -1482,8 +1482,8 @@ function conformanceChecks(opts) {
|
|
|
1482
1482
|
async run() {
|
|
1483
1483
|
let res = await authed("/audit");
|
|
1484
1484
|
assert.equal(res.status, 200);
|
|
1485
|
-
let
|
|
1486
|
-
assert.ok(Array.isArray(
|
|
1485
|
+
let b3 = await body(res);
|
|
1486
|
+
assert.ok(Array.isArray(b3.rows));
|
|
1487
1487
|
}
|
|
1488
1488
|
},
|
|
1489
1489
|
{
|
|
@@ -1497,24 +1497,24 @@ function conformanceChecks(opts) {
|
|
|
1497
1497
|
name: "no field in the catalog looks like a secret unless marked safe",
|
|
1498
1498
|
async run() {
|
|
1499
1499
|
let cat = await resources();
|
|
1500
|
-
for (let
|
|
1501
|
-
for (let f of
|
|
1502
|
-
assert.ok(!isSensitiveFieldName(f.name) || f.sensitive === "allow", `${
|
|
1500
|
+
for (let r2 of cat.resources)
|
|
1501
|
+
for (let f of r2.fields)
|
|
1502
|
+
assert.ok(!isSensitiveFieldName(f.name) || f.sensitive === "allow", `${r2.name}.${f.name} looks sensitive`);
|
|
1503
1503
|
}
|
|
1504
1504
|
},
|
|
1505
1505
|
{
|
|
1506
1506
|
name: "rows contain only declared fields, and a page is at most 100",
|
|
1507
1507
|
async run() {
|
|
1508
1508
|
let cat = await resources();
|
|
1509
|
-
for (let
|
|
1510
|
-
let res = await authed(`/resources/${
|
|
1509
|
+
for (let r2 of cat.resources) {
|
|
1510
|
+
let res = await authed(`/resources/${r2.name}?limit=1000`);
|
|
1511
1511
|
assert.equal(res.status, 200);
|
|
1512
1512
|
let page = await body(res);
|
|
1513
|
-
assert.ok(page.rows.length <= 100, `${
|
|
1514
|
-
let declared = /* @__PURE__ */ new Set(["id", ...
|
|
1513
|
+
assert.ok(page.rows.length <= 100, `${r2.name} returned ${page.rows.length} rows`);
|
|
1514
|
+
let declared = /* @__PURE__ */ new Set(["id", ...r2.fields.map((f) => f.name)]);
|
|
1515
1515
|
for (let row of page.rows)
|
|
1516
|
-
for (let
|
|
1517
|
-
assert.ok(declared.has(
|
|
1516
|
+
for (let k3 of Object.keys(row))
|
|
1517
|
+
assert.ok(declared.has(k3), `${r2.name} returned undeclared field ${k3}`);
|
|
1518
1518
|
}
|
|
1519
1519
|
}
|
|
1520
1520
|
},
|
|
@@ -1532,7 +1532,7 @@ function conformanceChecks(opts) {
|
|
|
1532
1532
|
{
|
|
1533
1533
|
name: "a mutation writes an audit row (PATCH round-trips into /audit)",
|
|
1534
1534
|
async run() {
|
|
1535
|
-
let writable = (await resources()).resources.find((
|
|
1535
|
+
let writable = (await resources()).resources.find((r2) => r2.writeCapability && r2.fields.some((f) => f.editable));
|
|
1536
1536
|
if (!writable || remote)
|
|
1537
1537
|
return;
|
|
1538
1538
|
let row = (await body(await authed(`/resources/${writable.name}?limit=1`))).rows[0];
|
|
@@ -1568,8 +1568,8 @@ async function checkAdapter(input) {
|
|
|
1568
1568
|
})
|
|
1569
1569
|
} : { authorization: `Bearer ${input.token ?? ""}` };
|
|
1570
1570
|
status = (await fetch(url, { headers })).status;
|
|
1571
|
-
} catch (
|
|
1572
|
-
let error = `Could not reach ${base} (${
|
|
1571
|
+
} catch (e2) {
|
|
1572
|
+
let error = `Could not reach ${base} (${e2 instanceof Error ? e2.message : String(e2)}).`;
|
|
1573
1573
|
return log(`FAIL ${error}`), { passed, failed: [{ name: "reachable", error }] };
|
|
1574
1574
|
}
|
|
1575
1575
|
if (status === 401) {
|
|
@@ -1587,9 +1587,9 @@ async function checkAdapter(input) {
|
|
|
1587
1587
|
}))
|
|
1588
1588
|
try {
|
|
1589
1589
|
await c.run(), passed++, log(`pass ${c.name}`);
|
|
1590
|
-
} catch (
|
|
1591
|
-
let error =
|
|
1592
|
-
`)[0] : String(
|
|
1590
|
+
} catch (e2) {
|
|
1591
|
+
let error = e2 instanceof Error ? e2.message.split(`
|
|
1592
|
+
`)[0] : String(e2);
|
|
1593
1593
|
failed.push({ name: c.name, error }), log(`FAIL ${c.name}: ${error}`);
|
|
1594
1594
|
}
|
|
1595
1595
|
return { passed, failed };
|
|
@@ -1603,7 +1603,7 @@ var AGENTS = [
|
|
|
1603
1603
|
id: "claude",
|
|
1604
1604
|
label: "Claude Code",
|
|
1605
1605
|
bin: "claude",
|
|
1606
|
-
args: (
|
|
1606
|
+
args: (p2) => [p2],
|
|
1607
1607
|
mcpAdd: (argv) => [
|
|
1608
1608
|
"mcp",
|
|
1609
1609
|
"add",
|
|
@@ -1618,27 +1618,27 @@ var AGENTS = [
|
|
|
1618
1618
|
id: "codex",
|
|
1619
1619
|
label: "Codex",
|
|
1620
1620
|
bin: "codex",
|
|
1621
|
-
args: (
|
|
1621
|
+
args: (p2) => [p2],
|
|
1622
1622
|
mcpAdd: (argv) => ["mcp", "add", "lopebase", "--", ...argv]
|
|
1623
1623
|
},
|
|
1624
1624
|
{
|
|
1625
1625
|
id: "gemini",
|
|
1626
1626
|
label: "Gemini CLI",
|
|
1627
1627
|
bin: "gemini",
|
|
1628
|
-
args: (
|
|
1628
|
+
args: (p2) => ["-i", p2],
|
|
1629
1629
|
mcpAdd: (argv) => ["mcp", "add", "lopebase", ...argv]
|
|
1630
1630
|
},
|
|
1631
1631
|
{
|
|
1632
1632
|
id: "opencode",
|
|
1633
1633
|
label: "opencode",
|
|
1634
1634
|
bin: "opencode",
|
|
1635
|
-
args: (
|
|
1635
|
+
args: (p2) => ["--prompt", p2]
|
|
1636
1636
|
},
|
|
1637
1637
|
{
|
|
1638
1638
|
id: "cursor-agent",
|
|
1639
1639
|
label: "Cursor Agent",
|
|
1640
1640
|
bin: "cursor-agent",
|
|
1641
|
-
args: (
|
|
1641
|
+
args: (p2) => [p2]
|
|
1642
1642
|
}
|
|
1643
1643
|
];
|
|
1644
1644
|
function isInstalled(bin) {
|
|
@@ -1665,29 +1665,287 @@ function installedAgents() {
|
|
|
1665
1665
|
return AGENTS.filter((a) => isInstalled(a.bin));
|
|
1666
1666
|
}
|
|
1667
1667
|
|
|
1668
|
+
// src/blog-check.ts
|
|
1669
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
1670
|
+
import { join } from "node:path";
|
|
1671
|
+
// ../lopebase-blog/src/contract.ts
|
|
1672
|
+
var shared = {
|
|
1673
|
+
title: { kind: "string", required: !0, role: "title" },
|
|
1674
|
+
description: { kind: "string", required: !0, role: "description" },
|
|
1675
|
+
author: { kind: "string", role: "author" },
|
|
1676
|
+
tags: { kind: "tags", role: "tags" }
|
|
1677
|
+
}, markdownKeys = {
|
|
1678
|
+
...shared,
|
|
1679
|
+
date: { kind: "date", required: !0, role: "date" },
|
|
1680
|
+
slug: { kind: "string", required: !0, role: "slug" },
|
|
1681
|
+
updated: { kind: "date", role: "updated" },
|
|
1682
|
+
image: { kind: "url", role: "image" },
|
|
1683
|
+
image_alt: { kind: "string", role: "imageAlt" },
|
|
1684
|
+
faq: { kind: "faq", role: "faq" }
|
|
1685
|
+
}, BLOG_PRESETS = {
|
|
1686
|
+
markdown: {
|
|
1687
|
+
dir: "content/blog",
|
|
1688
|
+
ext: ".md",
|
|
1689
|
+
keys: markdownKeys,
|
|
1690
|
+
slugKey: "slug"
|
|
1691
|
+
},
|
|
1692
|
+
"next-mdx": {
|
|
1693
|
+
dir: "content/blog",
|
|
1694
|
+
ext: ".mdx",
|
|
1695
|
+
keys: markdownKeys,
|
|
1696
|
+
slugKey: "slug"
|
|
1697
|
+
},
|
|
1698
|
+
astro: {
|
|
1699
|
+
dir: "src/content/blog",
|
|
1700
|
+
ext: ".md",
|
|
1701
|
+
keys: {
|
|
1702
|
+
...shared,
|
|
1703
|
+
pubDate: { kind: "date", required: !0, role: "date" },
|
|
1704
|
+
updatedDate: { kind: "date", role: "updated" },
|
|
1705
|
+
heroImage: { kind: "url", role: "image" }
|
|
1706
|
+
}
|
|
1707
|
+
},
|
|
1708
|
+
hugo: {
|
|
1709
|
+
dir: "content/posts",
|
|
1710
|
+
ext: ".md",
|
|
1711
|
+
keys: {
|
|
1712
|
+
...shared,
|
|
1713
|
+
date: { kind: "datetime", required: !0, role: "date" },
|
|
1714
|
+
slug: { kind: "string", required: !0, role: "slug" },
|
|
1715
|
+
lastmod: { kind: "datetime", role: "updated" },
|
|
1716
|
+
images: { kind: "urls", role: "images" }
|
|
1717
|
+
},
|
|
1718
|
+
slugKey: "slug"
|
|
1719
|
+
},
|
|
1720
|
+
jekyll: {
|
|
1721
|
+
dir: "_posts",
|
|
1722
|
+
ext: ".md",
|
|
1723
|
+
keys: {
|
|
1724
|
+
layout: { kind: "layout", required: !0 },
|
|
1725
|
+
...shared,
|
|
1726
|
+
date: { kind: "date", required: !0, role: "date" },
|
|
1727
|
+
last_modified_at: { kind: "date", role: "updated" },
|
|
1728
|
+
image: { kind: "url", role: "image" }
|
|
1729
|
+
},
|
|
1730
|
+
datedFileKey: "date"
|
|
1731
|
+
}
|
|
1732
|
+
}, isBlogFormat = (v2) => Object.hasOwn(BLOG_PRESETS, v2), SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/, DATE = /^(\d{4})-(\d{2})-(\d{2})$/, DATETIME = /^(\d{4})-(\d{2})-(\d{2})T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/, JEKYLL_FILE = /^(\d{4}-\d{2}-\d{2})-(.+)$/, HTML_TAG = /<\/?[A-Za-z][A-Za-z0-9-]*(?:\s[^<>]*)?\/?>|<!--/;
|
|
1733
|
+
function realDate(y3, m2, d3) {
|
|
1734
|
+
let date = new Date(Date.UTC(+y3, +m2 - 1, +d3));
|
|
1735
|
+
return date.getUTCFullYear() === +y3 && date.getUTCMonth() === +m2 - 1 && date.getUTCDate() === +d3;
|
|
1736
|
+
}
|
|
1737
|
+
function isUrl(v2) {
|
|
1738
|
+
if (typeof v2 !== "string")
|
|
1739
|
+
return !1;
|
|
1740
|
+
try {
|
|
1741
|
+
let u2 = new URL(v2);
|
|
1742
|
+
return u2.protocol === "https:" || u2.protocol === "http:";
|
|
1743
|
+
} catch {
|
|
1744
|
+
return !1;
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
var filled = (v2) => typeof v2 === "string" && v2.trim() !== "";
|
|
1748
|
+
function json(raw) {
|
|
1749
|
+
try {
|
|
1750
|
+
return { ok: !0, value: JSON.parse(raw) };
|
|
1751
|
+
} catch {
|
|
1752
|
+
return { ok: !1 };
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
function checkValue(key, kind, raw) {
|
|
1756
|
+
switch (kind) {
|
|
1757
|
+
case "layout":
|
|
1758
|
+
return raw === "post" ? void 0 : `${key} must be post`;
|
|
1759
|
+
case "date": {
|
|
1760
|
+
let m2 = DATE.exec(raw);
|
|
1761
|
+
return m2 && realDate(m2[1], m2[2], m2[3]) ? void 0 : `${key} must be a date like 2026-11-05`;
|
|
1762
|
+
}
|
|
1763
|
+
case "datetime": {
|
|
1764
|
+
let m2 = DATETIME.exec(raw);
|
|
1765
|
+
return m2 && realDate(m2[1], m2[2], m2[3]) ? void 0 : `${key} must be a date and time like 2026-11-05T15:00:00Z`;
|
|
1766
|
+
}
|
|
1767
|
+
case "string": {
|
|
1768
|
+
let v2 = json(raw);
|
|
1769
|
+
if (!v2.ok || typeof v2.value !== "string")
|
|
1770
|
+
return `${key} must be a quoted string`;
|
|
1771
|
+
return v2.value.trim() ? void 0 : `${key} is empty`;
|
|
1772
|
+
}
|
|
1773
|
+
case "url": {
|
|
1774
|
+
let v2 = json(raw);
|
|
1775
|
+
return v2.ok && isUrl(v2.value) ? void 0 : `${key} must be a quoted http or https address`;
|
|
1776
|
+
}
|
|
1777
|
+
case "tags": {
|
|
1778
|
+
let v2 = json(raw);
|
|
1779
|
+
return v2.ok && Array.isArray(v2.value) && v2.value.every((t) => typeof t === "string") ? void 0 : `${key} must be a list of quoted strings, like ["seo"]`;
|
|
1780
|
+
}
|
|
1781
|
+
case "urls": {
|
|
1782
|
+
let v2 = json(raw);
|
|
1783
|
+
return v2.ok && Array.isArray(v2.value) && v2.value.every(isUrl) ? void 0 : `${key} must be a list of quoted http or https addresses`;
|
|
1784
|
+
}
|
|
1785
|
+
case "faq": {
|
|
1786
|
+
let v2 = json(raw);
|
|
1787
|
+
return v2.ok && Array.isArray(v2.value) && v2.value.every((f) => f !== null && typeof f === "object" && filled(f.q) && filled(f.a)) ? void 0 : `${key} must be one line of JSON like [{"q": "A question?", "a": "An answer."}]`;
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
function proseOnly(body2) {
|
|
1792
|
+
let out = [], fence;
|
|
1793
|
+
for (let line of body2.split(`
|
|
1794
|
+
`)) {
|
|
1795
|
+
let open = /^\s{0,3}(`{3,}|~{3,})/.exec(line);
|
|
1796
|
+
if (fence) {
|
|
1797
|
+
if (open && open[1][0] === fence[0] && open[1].length >= fence.length)
|
|
1798
|
+
fence = void 0;
|
|
1799
|
+
continue;
|
|
1800
|
+
}
|
|
1801
|
+
if (open) {
|
|
1802
|
+
fence = open[1];
|
|
1803
|
+
continue;
|
|
1804
|
+
}
|
|
1805
|
+
out.push(line.replace(/(`+)[^`]*?\1/g, ""));
|
|
1806
|
+
}
|
|
1807
|
+
return out;
|
|
1808
|
+
}
|
|
1809
|
+
function inspectPost(fileName, text, format) {
|
|
1810
|
+
let preset = BLOG_PRESETS[format], problems = [], base = fileName.endsWith(preset.ext) ? fileName.slice(0, fileName.length - preset.ext.length) : fileName.replace(/\.[^.]*$/, ""), fileSlug = base, fileDate;
|
|
1811
|
+
if (preset.datedFileKey) {
|
|
1812
|
+
let m2 = JEKYLL_FILE.exec(base);
|
|
1813
|
+
if (m2)
|
|
1814
|
+
fileDate = m2[1], fileSlug = m2[2];
|
|
1815
|
+
else
|
|
1816
|
+
problems.push("file name must look like YYYY-MM-DD-<slug>.md");
|
|
1817
|
+
}
|
|
1818
|
+
if (!SLUG.test(fileSlug))
|
|
1819
|
+
problems.push(`file name slug "${fileSlug}" must be lowercase words joined by hyphens`);
|
|
1820
|
+
let lines = text.replace(/^/, "").split(/\r?\n/), end = lines[0] === "---" ? lines.indexOf("---", 1) : -1;
|
|
1821
|
+
if (end < 0)
|
|
1822
|
+
return problems.push("front matter must open and close with --- lines at the top"), { slug: fileSlug, problems };
|
|
1823
|
+
let raws = /* @__PURE__ */ new Map;
|
|
1824
|
+
for (let line of lines.slice(1, end)) {
|
|
1825
|
+
if (!line.trim())
|
|
1826
|
+
continue;
|
|
1827
|
+
let m2 = /^([A-Za-z_][A-Za-z0-9_]*):\s*(.*)$/.exec(line);
|
|
1828
|
+
if (!m2) {
|
|
1829
|
+
problems.push(`front matter line is not "key: value": ${line.trim()}`);
|
|
1830
|
+
continue;
|
|
1831
|
+
}
|
|
1832
|
+
if (raws.has(m2[1]))
|
|
1833
|
+
problems.push(`${m2[1]} appears more than once`);
|
|
1834
|
+
raws.set(m2[1], m2[2].trim());
|
|
1835
|
+
}
|
|
1836
|
+
let values = {};
|
|
1837
|
+
for (let [key, spec] of Object.entries(preset.keys)) {
|
|
1838
|
+
let raw = raws.get(key);
|
|
1839
|
+
if (raw === void 0 || raw === "") {
|
|
1840
|
+
if (spec.required)
|
|
1841
|
+
problems.push(`missing ${key}`);
|
|
1842
|
+
continue;
|
|
1843
|
+
}
|
|
1844
|
+
let problem = checkValue(key, spec.kind, raw);
|
|
1845
|
+
if (problem)
|
|
1846
|
+
problems.push(problem);
|
|
1847
|
+
else if (spec.role)
|
|
1848
|
+
values[spec.role] = spec.kind === "date" || spec.kind === "datetime" ? raw : JSON.parse(raw);
|
|
1849
|
+
}
|
|
1850
|
+
let slug = fileSlug;
|
|
1851
|
+
if (preset.slugKey) {
|
|
1852
|
+
let v2 = json(raws.get(preset.slugKey) ?? "");
|
|
1853
|
+
if (v2.ok && typeof v2.value === "string" && v2.value) {
|
|
1854
|
+
if (!SLUG.test(v2.value))
|
|
1855
|
+
problems.push(`${preset.slugKey} "${v2.value}" must be lowercase words joined by hyphens`);
|
|
1856
|
+
else if (v2.value !== fileSlug)
|
|
1857
|
+
problems.push(`${preset.slugKey} "${v2.value}" does not match the file name "${fileSlug}"`);
|
|
1858
|
+
slug = v2.value;
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
if (preset.datedFileKey && fileDate) {
|
|
1862
|
+
let date = raws.get(preset.datedFileKey);
|
|
1863
|
+
if (date && DATE.test(date) && date !== fileDate)
|
|
1864
|
+
problems.push(`${preset.datedFileKey} ${date} does not match the file name date ${fileDate}`);
|
|
1865
|
+
}
|
|
1866
|
+
let body2 = lines.slice(end + 1).join(`
|
|
1867
|
+
`), fields = { slug, values, body: body2 };
|
|
1868
|
+
if (!body2.trim())
|
|
1869
|
+
return problems.push("the post has no body"), { slug, problems, fields };
|
|
1870
|
+
let prose = proseOnly(body2), first = body2.split(`
|
|
1871
|
+
`).find((l2) => l2.trim());
|
|
1872
|
+
if (first && /^\s{0,3}#(?:\s|$)/.test(first))
|
|
1873
|
+
problems.push("the body starts with a # heading; the title comes from front matter");
|
|
1874
|
+
if (!prose.some((l2) => /^\s{0,3}## /.test(l2)))
|
|
1875
|
+
problems.push("the body needs at least one ## heading");
|
|
1876
|
+
if (prose.some((l2) => HTML_TAG.test(l2)))
|
|
1877
|
+
problems.push("the body has raw HTML outside code; use Markdown instead");
|
|
1878
|
+
return { slug, problems, fields };
|
|
1879
|
+
}
|
|
1880
|
+
function checkPost(fileName, text, format) {
|
|
1881
|
+
let { slug, problems } = inspectPost(fileName, text, format);
|
|
1882
|
+
return { slug, problems };
|
|
1883
|
+
}
|
|
1884
|
+
// src/blog-check.ts
|
|
1885
|
+
function checkBlog(opts) {
|
|
1886
|
+
let format = opts.format ?? "markdown", preset = BLOG_PRESETS[format], dir = (opts.dir ?? preset.dir).replace(/\\/g, "/").replace(/\/+$/, ""), abs = join(opts.cwd, dir), problems = [], names;
|
|
1887
|
+
try {
|
|
1888
|
+
if (!statSync(abs).isDirectory())
|
|
1889
|
+
return {
|
|
1890
|
+
dir,
|
|
1891
|
+
posts: 0,
|
|
1892
|
+
problems: [{ file: dir, problem: "is not a folder" }]
|
|
1893
|
+
};
|
|
1894
|
+
names = readdirSync(abs, { withFileTypes: !0 }).filter((e2) => e2.isFile() && e2.name.endsWith(preset.ext)).map((e2) => e2.name).sort();
|
|
1895
|
+
} catch {
|
|
1896
|
+
return {
|
|
1897
|
+
dir,
|
|
1898
|
+
posts: 0,
|
|
1899
|
+
problems: opts.allowEmpty ? [] : [{ file: dir, problem: "folder not found (pass --allow-empty)" }]
|
|
1900
|
+
};
|
|
1901
|
+
}
|
|
1902
|
+
if (!names.length && !opts.allowEmpty)
|
|
1903
|
+
problems.push({
|
|
1904
|
+
file: dir,
|
|
1905
|
+
problem: `no ${preset.ext} posts found (pass --allow-empty)`
|
|
1906
|
+
});
|
|
1907
|
+
let seen = /* @__PURE__ */ new Map;
|
|
1908
|
+
for (let name of names) {
|
|
1909
|
+
let file = `${dir}/${name}`, res = checkPost(name, readFileSync(join(abs, name), "utf8"), format);
|
|
1910
|
+
for (let problem of res.problems)
|
|
1911
|
+
problems.push({ file, problem });
|
|
1912
|
+
if (res.slug) {
|
|
1913
|
+
let other = seen.get(res.slug);
|
|
1914
|
+
if (other)
|
|
1915
|
+
problems.push({
|
|
1916
|
+
file,
|
|
1917
|
+
problem: `slug "${res.slug}" is also used by ${other}`
|
|
1918
|
+
});
|
|
1919
|
+
else
|
|
1920
|
+
seen.set(res.slug, file);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
return { dir, posts: names.length, problems };
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1668
1926
|
// src/config.ts
|
|
1669
1927
|
import {
|
|
1670
1928
|
chmodSync,
|
|
1671
1929
|
existsSync,
|
|
1672
1930
|
mkdirSync,
|
|
1673
|
-
readFileSync,
|
|
1931
|
+
readFileSync as readFileSync2,
|
|
1674
1932
|
writeFileSync
|
|
1675
1933
|
} from "node:fs";
|
|
1676
1934
|
import os from "node:os";
|
|
1677
1935
|
import path from "node:path";
|
|
1678
1936
|
var DEFAULT_URL = "https://app.lopebase.com";
|
|
1679
1937
|
function workspaceUrl(input) {
|
|
1680
|
-
let
|
|
1681
|
-
if (!
|
|
1938
|
+
let v2 = input.trim().replace(/\/+$/, "");
|
|
1939
|
+
if (!v2)
|
|
1682
1940
|
return null;
|
|
1683
|
-
if (/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(
|
|
1684
|
-
return `https://${
|
|
1685
|
-
let withScheme = /^https?:\/\//i.test(
|
|
1941
|
+
if (/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(v2))
|
|
1942
|
+
return `https://${v2.toLowerCase()}.lopebase.com`;
|
|
1943
|
+
let withScheme = /^https?:\/\//i.test(v2) ? v2 : `https://${v2}`;
|
|
1686
1944
|
try {
|
|
1687
|
-
let
|
|
1688
|
-
if (!
|
|
1945
|
+
let u2 = new URL(withScheme);
|
|
1946
|
+
if (!u2.hostname.includes(".") && u2.hostname !== "localhost")
|
|
1689
1947
|
return null;
|
|
1690
|
-
return `${
|
|
1948
|
+
return `${u2.origin}${u2.pathname.replace(/\/+$/, "")}`;
|
|
1691
1949
|
} catch {
|
|
1692
1950
|
return null;
|
|
1693
1951
|
}
|
|
@@ -1716,7 +1974,7 @@ function loadAuth() {
|
|
|
1716
1974
|
if (!existsSync(authFile()))
|
|
1717
1975
|
return null;
|
|
1718
1976
|
try {
|
|
1719
|
-
let a = JSON.parse(
|
|
1977
|
+
let a = JSON.parse(readFileSync2(authFile(), "utf8"));
|
|
1720
1978
|
if (!a.token)
|
|
1721
1979
|
return null;
|
|
1722
1980
|
return { url: envUrl || a.url || DEFAULT_URL, token: a.token };
|
|
@@ -1737,7 +1995,7 @@ var APP_SIDE_ENV = /^(NEXT_PUBLIC_|PUBLIC_|VITE_|NUXT_PUBLIC_|EXPO_PUBLIC_|REACT
|
|
|
1737
1995
|
function evidenceFor(facts, rule) {
|
|
1738
1996
|
let out = [], weak = [];
|
|
1739
1997
|
for (let re of rule.deps ?? []) {
|
|
1740
|
-
let hit = facts.dependencies.find((
|
|
1998
|
+
let hit = facts.dependencies.find((d3) => re.test(d3));
|
|
1741
1999
|
if (hit)
|
|
1742
2000
|
out.push(`uses ${hit.replace(/^(py|gem|composer|go|cargo):/, "")}`);
|
|
1743
2001
|
}
|
|
@@ -1747,7 +2005,7 @@ function evidenceFor(facts, rule) {
|
|
|
1747
2005
|
out.push(`has ${hit.replace(/#.*$/, "")}`);
|
|
1748
2006
|
}
|
|
1749
2007
|
for (let re of rule.env ?? []) {
|
|
1750
|
-
let hit = facts.envKeys.find((
|
|
2008
|
+
let hit = facts.envKeys.find((k3) => re.test(k3));
|
|
1751
2009
|
if (hit)
|
|
1752
2010
|
(APP_SIDE_ENV.test(hit) ? out : weak).push(`env ${hit}`);
|
|
1753
2011
|
}
|
|
@@ -2329,11 +2587,11 @@ var QUEUES = [
|
|
|
2329
2587
|
[/^py:celery$/, "Job queue (Celery)"],
|
|
2330
2588
|
[/^gem:sidekiq$/, "Job queue (Sidekiq)"]
|
|
2331
2589
|
], NOT_A_SERVICE = /* @__PURE__ */ new Set(["Prisma", "Drizzle"]);
|
|
2332
|
-
function watchList(
|
|
2590
|
+
function watchList(d3, facts) {
|
|
2333
2591
|
let slug = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, ""), out = [];
|
|
2334
|
-
if (
|
|
2592
|
+
if (d3.sources.some((s) => s.source === "uptime") || facts.siteUrl)
|
|
2335
2593
|
out.push({ id: "site", label: facts.siteUrl ?? "Website", how: "address" });
|
|
2336
|
-
for (let s of
|
|
2594
|
+
for (let s of d3.stack)
|
|
2337
2595
|
if (s.kind === "database" && !NOT_A_SERVICE.has(s.name))
|
|
2338
2596
|
out.push({ id: slug(s.name), label: s.name, how: "service" });
|
|
2339
2597
|
for (let [re, label] of QUEUES)
|
|
@@ -2343,7 +2601,7 @@ function watchList(d, facts) {
|
|
|
2343
2601
|
label,
|
|
2344
2602
|
how: "service"
|
|
2345
2603
|
});
|
|
2346
|
-
let email =
|
|
2604
|
+
let email = d3.stack.find((s) => s.kind === "email" && s.name !== "Nodemailer");
|
|
2347
2605
|
if (email)
|
|
2348
2606
|
out.push({
|
|
2349
2607
|
id: "email",
|
|
@@ -2351,7 +2609,7 @@ function watchList(d, facts) {
|
|
|
2351
2609
|
how: "service"
|
|
2352
2610
|
});
|
|
2353
2611
|
let seen = /* @__PURE__ */ new Set;
|
|
2354
|
-
return out.filter((
|
|
2612
|
+
return out.filter((w2) => !seen.has(w2.id) && seen.add(w2.id));
|
|
2355
2613
|
}
|
|
2356
2614
|
|
|
2357
2615
|
// src/device-login.ts
|
|
@@ -2368,27 +2626,27 @@ async function startDeviceLogin(url, opts = {}) {
|
|
|
2368
2626
|
});
|
|
2369
2627
|
if (!res.ok)
|
|
2370
2628
|
return null;
|
|
2371
|
-
let
|
|
2372
|
-
if (typeof
|
|
2629
|
+
let b3 = await res.json();
|
|
2630
|
+
if (typeof b3.device_code !== "string" || typeof b3.user_code !== "string")
|
|
2373
2631
|
return null;
|
|
2374
2632
|
return {
|
|
2375
|
-
deviceCode:
|
|
2376
|
-
userCode:
|
|
2377
|
-
verifyUrl: String(
|
|
2378
|
-
interval: Number(
|
|
2379
|
-
expiresIn: Number(
|
|
2633
|
+
deviceCode: b3.device_code,
|
|
2634
|
+
userCode: b3.user_code,
|
|
2635
|
+
verifyUrl: String(b3.verification_uri_complete ?? b3.verification_uri),
|
|
2636
|
+
interval: Number(b3.interval) || 5,
|
|
2637
|
+
expiresIn: Number(b3.expires_in) || 600
|
|
2380
2638
|
};
|
|
2381
2639
|
} catch {
|
|
2382
2640
|
return null;
|
|
2383
2641
|
}
|
|
2384
2642
|
}
|
|
2385
2643
|
async function pollDeviceLogin(url, start, opts = {}) {
|
|
2386
|
-
let f = opts.fetchImpl ?? fetch, sleep = opts.sleep ?? ((ms) => new Promise((
|
|
2644
|
+
let f = opts.fetchImpl ?? fetch, sleep = opts.sleep ?? ((ms) => new Promise((r2) => setTimeout(r2, ms))), interval = start.interval, deadline = Date.now() + start.expiresIn * 1000;
|
|
2387
2645
|
while (Date.now() < deadline) {
|
|
2388
2646
|
await sleep(interval * 1000);
|
|
2389
|
-
let
|
|
2647
|
+
let body2;
|
|
2390
2648
|
try {
|
|
2391
|
-
|
|
2649
|
+
body2 = await (await f(`${url}/api/oauth/token`, {
|
|
2392
2650
|
method: "POST",
|
|
2393
2651
|
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
2394
2652
|
body: new URLSearchParams({
|
|
@@ -2399,14 +2657,14 @@ async function pollDeviceLogin(url, start, opts = {}) {
|
|
|
2399
2657
|
} catch {
|
|
2400
2658
|
continue;
|
|
2401
2659
|
}
|
|
2402
|
-
if (typeof
|
|
2660
|
+
if (typeof body2.access_token === "string")
|
|
2403
2661
|
return {
|
|
2404
2662
|
ok: !0,
|
|
2405
|
-
token:
|
|
2406
|
-
url: typeof
|
|
2407
|
-
scope: String(
|
|
2663
|
+
token: body2.access_token,
|
|
2664
|
+
url: typeof body2.workspace_url === "string" ? body2.workspace_url : url,
|
|
2665
|
+
scope: String(body2.scope ?? "")
|
|
2408
2666
|
};
|
|
2409
|
-
switch (
|
|
2667
|
+
switch (body2.error) {
|
|
2410
2668
|
case "authorization_pending":
|
|
2411
2669
|
continue;
|
|
2412
2670
|
case "slow_down":
|
|
@@ -2428,7 +2686,7 @@ async function pollDeviceLogin(url, start, opts = {}) {
|
|
|
2428
2686
|
return {
|
|
2429
2687
|
ok: !1,
|
|
2430
2688
|
reason: "error",
|
|
2431
|
-
message: String(
|
|
2689
|
+
message: String(body2.error_description ?? body2.error ?? "Sign-in failed.")
|
|
2432
2690
|
};
|
|
2433
2691
|
}
|
|
2434
2692
|
}
|
|
@@ -2497,7 +2755,7 @@ async function runMcpProxy(auth, fetchImpl = fetch) {
|
|
|
2497
2755
|
}
|
|
2498
2756
|
process.stdout.write(`${text}
|
|
2499
2757
|
`);
|
|
2500
|
-
} catch (
|
|
2758
|
+
} catch (e2) {
|
|
2501
2759
|
if (id === null)
|
|
2502
2760
|
return;
|
|
2503
2761
|
write({
|
|
@@ -2505,7 +2763,7 @@ async function runMcpProxy(auth, fetchImpl = fetch) {
|
|
|
2505
2763
|
id,
|
|
2506
2764
|
error: {
|
|
2507
2765
|
code: -32000,
|
|
2508
|
-
message: `Could not reach LopeBase at ${endpoint}: ${String(
|
|
2766
|
+
message: `Could not reach LopeBase at ${endpoint}: ${String(e2)}`
|
|
2509
2767
|
}
|
|
2510
2768
|
});
|
|
2511
2769
|
}
|
|
@@ -2523,7 +2781,7 @@ function onboardingPrompt(input) {
|
|
|
2523
2781
|
`- Stack: ${stack}`,
|
|
2524
2782
|
`- Already in use: ${found.map((s) => s.label).join(", ") || "none of the sources LopeBase reads"}`,
|
|
2525
2783
|
`- The human wants connected: ${chosen.join(", ") || "whatever get_onboarding_status recommends"}`,
|
|
2526
|
-
`- Worth watching for uptime: ${(input.watch ?? []).map((
|
|
2784
|
+
`- Worth watching for uptime: ${(input.watch ?? []).map((w2) => `${w2.label} (${w2.how === "address" ? "uptime check on its address" : "adapter health service"})`).join(", ") || "the site, if it has one"}`,
|
|
2527
2785
|
`- LopeBase pageviews: ${input.pageviews ? "yes, the human wants them" : "not chosen; offer once, do not push"}`,
|
|
2528
2786
|
"",
|
|
2529
2787
|
"How to work:",
|
|
@@ -2541,7 +2799,7 @@ function onboardingPrompt(input) {
|
|
|
2541
2799
|
}
|
|
2542
2800
|
|
|
2543
2801
|
// src/scan.ts
|
|
2544
|
-
import { existsSync as existsSync2, readdirSync, readFileSync as
|
|
2802
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync2 } from "node:fs";
|
|
2545
2803
|
import path2 from "node:path";
|
|
2546
2804
|
var MONOREPO_DIRS = ["apps", "packages", "services", "sites", "web", "site"], SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2547
2805
|
"node_modules",
|
|
@@ -2597,15 +2855,15 @@ var MONOREPO_DIRS = ["apps", "packages", "services", "sites", "web", "site"], SK
|
|
|
2597
2855
|
".github/workflows",
|
|
2598
2856
|
"content/config.ts"
|
|
2599
2857
|
], EXAMPLE_ENV = /^(\.env\.(example|sample|template|dist|local\.example|development\.example|production\.example)|example\.env|\.dev\.vars\.example)$/;
|
|
2600
|
-
function readText(
|
|
2858
|
+
function readText(file2) {
|
|
2601
2859
|
try {
|
|
2602
|
-
return
|
|
2860
|
+
return readFileSync3(file2, "utf8");
|
|
2603
2861
|
} catch {
|
|
2604
2862
|
return null;
|
|
2605
2863
|
}
|
|
2606
2864
|
}
|
|
2607
|
-
function readJson(
|
|
2608
|
-
let text = readText(
|
|
2865
|
+
function readJson(file2) {
|
|
2866
|
+
let text = readText(file2);
|
|
2609
2867
|
if (text === null)
|
|
2610
2868
|
return null;
|
|
2611
2869
|
try {
|
|
@@ -2614,16 +2872,16 @@ function readJson(file) {
|
|
|
2614
2872
|
return null;
|
|
2615
2873
|
}
|
|
2616
2874
|
}
|
|
2617
|
-
function isDir(
|
|
2875
|
+
function isDir(p2) {
|
|
2618
2876
|
try {
|
|
2619
|
-
return
|
|
2877
|
+
return statSync2(p2).isDirectory();
|
|
2620
2878
|
} catch {
|
|
2621
2879
|
return !1;
|
|
2622
2880
|
}
|
|
2623
2881
|
}
|
|
2624
2882
|
function listDir(dir) {
|
|
2625
2883
|
try {
|
|
2626
|
-
return
|
|
2884
|
+
return readdirSync2(dir);
|
|
2627
2885
|
} catch {
|
|
2628
2886
|
return [];
|
|
2629
2887
|
}
|
|
@@ -2641,14 +2899,14 @@ function isPackage(dir) {
|
|
|
2641
2899
|
}
|
|
2642
2900
|
function workspaceGlobs(root) {
|
|
2643
2901
|
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 : [];
|
|
2644
|
-
for (let
|
|
2645
|
-
if (typeof
|
|
2646
|
-
globs.push(
|
|
2902
|
+
for (let g2 of list)
|
|
2903
|
+
if (typeof g2 === "string")
|
|
2904
|
+
globs.push(g2);
|
|
2647
2905
|
let pnpm = readText(path2.join(root, "pnpm-workspace.yaml"));
|
|
2648
2906
|
if (pnpm)
|
|
2649
|
-
for (let
|
|
2650
|
-
globs.push(
|
|
2651
|
-
return globs.filter((
|
|
2907
|
+
for (let m2 of pnpm.matchAll(/^\s*-\s*["']?([^"'\s#]+)["']?/gm))
|
|
2908
|
+
globs.push(m2[1]);
|
|
2909
|
+
return globs.filter((g2) => !g2.startsWith("!"));
|
|
2652
2910
|
}
|
|
2653
2911
|
function expandGlob(root, glob) {
|
|
2654
2912
|
let clean = glob.replace(/\/\*\*$/, "/*").replace(/\/$/, "");
|
|
@@ -2657,24 +2915,24 @@ function expandGlob(root, glob) {
|
|
|
2657
2915
|
return isDir(dir) ? [dir] : [];
|
|
2658
2916
|
}
|
|
2659
2917
|
let [base] = clean.split("*"), parent = path2.join(root, base);
|
|
2660
|
-
return listDir(parent).filter((
|
|
2918
|
+
return listDir(parent).filter((e2) => !SKIP_DIRS.has(e2) && !e2.startsWith(".")).map((e2) => path2.join(parent, e2)).filter(isDir);
|
|
2661
2919
|
}
|
|
2662
2920
|
function packageDirs(root) {
|
|
2663
2921
|
let dirs = /* @__PURE__ */ new Set([root]);
|
|
2664
|
-
for (let
|
|
2665
|
-
for (let
|
|
2666
|
-
if (isPackage(
|
|
2667
|
-
dirs.add(
|
|
2922
|
+
for (let g2 of workspaceGlobs(root))
|
|
2923
|
+
for (let d3 of expandGlob(root, g2))
|
|
2924
|
+
if (isPackage(d3))
|
|
2925
|
+
dirs.add(d3);
|
|
2668
2926
|
for (let group of MONOREPO_DIRS) {
|
|
2669
|
-
let
|
|
2670
|
-
if (!isDir(
|
|
2927
|
+
let g2 = path2.join(root, group);
|
|
2928
|
+
if (!isDir(g2))
|
|
2671
2929
|
continue;
|
|
2672
|
-
if (isPackage(
|
|
2673
|
-
dirs.add(
|
|
2674
|
-
for (let entry of listDir(
|
|
2675
|
-
let
|
|
2676
|
-
if (!SKIP_DIRS.has(entry) && isDir(
|
|
2677
|
-
dirs.add(
|
|
2930
|
+
if (isPackage(g2))
|
|
2931
|
+
dirs.add(g2);
|
|
2932
|
+
for (let entry of listDir(g2)) {
|
|
2933
|
+
let d3 = path2.join(g2, entry);
|
|
2934
|
+
if (!SKIP_DIRS.has(entry) && isDir(d3) && isPackage(d3))
|
|
2935
|
+
dirs.add(d3);
|
|
2678
2936
|
}
|
|
2679
2937
|
}
|
|
2680
2938
|
return [...dirs].slice(0, MAX_PACKAGES);
|
|
@@ -2682,36 +2940,36 @@ function packageDirs(root) {
|
|
|
2682
2940
|
function pythonRequirements(text) {
|
|
2683
2941
|
let out = [];
|
|
2684
2942
|
for (let line of text.split(/\r?\n/)) {
|
|
2685
|
-
let
|
|
2686
|
-
if (
|
|
2687
|
-
out.push(
|
|
2943
|
+
let m2 = /^\s*([A-Za-z0-9][A-Za-z0-9._-]*)/.exec(line.replace(/#.*/, ""));
|
|
2944
|
+
if (m2 && !line.trim().startsWith("-"))
|
|
2945
|
+
out.push(m2[1].toLowerCase());
|
|
2688
2946
|
}
|
|
2689
2947
|
return out;
|
|
2690
2948
|
}
|
|
2691
2949
|
function pythonProjectDeps(text) {
|
|
2692
2950
|
let out = /* @__PURE__ */ new Set;
|
|
2693
2951
|
for (let block of text.matchAll(/(?:^|\n)\s*dependencies\s*=\s*\[([\s\S]*?)\]/g))
|
|
2694
|
-
for (let
|
|
2695
|
-
out.add(
|
|
2952
|
+
for (let m2 of block[1].matchAll(/["']([A-Za-z0-9][A-Za-z0-9._-]*)/g))
|
|
2953
|
+
out.add(m2[1].toLowerCase());
|
|
2696
2954
|
for (let table of text.matchAll(/\[(?:tool\.poetry\.(?:group\.[a-z]+\.)?dependencies|(?:dev-)?packages)\]([\s\S]*?)(?=\n\[|$)/g))
|
|
2697
|
-
for (let
|
|
2698
|
-
out.add(
|
|
2955
|
+
for (let m2 of table[1].matchAll(/^\s*([A-Za-z0-9][A-Za-z0-9._-]*)\s*=/gm))
|
|
2956
|
+
out.add(m2[1].toLowerCase());
|
|
2699
2957
|
return out.delete("python"), [...out];
|
|
2700
2958
|
}
|
|
2701
2959
|
function gemfileDeps(text) {
|
|
2702
|
-
return [...text.matchAll(/^\s*gem\s+["']([^"']+)["']/gm)].map((
|
|
2960
|
+
return [...text.matchAll(/^\s*gem\s+["']([^"']+)["']/gm)].map((m2) => m2[1]);
|
|
2703
2961
|
}
|
|
2704
2962
|
function goModDeps(text) {
|
|
2705
2963
|
let out = [];
|
|
2706
|
-
for (let
|
|
2707
|
-
out.push(
|
|
2964
|
+
for (let m2 of text.matchAll(/^\s*(?:require\s+)?([a-z0-9.-]+\.[a-z]+\/[^\s]+)\s+v[0-9]/gm))
|
|
2965
|
+
out.push(m2[1]);
|
|
2708
2966
|
return out;
|
|
2709
2967
|
}
|
|
2710
2968
|
function cargoDeps(text) {
|
|
2711
2969
|
let out = [];
|
|
2712
2970
|
for (let table of text.matchAll(/\[(?:dev-|build-)?dependencies\]([\s\S]*?)(?=\n\[|$)/g))
|
|
2713
|
-
for (let
|
|
2714
|
-
out.push(
|
|
2971
|
+
for (let m2 of table[1].matchAll(/^\s*([A-Za-z0-9_-]+)\s*=/gm))
|
|
2972
|
+
out.push(m2[1]);
|
|
2715
2973
|
return out;
|
|
2716
2974
|
}
|
|
2717
2975
|
function manifestDeps(dir) {
|
|
@@ -2730,10 +2988,10 @@ function manifestDeps(dir) {
|
|
|
2730
2988
|
if (gem)
|
|
2731
2989
|
add("gem", gemfileDeps(gem));
|
|
2732
2990
|
let composer = readJson(path2.join(dir, "composer.json"));
|
|
2733
|
-
for (let
|
|
2734
|
-
let
|
|
2735
|
-
if (
|
|
2736
|
-
add("composer", Object.keys(
|
|
2991
|
+
for (let k3 of ["require", "require-dev"]) {
|
|
2992
|
+
let d3 = composer?.[k3];
|
|
2993
|
+
if (d3 && typeof d3 === "object")
|
|
2994
|
+
add("composer", Object.keys(d3));
|
|
2737
2995
|
}
|
|
2738
2996
|
let gomod = readText(path2.join(dir, "go.mod"));
|
|
2739
2997
|
if (gomod)
|
|
@@ -2747,7 +3005,7 @@ function siteFromWrangler(text) {
|
|
|
2747
3005
|
let first = [
|
|
2748
3006
|
...text.matchAll(/(?:pattern|route)["']?\s*[:=]\s*["']([^"'*/\s]+)/g),
|
|
2749
3007
|
...text.matchAll(/custom_domain["']?\s*[:=]\s*["']([^"'*/\s]+)/g)
|
|
2750
|
-
].map((
|
|
3008
|
+
].map((m2) => m2[1].toLowerCase()).filter((h2) => h2.includes(".") && !h2.endsWith("workers.dev"))[0];
|
|
2751
3009
|
if (!first)
|
|
2752
3010
|
return;
|
|
2753
3011
|
return `https://${first.replace(/^(app|www|api|console)\./, "")}`;
|
|
@@ -2755,26 +3013,26 @@ function siteFromWrangler(text) {
|
|
|
2755
3013
|
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)$/;
|
|
2756
3014
|
function publicSite(url) {
|
|
2757
3015
|
try {
|
|
2758
|
-
let
|
|
2759
|
-
if (!
|
|
3016
|
+
let u2 = new URL(url.startsWith("http") ? url : `https://${url}`);
|
|
3017
|
+
if (!u2.hostname.includes(".") || NOT_A_SITE.test(u2.hostname))
|
|
2760
3018
|
return;
|
|
2761
|
-
return `https://${
|
|
3019
|
+
return `https://${u2.hostname.replace(/^www\./, "")}`;
|
|
2762
3020
|
} catch {
|
|
2763
3021
|
return;
|
|
2764
3022
|
}
|
|
2765
3023
|
}
|
|
2766
3024
|
function siteFromConfig(text) {
|
|
2767
|
-
let
|
|
2768
|
-
return
|
|
3025
|
+
let m2 = /\b(?:site|siteUrl|baseURL|baseUrl|url)\s*[:=]\s*["'`](https?:\/\/[^"'`\s]+)["'`]/.exec(text);
|
|
3026
|
+
return m2 ? publicSite(m2[1]) : void 0;
|
|
2769
3027
|
}
|
|
2770
3028
|
function githubRepo(gitConfig) {
|
|
2771
3029
|
let remotes = [
|
|
2772
3030
|
...gitConfig.matchAll(/\[remote "([^"]+)"\][^[]*?url\s*=\s*(\S+)/g)
|
|
2773
|
-
].sort((a,
|
|
3031
|
+
].sort((a, b3) => Number(b3[1] === "origin") - Number(a[1] === "origin"));
|
|
2774
3032
|
for (let [, , url] of remotes) {
|
|
2775
|
-
let
|
|
2776
|
-
if (
|
|
2777
|
-
return `${
|
|
3033
|
+
let m2 = /github\.com[:/]([\w.-]+)\/([\w.-]+?)(?:\.git)?\/?$/.exec(url);
|
|
3034
|
+
if (m2)
|
|
3035
|
+
return `${m2[1]}/${m2[2]}`;
|
|
2778
3036
|
}
|
|
2779
3037
|
return;
|
|
2780
3038
|
}
|
|
@@ -2789,21 +3047,21 @@ function readGitConfig(root) {
|
|
|
2789
3047
|
return readText(path2.join(gitDir, "config")) ?? readText(path2.join(gitDir, "..", "..", "config"));
|
|
2790
3048
|
}
|
|
2791
3049
|
function siteFromHtml(html) {
|
|
2792
|
-
let
|
|
2793
|
-
return
|
|
3050
|
+
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);
|
|
3051
|
+
return m2 ? publicSite(m2[1]) : void 0;
|
|
2794
3052
|
}
|
|
2795
3053
|
function envKeyNames(text) {
|
|
2796
3054
|
let out = [];
|
|
2797
3055
|
for (let line of text.split(/\r?\n/)) {
|
|
2798
|
-
let
|
|
2799
|
-
if (
|
|
2800
|
-
out.push(
|
|
3056
|
+
let m2 = /^\s*(?:#\s*)?(?:export\s+)?([A-Z][A-Z0-9_]*)\s*=/.exec(line);
|
|
3057
|
+
if (m2)
|
|
3058
|
+
out.push(m2[1]);
|
|
2801
3059
|
}
|
|
2802
3060
|
return out;
|
|
2803
3061
|
}
|
|
2804
3062
|
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+)$/;
|
|
2805
3063
|
function scanProject(root, onProgress) {
|
|
2806
|
-
let deps = /* @__PURE__ */ new Set, files = /* @__PURE__ */ new Set, envKeys = /* @__PURE__ */ new Set, name, siteUrl, dirs = packageDirs(root), relDir = (
|
|
3064
|
+
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("/") || ".";
|
|
2807
3065
|
onProgress?.({
|
|
2808
3066
|
label: `Found ${dirs.length} ${dirs.length === 1 ? "package" : "packages"}`,
|
|
2809
3067
|
detail: dirs.length > 1 ? dirs.map(relDir).join(", ") : void 0
|
|
@@ -2816,22 +3074,22 @@ function scanProject(root, onProgress) {
|
|
|
2816
3074
|
name = pkg.name;
|
|
2817
3075
|
if (!siteUrl && typeof pkg.homepage === "string")
|
|
2818
3076
|
siteUrl = publicSite(pkg.homepage);
|
|
2819
|
-
for (let
|
|
2820
|
-
let
|
|
2821
|
-
if (
|
|
2822
|
-
for (let n of Object.keys(
|
|
3077
|
+
for (let k3 of ["dependencies", "devDependencies"]) {
|
|
3078
|
+
let d3 = pkg[k3];
|
|
3079
|
+
if (d3 && typeof d3 === "object")
|
|
3080
|
+
for (let n of Object.keys(d3))
|
|
2823
3081
|
deps.add(n);
|
|
2824
3082
|
}
|
|
2825
3083
|
}
|
|
2826
|
-
for (let
|
|
2827
|
-
deps.add(
|
|
3084
|
+
for (let d3 of manifestDeps(dir))
|
|
3085
|
+
deps.add(d3);
|
|
2828
3086
|
for (let entry of listDir(dir)) {
|
|
2829
3087
|
if (EXAMPLE_ENV.test(entry)) {
|
|
2830
3088
|
let text = readText(path2.join(dir, entry));
|
|
2831
3089
|
if (text) {
|
|
2832
3090
|
envFiles++;
|
|
2833
|
-
for (let
|
|
2834
|
-
envKeys.add(
|
|
3091
|
+
for (let k3 of envKeyNames(text))
|
|
3092
|
+
envKeys.add(k3);
|
|
2835
3093
|
}
|
|
2836
3094
|
}
|
|
2837
3095
|
if (!MARKERS.some((re) => re.test(entry)))
|
|
@@ -2902,8 +3160,8 @@ function paint(rgb, text, bg = !1) {
|
|
|
2902
3160
|
return text;
|
|
2903
3161
|
if (!trueColor())
|
|
2904
3162
|
return bg ? import_picocolors3.default.bgMagenta(import_picocolors3.default.black(text)) : import_picocolors3.default.magenta(text);
|
|
2905
|
-
let [
|
|
2906
|
-
return `\x1B[${bg ? 48 : 38};2;${
|
|
3163
|
+
let [r2, g2, b3] = rgb;
|
|
3164
|
+
return `\x1B[${bg ? 48 : 38};2;${r2};${g2};${b3}m${text}\x1B[${bg ? 49 : 39}m`;
|
|
2907
3165
|
}
|
|
2908
3166
|
var brand = (text) => paint(BRAND, text), badge = (text) => paint(BRAND, import_picocolors3.default.black(import_picocolors3.default.bold(` ${text} `)), !0), ramp = (i, text) => paint(RAMP[Math.min(i, RAMP.length - 1)], text), MARK = [
|
|
2909
3167
|
" ▗▖ ▗▖ ",
|
|
@@ -2918,7 +3176,7 @@ var brand = (text) => paint(BRAND, text), badge = (text) => paint(BRAND, import_
|
|
|
2918
3176
|
function motionAllowed(argv = process.argv, env = process.env, isTTY = Boolean(process.stdout.isTTY)) {
|
|
2919
3177
|
return isTTY && !env.CI && !env.LOPEBASE_NO_MOTION && env.TERM !== "dumb" && !argv.includes("--no-motion");
|
|
2920
3178
|
}
|
|
2921
|
-
var sleep = (ms) => new Promise((
|
|
3179
|
+
var sleep = (ms) => new Promise((r2) => setTimeout(r2, ms));
|
|
2922
3180
|
function logoLines(opts) {
|
|
2923
3181
|
let right = {
|
|
2924
3182
|
3: `${import_picocolors3.default.bold("Lope")}${brand("Base")}${opts.version ? import_picocolors3.default.dim(` v${opts.version}`) : ""}`,
|
|
@@ -2939,7 +3197,7 @@ ${lines.join(`
|
|
|
2939
3197
|
`);
|
|
2940
3198
|
return;
|
|
2941
3199
|
}
|
|
2942
|
-
let blank = MARK.map((
|
|
3200
|
+
let blank = MARK.map((r2) => " ".repeat(r2.length)), frame = (shown) => MARK.map((row, i) => shown.has(i) ? HORIZON.has(i) ? import_picocolors3.default.dim(ramp(i, row)) : ramp(i, row) : blank[i]), up = `\x1B[${MARK.length}A`, onInterrupt = () => {
|
|
2943
3201
|
write(`\x1B[?25h
|
|
2944
3202
|
`), process.exit(130);
|
|
2945
3203
|
};
|
|
@@ -2993,8 +3251,8 @@ function stackRows(detection, facts) {
|
|
|
2993
3251
|
rows.push(["Repo", facts.repo]);
|
|
2994
3252
|
if (!rows.length)
|
|
2995
3253
|
return [import_picocolors3.default.dim("Nothing recognized yet. Your agent will look closer.")];
|
|
2996
|
-
let width = Math.max(...rows.map(([
|
|
2997
|
-
return rows.map(([
|
|
3254
|
+
let width = Math.max(...rows.map(([k3]) => k3.length));
|
|
3255
|
+
return rows.map(([k3, v2]) => `${import_picocolors3.default.dim(k3.padEnd(width))} ${v2}`);
|
|
2998
3256
|
}
|
|
2999
3257
|
var PLAN = [
|
|
3000
3258
|
{
|
|
@@ -3019,13 +3277,13 @@ function planLines() {
|
|
|
3019
3277
|
${import_picocolors3.default.dim(s.detail)}`);
|
|
3020
3278
|
}
|
|
3021
3279
|
function stepRail(n, total = PLAN.length) {
|
|
3022
|
-
return Array.from({ length: total }, (
|
|
3280
|
+
return Array.from({ length: total }, (_3, i) => i < n ? brand("◆") : import_picocolors3.default.dim("◇")).join(import_picocolors3.default.dim(" ── "));
|
|
3023
3281
|
}
|
|
3024
3282
|
function stepTitle(n) {
|
|
3025
3283
|
return `${stepRail(n)} ${import_picocolors3.default.dim(`Step ${n} of ${PLAN.length}`)} ${import_picocolors3.default.bold(PLAN[n - 1].title)}`;
|
|
3026
3284
|
}
|
|
3027
3285
|
async function horizon(opts) {
|
|
3028
|
-
let write = opts.write ?? ((s) => process.stdout.write(s)), width = opts.width ?? 28, line = (n) => Array.from({ length: n }, (
|
|
3286
|
+
let write = opts.write ?? ((s) => process.stdout.write(s)), width = opts.width ?? 28, line = (n) => Array.from({ length: n }, (_3, i) => ramp(Math.floor(i / width * RAMP.length), "━")).join("");
|
|
3029
3287
|
if (!opts.motion) {
|
|
3030
3288
|
write(` ${line(width)}
|
|
3031
3289
|
`);
|
|
@@ -3037,15 +3295,15 @@ async function horizon(opts) {
|
|
|
3037
3295
|
`);
|
|
3038
3296
|
}
|
|
3039
3297
|
function watchRows(items) {
|
|
3040
|
-
return items.map((
|
|
3298
|
+
return items.map((w2) => w2.how === "address" ? `${brand("◉")} ${import_picocolors3.default.bold(w2.label)} ${import_picocolors3.default.dim("uptime check, every 15 minutes")}` : `${brand("○")} ${import_picocolors3.default.bold(w2.label)} ${import_picocolors3.default.dim("a service in the adapter's health list")}`);
|
|
3041
3299
|
}
|
|
3042
3300
|
|
|
3043
3301
|
// src/update.ts
|
|
3044
|
-
import { readFileSync as
|
|
3302
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
3045
3303
|
import path3 from "node:path";
|
|
3046
|
-
var readJson2 = (
|
|
3304
|
+
var readJson2 = (file2) => {
|
|
3047
3305
|
try {
|
|
3048
|
-
return JSON.parse(
|
|
3306
|
+
return JSON.parse(readFileSync4(file2, "utf8"));
|
|
3049
3307
|
} catch {
|
|
3050
3308
|
return null;
|
|
3051
3309
|
}
|
|
@@ -3062,7 +3320,7 @@ function findAdapterInstalls(root) {
|
|
|
3062
3320
|
}["@lopebase/adapter"];
|
|
3063
3321
|
if (!range || range.startsWith("workspace:"))
|
|
3064
3322
|
continue;
|
|
3065
|
-
let installed = [dir, root].map((
|
|
3323
|
+
let installed = [dir, root].map((d3) => readJson2(path3.join(d3, "node_modules/@lopebase/adapter/package.json"))).find((p2) => typeof p2?.version === "string")?.version;
|
|
3066
3324
|
out.push({ dir: path3.relative(root, dir) || ".", range, installed });
|
|
3067
3325
|
}
|
|
3068
3326
|
return out;
|
|
@@ -3079,8 +3337,8 @@ async function latestOnNpm(name, timeoutMs = 1500, fetchImpl = fetch) {
|
|
|
3079
3337
|
});
|
|
3080
3338
|
if (!res.ok)
|
|
3081
3339
|
return;
|
|
3082
|
-
let
|
|
3083
|
-
return typeof
|
|
3340
|
+
let body2 = await res.json();
|
|
3341
|
+
return typeof body2.version === "string" ? body2.version : void 0;
|
|
3084
3342
|
} catch {
|
|
3085
3343
|
return;
|
|
3086
3344
|
}
|
|
@@ -3099,6 +3357,8 @@ ${import_picocolors4.default.bold("Usage")}
|
|
|
3099
3357
|
npx lopebase mcp run the LopeBase tools as a stdio MCP server
|
|
3100
3358
|
npx lopebase adapter secret print a new @lopebase/adapter signing secret
|
|
3101
3359
|
npx lopebase adapter check <url> test a running adapter (reads LOPEBASE_SIGNING_SECRET)
|
|
3360
|
+
npx lopebase blog check [dir] check the blog posts LopeBase publishes to your repo
|
|
3361
|
+
(--format <preset>, --allow-empty)
|
|
3102
3362
|
|
|
3103
3363
|
${import_picocolors4.default.bold("Options")}
|
|
3104
3364
|
--print print the agent prompt instead of starting an agent
|
|
@@ -3114,7 +3374,7 @@ ${import_picocolors4.default.bold("Learn more")}
|
|
|
3114
3374
|
}, cwd = process.cwd(), motion = motionAllowed(), TAGLINE = "Every product, one place.", PAGEVIEWS = "LopeBase pageviews";
|
|
3115
3375
|
function version() {
|
|
3116
3376
|
try {
|
|
3117
|
-
return JSON.parse(
|
|
3377
|
+
return JSON.parse(readFileSync5(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
3118
3378
|
} catch {
|
|
3119
3379
|
return;
|
|
3120
3380
|
}
|
|
@@ -3151,12 +3411,12 @@ async function checkToken(auth) {
|
|
|
3151
3411
|
});
|
|
3152
3412
|
if (res.status === 401)
|
|
3153
3413
|
return { ok: !1, message: "That token is not valid or has expired." };
|
|
3154
|
-
let
|
|
3155
|
-
return { ok: !0, products: JSON.parse(
|
|
3156
|
-
} catch (
|
|
3414
|
+
let body2 = await res.json();
|
|
3415
|
+
return { ok: !0, products: JSON.parse(body2.result?.content?.[0]?.text ?? "[]").length };
|
|
3416
|
+
} catch (e2) {
|
|
3157
3417
|
return {
|
|
3158
3418
|
ok: !1,
|
|
3159
|
-
message: `Could not reach ${auth.url} (${String(
|
|
3419
|
+
message: `Could not reach ${auth.url} (${String(e2)}).`
|
|
3160
3420
|
};
|
|
3161
3421
|
}
|
|
3162
3422
|
}
|
|
@@ -3184,8 +3444,8 @@ async function login() {
|
|
|
3184
3444
|
s.start("Waiting for you to approve in the browser");
|
|
3185
3445
|
let res = await pollDeviceLogin(url, device);
|
|
3186
3446
|
if (res.ok) {
|
|
3187
|
-
let
|
|
3188
|
-
return s.stop(`Connected to ${import_picocolors4.default.bold(res.url)}. Saved to ${import_picocolors4.default.dim(
|
|
3447
|
+
let file2 = saveAuth({ url: res.url, token: res.token });
|
|
3448
|
+
return s.stop(`Connected to ${import_picocolors4.default.bold(res.url)}. Saved to ${import_picocolors4.default.dim(file2)}`), { url: res.url, token: res.token };
|
|
3189
3449
|
}
|
|
3190
3450
|
if (s.stop(import_picocolors4.default.red(res.message)), res.reason === "denied")
|
|
3191
3451
|
bail("Nothing was connected.");
|
|
@@ -3198,7 +3458,7 @@ async function askWorkspace() {
|
|
|
3198
3458
|
return workspaceUrl(guard(await he({
|
|
3199
3459
|
message: "Your LopeBase workspace (name or address)",
|
|
3200
3460
|
placeholder: "yourteam or https://yourteam.lopebase.com",
|
|
3201
|
-
validate: (
|
|
3461
|
+
validate: (v2) => workspaceUrl(v2 ?? "") ? void 0 : "Type your workspace name, like yourteam, or its full address"
|
|
3202
3462
|
})));
|
|
3203
3463
|
}
|
|
3204
3464
|
async function pasteLogin(url) {
|
|
@@ -3212,33 +3472,33 @@ async function pasteLogin(url) {
|
|
|
3212
3472
|
for (;; ) {
|
|
3213
3473
|
let token = guard(await ge({
|
|
3214
3474
|
message: "Paste the agent token",
|
|
3215
|
-
validate: (
|
|
3475
|
+
validate: (v2) => v2?.startsWith("lb_agent_") ? void 0 : "Tokens start with lb_agent_"
|
|
3216
3476
|
})).trim(), s = Y2();
|
|
3217
3477
|
s.start("Checking the token");
|
|
3218
3478
|
let check = await checkToken({ url, token });
|
|
3219
3479
|
if (check.ok) {
|
|
3220
|
-
let
|
|
3221
|
-
return s.stop(`Signed in. ${check.products} ${check.products === 1 ? "property" : "properties"} in this workspace. Saved to ${import_picocolors4.default.dim(
|
|
3480
|
+
let file2 = saveAuth({ url, token });
|
|
3481
|
+
return s.stop(`Signed in. ${check.products} ${check.products === 1 ? "property" : "properties"} in this workspace. Saved to ${import_picocolors4.default.dim(file2)}`), { url, token };
|
|
3222
3482
|
}
|
|
3223
3483
|
s.stop(import_picocolors4.default.red(check.message ?? "That did not work."));
|
|
3224
3484
|
}
|
|
3225
3485
|
}
|
|
3226
|
-
async function printDetection(
|
|
3227
|
-
Me(stackRows(
|
|
3486
|
+
async function printDetection(d3, facts) {
|
|
3487
|
+
Me(stackRows(d3, facts).join(`
|
|
3228
3488
|
`), "Your stack"), await pace(motion, 150);
|
|
3229
|
-
let found =
|
|
3489
|
+
let found = d3.sources.filter((s) => s.status === "found");
|
|
3230
3490
|
if (found.length)
|
|
3231
3491
|
Me(found.map((s) => `${import_picocolors4.default.green("●")} ${import_picocolors4.default.bold(s.label)} ${import_picocolors4.default.dim(s.evidence.join(", "))}`).join(`
|
|
3232
3492
|
`), "Already in your code, ready to connect"), await pace(motion, 150);
|
|
3233
|
-
let possible =
|
|
3493
|
+
let possible = d3.sources.filter((s) => s.status === "possible");
|
|
3234
3494
|
if (possible.length)
|
|
3235
3495
|
Me(possible.map((s) => `${import_picocolors4.default.yellow("?")} ${import_picocolors4.default.bold(s.label)} ${import_picocolors4.default.dim(`only ${s.evidence.join(", ")}`)}`).join(`
|
|
3236
3496
|
`), "Maybe in use, your agent will check"), await pace(motion, 150);
|
|
3237
|
-
let watch = watchList(
|
|
3497
|
+
let watch = watchList(d3, facts);
|
|
3238
3498
|
if (watch.length)
|
|
3239
3499
|
Me(watchRows(watch).join(`
|
|
3240
3500
|
`), "Worth watching for uptime"), await pace(motion, 150);
|
|
3241
|
-
for (let gap of
|
|
3501
|
+
for (let gap of d3.gaps)
|
|
3242
3502
|
M2.info(import_picocolors4.default.dim(gap));
|
|
3243
3503
|
}
|
|
3244
3504
|
async function scanWithProgress() {
|
|
@@ -3300,7 +3560,7 @@ async function init() {
|
|
|
3300
3560
|
label: src.status === "found" ? src.label : src.status === "possible" ? `${src.label} ${import_picocolors4.default.dim("(maybe in use, we'll check)")}` : `${src.label} ${import_picocolors4.default.dim("(not set up yet, we'll help)")}`,
|
|
3301
3561
|
hint: src.why
|
|
3302
3562
|
})),
|
|
3303
|
-
...watchList(detection, facts).some((
|
|
3563
|
+
...watchList(detection, facts).some((w2) => w2.how === "address") ? [
|
|
3304
3564
|
{
|
|
3305
3565
|
value: PAGEVIEWS,
|
|
3306
3566
|
label: `${PAGEVIEWS} ${import_picocolors4.default.dim("(one tag, no cookies)")}`,
|
|
@@ -3324,7 +3584,7 @@ async function init() {
|
|
|
3324
3584
|
`${import_picocolors4.default.bold(agent)} will now:`,
|
|
3325
3585
|
` ${brand("1.")} add ${import_picocolors4.default.bold(productName || "this product")} to your workspace`,
|
|
3326
3586
|
` ${brand("2.")} connect ${chosen.length ? chosen.join(", ") : "what it recommends"}`,
|
|
3327
|
-
` ${brand("3.")} pick what to watch for uptime: ${watchList(detection, facts).map((
|
|
3587
|
+
` ${brand("3.")} pick what to watch for uptime: ${watchList(detection, facts).map((w2) => w2.label).join(", ") || "the site"}`,
|
|
3328
3588
|
` ${brand("4.")} stop and ask you for any sign-in, key, or yes`,
|
|
3329
3589
|
"",
|
|
3330
3590
|
import_picocolors4.default.dim("You can follow along in your workspace as each one turns green.")
|
|
@@ -3362,7 +3622,7 @@ async function handOff(prompt, plan) {
|
|
|
3362
3622
|
stdio: "inherit",
|
|
3363
3623
|
cwd
|
|
3364
3624
|
});
|
|
3365
|
-
child.on("error", (
|
|
3625
|
+
child.on("error", (e2) => bail(`Could not start ${agent.label}: ${e2.message}`)), child.on("exit", (code) => process.exit(code ?? 0));
|
|
3366
3626
|
}
|
|
3367
3627
|
async function noticeNewerCli() {
|
|
3368
3628
|
let mine = version(), latest = await latestOnNpm("lopebase");
|
|
@@ -3388,11 +3648,11 @@ async function update() {
|
|
|
3388
3648
|
let latest = npmLatest && isBehind(ADAPTER_VERSION, npmLatest) ? npmLatest : ADAPTER_VERSION, behind = installs.filter((i) => isBehind(runningVersion(i), latest));
|
|
3389
3649
|
if (s.stop(installs.map((i) => `${i.dir === "." ? "This folder" : i.dir} runs ${import_picocolors4.default.bold(runningVersion(i))}`).join(", ")), !behind.length)
|
|
3390
3650
|
return Se(`Up to date with ${import_picocolors4.default.bold(latest)}.`);
|
|
3391
|
-
let from = behind.map(runningVersion).sort((a,
|
|
3392
|
-
if (Me(
|
|
3393
|
-
`${brand("◆")} ${import_picocolors4.default.bold(
|
|
3394
|
-
...
|
|
3395
|
-
...(
|
|
3651
|
+
let from = behind.map(runningVersion).sort((a, b3) => isBehind(a, b3) ? -1 : 1)[0], releases2 = releasesSince(from);
|
|
3652
|
+
if (Me(releases2.flatMap((r2) => [
|
|
3653
|
+
`${brand("◆")} ${import_picocolors4.default.bold(r2.version)} ${import_picocolors4.default.dim(r2.date)}`,
|
|
3654
|
+
...r2.features.map((f) => ` ${f}`),
|
|
3655
|
+
...(r2.breaking ?? []).map((b3) => ` ${import_picocolors4.default.yellow("!")} ${b3}`)
|
|
3396
3656
|
]).join(`
|
|
3397
3657
|
`), `New since ${from}`), npmLatest && isBehind(ADAPTER_VERSION, npmLatest))
|
|
3398
3658
|
M2.warn(`npm has ${npmLatest}, newer than these notes. Run ${brand("npx lopebase@latest update")} for its steps.`);
|
|
@@ -3411,6 +3671,29 @@ The adapter is in: ${where}. If you have the lopebase MCP tools, call get_onboar
|
|
|
3411
3671
|
` ${brand("3.")} run the adapter check and your tests`
|
|
3412
3672
|
]);
|
|
3413
3673
|
}
|
|
3674
|
+
var BLOG_USAGE = `Usage:
|
|
3675
|
+
lopebase blog check [dir] [--format <preset>] [--allow-empty]
|
|
3676
|
+
|
|
3677
|
+
Presets: ${Object.entries(BLOG_PRESETS).map(([name, preset]) => `${name} (${preset.dir})`).join(", ")}. Default: markdown.`;
|
|
3678
|
+
function blog() {
|
|
3679
|
+
if (rest[0] !== "check")
|
|
3680
|
+
return console.log(BLOG_USAGE), process.exit(rest[0] ? 1 : 0);
|
|
3681
|
+
let format = option("format") ?? "markdown";
|
|
3682
|
+
if (!isBlogFormat(format))
|
|
3683
|
+
return console.error(`Unknown format "${format}".
|
|
3684
|
+
|
|
3685
|
+
${BLOG_USAGE}`), process.exit(1);
|
|
3686
|
+
let dir = rest.slice(1).find((a, i, args) => !a.startsWith("--") && args[i - 1] !== "--format"), res = checkBlog({
|
|
3687
|
+
cwd,
|
|
3688
|
+
format,
|
|
3689
|
+
dir,
|
|
3690
|
+
allowEmpty: flag("allow-empty")
|
|
3691
|
+
});
|
|
3692
|
+
for (let { file: file2, problem } of res.problems)
|
|
3693
|
+
console.log(`${file2}: ${problem}`);
|
|
3694
|
+
let posts = `${res.posts} ${res.posts === 1 ? "post" : "posts"}`;
|
|
3695
|
+
console.log(res.problems.length ? import_picocolors4.default.red(`${res.problems.length} ${res.problems.length === 1 ? "problem" : "problems"} in ${posts} in ${res.dir}.`) : import_picocolors4.default.green(`Checked ${posts} in ${res.dir}. All good.`)), process.exit(res.problems.length ? 1 : 0);
|
|
3696
|
+
}
|
|
3414
3697
|
async function main() {
|
|
3415
3698
|
switch (command) {
|
|
3416
3699
|
case "init":
|
|
@@ -3444,7 +3727,7 @@ async function main() {
|
|
|
3444
3727
|
baseUrl: rest[1],
|
|
3445
3728
|
secret,
|
|
3446
3729
|
token: secret ? void 0 : token,
|
|
3447
|
-
log: (
|
|
3730
|
+
log: (l2) => console.log(l2.startsWith("FAIL") ? import_picocolors4.default.red(l2) : import_picocolors4.default.green(l2))
|
|
3448
3731
|
});
|
|
3449
3732
|
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);
|
|
3450
3733
|
}
|
|
@@ -3452,6 +3735,8 @@ async function main() {
|
|
|
3452
3735
|
lopebase adapter secret print a new signing secret
|
|
3453
3736
|
lopebase adapter check <base-url> test a running adapter (reads LOPEBASE_SIGNING_SECRET)`), process.exit(sub ? 1 : 0);
|
|
3454
3737
|
}
|
|
3738
|
+
case "blog":
|
|
3739
|
+
return blog();
|
|
3455
3740
|
case "mcp": {
|
|
3456
3741
|
let auth = loadAuth();
|
|
3457
3742
|
if (!auth)
|