@mybase-dev/cli 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mybase.js +206 -1
- package/package.json +1 -1
package/dist/mybase.js
CHANGED
|
@@ -139,7 +139,7 @@ function parseDotenv(text) {
|
|
|
139
139
|
// package.json
|
|
140
140
|
var package_default = {
|
|
141
141
|
name: "@mybase-dev/cli",
|
|
142
|
-
version: "1.0.
|
|
142
|
+
version: "1.0.7",
|
|
143
143
|
description: "The mybase CLI: projects, keys, cron, queues, cache, rate limits, analytics, docs, and agent setup for a self-hosted mybase.",
|
|
144
144
|
type: "module",
|
|
145
145
|
repository: {
|
|
@@ -3717,6 +3717,109 @@ ${i}
|
|
|
3717
3717
|
import { join as join4 } from "path";
|
|
3718
3718
|
import { styleText as styleText4 } from "util";
|
|
3719
3719
|
|
|
3720
|
+
// ../../packages/shared/src/logger.ts
|
|
3721
|
+
var LEVELS = { debug: 10, info: 20, warn: 30, error: 40 };
|
|
3722
|
+
var isLogLevel = (value) => (value in LEVELS);
|
|
3723
|
+
var levelAtLeast = (level, min) => LEVELS[level] >= LEVELS[min];
|
|
3724
|
+
var ansi = (code) => (s) => useColor() ? `\x1B[${code}m${s}\x1B[0m` : s;
|
|
3725
|
+
var color = {
|
|
3726
|
+
dim: ansi(2),
|
|
3727
|
+
bold: ansi(1),
|
|
3728
|
+
red: ansi(31),
|
|
3729
|
+
green: ansi(32),
|
|
3730
|
+
yellow: ansi(33),
|
|
3731
|
+
blue: ansi(34),
|
|
3732
|
+
magenta: ansi(35),
|
|
3733
|
+
cyan: ansi(36),
|
|
3734
|
+
gray: ansi(90)
|
|
3735
|
+
};
|
|
3736
|
+
var LEVEL_STYLE = {
|
|
3737
|
+
debug: color.gray,
|
|
3738
|
+
info: color.green,
|
|
3739
|
+
warn: color.yellow,
|
|
3740
|
+
error: color.red
|
|
3741
|
+
};
|
|
3742
|
+
var SERVICE_COLORS = {
|
|
3743
|
+
api: color.cyan,
|
|
3744
|
+
dashboard: color.magenta,
|
|
3745
|
+
cli: color.blue,
|
|
3746
|
+
cron: color.yellow,
|
|
3747
|
+
analytics: color.green,
|
|
3748
|
+
cache: color.red,
|
|
3749
|
+
ratelimit: color.magenta,
|
|
3750
|
+
queue: color.blue,
|
|
3751
|
+
search: color.cyan,
|
|
3752
|
+
mcp: color.green,
|
|
3753
|
+
docs: color.gray,
|
|
3754
|
+
audit: color.yellow,
|
|
3755
|
+
alerts: color.red,
|
|
3756
|
+
users: color.green,
|
|
3757
|
+
captcha: color.blue,
|
|
3758
|
+
"db-public": color.cyan,
|
|
3759
|
+
dev: color.gray
|
|
3760
|
+
};
|
|
3761
|
+
var FALLBACK_COLORS = [color.cyan, color.magenta, color.blue, color.yellow, color.green];
|
|
3762
|
+
function serviceColor(service) {
|
|
3763
|
+
const known = SERVICE_COLORS[service];
|
|
3764
|
+
if (known)
|
|
3765
|
+
return known;
|
|
3766
|
+
let hash = 0;
|
|
3767
|
+
for (const ch of service)
|
|
3768
|
+
hash = hash * 31 + ch.charCodeAt(0) >>> 0;
|
|
3769
|
+
return FALLBACK_COLORS[hash % FALLBACK_COLORS.length];
|
|
3770
|
+
}
|
|
3771
|
+
function useColor() {
|
|
3772
|
+
if (process.env.NO_COLOR)
|
|
3773
|
+
return false;
|
|
3774
|
+
if (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== "0")
|
|
3775
|
+
return true;
|
|
3776
|
+
return Boolean(process.stdout.isTTY);
|
|
3777
|
+
}
|
|
3778
|
+
function formatValue(value) {
|
|
3779
|
+
if (typeof value === "string")
|
|
3780
|
+
return /\s|=|"/.test(value) ? JSON.stringify(value) : value;
|
|
3781
|
+
if (value instanceof Error)
|
|
3782
|
+
return JSON.stringify(value.message);
|
|
3783
|
+
return JSON.stringify(value);
|
|
3784
|
+
}
|
|
3785
|
+
function formatPretty(service, level, msg, fields, at = new Date) {
|
|
3786
|
+
const time = color.dim(at.toISOString().slice(11, 23));
|
|
3787
|
+
const lvl = LEVEL_STYLE[level](level.toUpperCase().padEnd(5));
|
|
3788
|
+
const tag = process.env.MYBASE_LOG_PREFIXED ? "" : `${serviceColor(service)(`[${service}]`)} `;
|
|
3789
|
+
let errorStack = "";
|
|
3790
|
+
const pairs = [];
|
|
3791
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
3792
|
+
if (value === undefined)
|
|
3793
|
+
continue;
|
|
3794
|
+
if (value instanceof Error && value.stack)
|
|
3795
|
+
errorStack = `
|
|
3796
|
+
${color.dim(value.stack)}`;
|
|
3797
|
+
pairs.push(`${color.gray(`${key}=`)}${formatValue(value)}`);
|
|
3798
|
+
}
|
|
3799
|
+
return `${time} ${lvl} ${tag}${msg}${pairs.length ? ` ${pairs.join(" ")}` : ""}${errorStack}`;
|
|
3800
|
+
}
|
|
3801
|
+
function prettyJsonLine(line) {
|
|
3802
|
+
if (!line.startsWith("{"))
|
|
3803
|
+
return null;
|
|
3804
|
+
let entry;
|
|
3805
|
+
try {
|
|
3806
|
+
entry = JSON.parse(line);
|
|
3807
|
+
} catch {
|
|
3808
|
+
return null;
|
|
3809
|
+
}
|
|
3810
|
+
const { time, level, service, msg, ...fields } = entry;
|
|
3811
|
+
if (typeof level !== "string" || !isLogLevel(level) || typeof service !== "string" || typeof msg !== "string")
|
|
3812
|
+
return null;
|
|
3813
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
3814
|
+
const err = value;
|
|
3815
|
+
if (err && typeof err === "object" && typeof err.message === "string" && typeof err.stack === "string") {
|
|
3816
|
+
fields[key] = Object.assign(new Error(err.message), { name: String(err.name ?? "Error"), stack: err.stack });
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3819
|
+
const at = typeof time === "string" && !Number.isNaN(Date.parse(time)) ? new Date(time) : new Date;
|
|
3820
|
+
return { level, entry, text: formatPretty(service, level, msg, fields, at) };
|
|
3821
|
+
}
|
|
3822
|
+
|
|
3720
3823
|
// src/server-docker.ts
|
|
3721
3824
|
import { existsSync, readFileSync } from "fs";
|
|
3722
3825
|
import { statfs } from "fs/promises";
|
|
@@ -4278,6 +4381,35 @@ function tunnelConnections(logs) {
|
|
|
4278
4381
|
}
|
|
4279
4382
|
return [...conns.values()].sort((a, b) => a.index - b.index);
|
|
4280
4383
|
}
|
|
4384
|
+
var LOG_SERVICES = ["mybase", "postgres", "garage", "valkey", "searxng", "cloudflared"];
|
|
4385
|
+
var LOG_ALIASES = {
|
|
4386
|
+
api: "mybase",
|
|
4387
|
+
dashboard: "mybase",
|
|
4388
|
+
db: "postgres",
|
|
4389
|
+
database: "postgres",
|
|
4390
|
+
storage: "garage",
|
|
4391
|
+
s3: "garage",
|
|
4392
|
+
cache: "valkey",
|
|
4393
|
+
search: "searxng",
|
|
4394
|
+
tunnel: "cloudflared"
|
|
4395
|
+
};
|
|
4396
|
+
function logService(name) {
|
|
4397
|
+
const lower = name.toLowerCase();
|
|
4398
|
+
return LOG_SERVICES.includes(lower) ? lower : LOG_ALIASES[lower] ?? null;
|
|
4399
|
+
}
|
|
4400
|
+
function splitComposeLine(line) {
|
|
4401
|
+
const m = /^([a-z0-9][\w.-]*?)(?:[-_]\d+)?\s+\| ?(.*)$/i.exec(line);
|
|
4402
|
+
return m ? { service: m[1], text: m[2] } : null;
|
|
4403
|
+
}
|
|
4404
|
+
function guessLevel(text) {
|
|
4405
|
+
if (/\b(ERR|ERROR|FATAL|PANIC|CRIT|CRITICAL)\b|level=(error|fatal)/.test(text))
|
|
4406
|
+
return "error";
|
|
4407
|
+
if (/\b(WRN|WARN|WARNING)\b|level=warn/.test(text))
|
|
4408
|
+
return "warn";
|
|
4409
|
+
if (/\b(DBG|DEBUG)\b|level=debug/.test(text))
|
|
4410
|
+
return "debug";
|
|
4411
|
+
return "info";
|
|
4412
|
+
}
|
|
4281
4413
|
|
|
4282
4414
|
// src/server-setup.ts
|
|
4283
4415
|
import { chmod as chmod2, mkdir as mkdir3 } from "fs/promises";
|
|
@@ -5070,6 +5202,9 @@ var serverUsage = ` server setup [--dir <dir>] Install mybase on this machi
|
|
|
5070
5202
|
server status [--dir <dir>] Containers, health, and whether mybase starts on boot
|
|
5071
5203
|
server update [--dir <dir>] Download new images and restart (also refreshes compose.yml)
|
|
5072
5204
|
server restart [--dir <dir>] Restart every container (API, Postgres, storage, cache, search, tunnel)
|
|
5205
|
+
server logs [<service>\u2026] [--dir <dir>] Live logs (all services, or api|postgres|storage|cache|search|tunnel)
|
|
5206
|
+
[-f | --no-follow] [--tail <n>|all] [--since <10m|timestamp>] [--level warn] [--raw]
|
|
5207
|
+
Follows in a terminal; without one prints the tail and exits
|
|
5073
5208
|
server tunnel [--dir <dir>] Add or replace the Cloudflare Tunnel [--tunnel-token <t> --domain <d>] | --off
|
|
5074
5209
|
server database-public [--dir <dir>] A normal postgres:// URL from anywhere: router port forward +
|
|
5075
5210
|
Let's Encrypt TLS + DNS kept on your IP (Cloudflare)
|
|
@@ -5181,6 +5316,74 @@ ${res.stderr}`);
|
|
|
5181
5316
|
console.log(`mybase ${version} is running at ${localUrl(values)}`);
|
|
5182
5317
|
});
|
|
5183
5318
|
}
|
|
5319
|
+
var LOGS_VALUE_FLAGS = new Set(["--dir", "--tail", "--since", "--level"]);
|
|
5320
|
+
async function logs(args) {
|
|
5321
|
+
const dir = await serverDir(args);
|
|
5322
|
+
await loadEnv(dir);
|
|
5323
|
+
if (!await stackExists(dir))
|
|
5324
|
+
throw new CliError("no containers yet. Start them: mybase server update", 4);
|
|
5325
|
+
const services = [];
|
|
5326
|
+
for (let i = 0;i < args.length; i++) {
|
|
5327
|
+
const arg = args[i];
|
|
5328
|
+
if (LOGS_VALUE_FLAGS.has(arg))
|
|
5329
|
+
i++;
|
|
5330
|
+
if (arg.startsWith("-"))
|
|
5331
|
+
continue;
|
|
5332
|
+
const service = logService(arg);
|
|
5333
|
+
if (!service)
|
|
5334
|
+
throw usageError(`unknown service "${arg}": one of ${LOG_SERVICES.join(", ")} (or api, db, storage, cache, search, tunnel)`);
|
|
5335
|
+
if (!services.includes(service))
|
|
5336
|
+
services.push(service);
|
|
5337
|
+
}
|
|
5338
|
+
const min = flag(args, "--level") ?? "debug";
|
|
5339
|
+
if (!isLogLevel(min))
|
|
5340
|
+
throw usageError("--level is one of debug, info, warn, error");
|
|
5341
|
+
const tail = flag(args, "--tail") ?? "100";
|
|
5342
|
+
if (tail !== "all" && !/^\d+$/.test(tail))
|
|
5343
|
+
throw usageError("--tail is a number of lines, or all");
|
|
5344
|
+
const since = flag(args, "--since");
|
|
5345
|
+
const follow = args.includes("-f") || args.includes("--follow") || !args.includes("--no-follow") && !jsonMode && process.stdout.isTTY === true;
|
|
5346
|
+
const raw = args.includes("--raw");
|
|
5347
|
+
const cmd = ["docker", "compose", "logs", "--no-color", "--tail", tail, ...since ? ["--since", since] : [], ...follow ? ["--follow"] : [], ...services];
|
|
5348
|
+
const proc = Bun.spawn(cmd, { cwd: dir, stdout: "pipe", stderr: "pipe", env: composeEnv() });
|
|
5349
|
+
const colored = !jsonMode && color.red("x") !== "x";
|
|
5350
|
+
const width = Math.max(...(services.length > 0 ? services : LOG_SERVICES).map((s) => s.length));
|
|
5351
|
+
const show = (line) => {
|
|
5352
|
+
if (!colored)
|
|
5353
|
+
line = line.replace(/\x1b\[[0-9;]*m/g, "");
|
|
5354
|
+
const parts = splitComposeLine(line);
|
|
5355
|
+
if (!parts)
|
|
5356
|
+
return line && console.error(line);
|
|
5357
|
+
const ours = prettyJsonLine(parts.text);
|
|
5358
|
+
const level = ours?.level ?? guessLevel(parts.text);
|
|
5359
|
+
if (!levelAtLeast(level, min))
|
|
5360
|
+
return;
|
|
5361
|
+
if (jsonMode)
|
|
5362
|
+
console.log(JSON.stringify(ours ? { container: parts.service, ...ours.entry } : { container: parts.service, level, line: parts.text }));
|
|
5363
|
+
else if (raw)
|
|
5364
|
+
console.log(line);
|
|
5365
|
+
else
|
|
5366
|
+
console.log(`${serviceColor(parts.service)(parts.service.padEnd(width))} ${color.dim("|")} ${ours?.text ?? parts.text}`);
|
|
5367
|
+
};
|
|
5368
|
+
await Promise.all([eachLine(proc.stdout, show), eachLine(proc.stderr, show)]);
|
|
5369
|
+
if (await proc.exited !== 0)
|
|
5370
|
+
process.exitCode = 1;
|
|
5371
|
+
}
|
|
5372
|
+
async function eachLine(stream, fn) {
|
|
5373
|
+
const decoder = new TextDecoder;
|
|
5374
|
+
let buffer = "";
|
|
5375
|
+
for await (const chunk of stream) {
|
|
5376
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
5377
|
+
for (let i = buffer.indexOf(`
|
|
5378
|
+
`);i !== -1; i = buffer.indexOf(`
|
|
5379
|
+
`)) {
|
|
5380
|
+
fn(buffer.slice(0, i).replace(/\r$/, ""));
|
|
5381
|
+
buffer = buffer.slice(i + 1);
|
|
5382
|
+
}
|
|
5383
|
+
}
|
|
5384
|
+
if (buffer)
|
|
5385
|
+
fn(buffer);
|
|
5386
|
+
}
|
|
5184
5387
|
async function tunnel(args) {
|
|
5185
5388
|
const dir = await serverDir(args);
|
|
5186
5389
|
const { text, values } = await loadEnv(dir);
|
|
@@ -5339,6 +5542,8 @@ async function server(command, args) {
|
|
|
5339
5542
|
await update(args);
|
|
5340
5543
|
else if (command === "restart")
|
|
5341
5544
|
await restart(args);
|
|
5545
|
+
else if (command === "logs")
|
|
5546
|
+
await logs(args);
|
|
5342
5547
|
else if (command === "tunnel")
|
|
5343
5548
|
await tunnel(args);
|
|
5344
5549
|
else if (command === "database-public")
|
package/package.json
CHANGED