@tensorix/metahub 0.1.2 → 0.1.4
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 +50 -5
- package/dist/cli.js +3160 -196
- package/dist/cli.js.map +37 -21
- package/dist/core/config.d.ts +14 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/databases.d.ts.map +1 -1
- package/dist/core/db.d.ts +6 -0
- package/dist/core/db.d.ts.map +1 -1
- package/dist/core/documents.d.ts +1 -0
- package/dist/core/documents.d.ts.map +1 -1
- package/dist/core/integrity.d.ts +32 -0
- package/dist/core/integrity.d.ts.map +1 -0
- package/dist/core/properties.d.ts.map +1 -1
- package/dist/core/schema.d.ts +1 -1
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/search.d.ts +2 -0
- package/dist/core/search.d.ts.map +1 -1
- package/dist/core/snapshot.d.ts +2 -0
- package/dist/core/snapshot.d.ts.map +1 -1
- package/dist/core/sync/auth.d.ts +6 -0
- package/dist/core/sync/auth.d.ts.map +1 -1
- package/dist/core/sync/client.d.ts +7 -2
- package/dist/core/sync/client.d.ts.map +1 -1
- package/dist/core/sync/pairing.d.ts +50 -0
- package/dist/core/sync/pairing.d.ts.map +1 -0
- package/dist/core/sync/peers-routes.d.ts +3 -0
- package/dist/core/sync/peers-routes.d.ts.map +1 -0
- package/dist/core/sync/peers.d.ts +45 -0
- package/dist/core/sync/peers.d.ts.map +1 -0
- package/dist/core/sync/protocol.d.ts +13 -0
- package/dist/core/sync/protocol.d.ts.map +1 -1
- package/dist/core/sync/routes.d.ts.map +1 -1
- package/dist/core/sync/server.d.ts +20 -0
- package/dist/core/sync/server.d.ts.map +1 -1
- package/dist/core/sync/webui-routes.d.ts.map +1 -1
- package/dist/core/sync/webui.d.ts +11 -0
- package/dist/core/sync/webui.d.ts.map +1 -1
- package/dist/index.js +806 -143
- package/dist/index.js.map +21 -17
- package/dist/webui.js +33 -32
- package/package.json +4 -2
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
3
5
|
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
9
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
|
+
for (let key of __getOwnPropNames(mod))
|
|
12
|
+
if (!__hasOwnProp.call(to, key))
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: () => mod[key],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
4
20
|
var __export = (target, all) => {
|
|
5
21
|
for (var name in all)
|
|
6
22
|
__defProp(target, name, {
|
|
@@ -1272,12 +1288,75 @@ var init_sites = __esm(() => {
|
|
|
1272
1288
|
};
|
|
1273
1289
|
});
|
|
1274
1290
|
|
|
1291
|
+
// node_modules/sisteransi/src/index.js
|
|
1292
|
+
var require_src = __commonJS((exports, module) => {
|
|
1293
|
+
var ESC2 = "\x1B";
|
|
1294
|
+
var CSI2 = `${ESC2}[`;
|
|
1295
|
+
var beep = "\x07";
|
|
1296
|
+
var cursor = {
|
|
1297
|
+
to(x2, y3) {
|
|
1298
|
+
if (!y3)
|
|
1299
|
+
return `${CSI2}${x2 + 1}G`;
|
|
1300
|
+
return `${CSI2}${y3 + 1};${x2 + 1}H`;
|
|
1301
|
+
},
|
|
1302
|
+
move(x2, y3) {
|
|
1303
|
+
let ret = "";
|
|
1304
|
+
if (x2 < 0)
|
|
1305
|
+
ret += `${CSI2}${-x2}D`;
|
|
1306
|
+
else if (x2 > 0)
|
|
1307
|
+
ret += `${CSI2}${x2}C`;
|
|
1308
|
+
if (y3 < 0)
|
|
1309
|
+
ret += `${CSI2}${-y3}A`;
|
|
1310
|
+
else if (y3 > 0)
|
|
1311
|
+
ret += `${CSI2}${y3}B`;
|
|
1312
|
+
return ret;
|
|
1313
|
+
},
|
|
1314
|
+
up: (count = 1) => `${CSI2}${count}A`,
|
|
1315
|
+
down: (count = 1) => `${CSI2}${count}B`,
|
|
1316
|
+
forward: (count = 1) => `${CSI2}${count}C`,
|
|
1317
|
+
backward: (count = 1) => `${CSI2}${count}D`,
|
|
1318
|
+
nextLine: (count = 1) => `${CSI2}E`.repeat(count),
|
|
1319
|
+
prevLine: (count = 1) => `${CSI2}F`.repeat(count),
|
|
1320
|
+
left: `${CSI2}G`,
|
|
1321
|
+
hide: `${CSI2}?25l`,
|
|
1322
|
+
show: `${CSI2}?25h`,
|
|
1323
|
+
save: `${ESC2}7`,
|
|
1324
|
+
restore: `${ESC2}8`
|
|
1325
|
+
};
|
|
1326
|
+
var scroll = {
|
|
1327
|
+
up: (count = 1) => `${CSI2}S`.repeat(count),
|
|
1328
|
+
down: (count = 1) => `${CSI2}T`.repeat(count)
|
|
1329
|
+
};
|
|
1330
|
+
var erase = {
|
|
1331
|
+
screen: `${CSI2}2J`,
|
|
1332
|
+
up: (count = 1) => `${CSI2}1J`.repeat(count),
|
|
1333
|
+
down: (count = 1) => `${CSI2}J`.repeat(count),
|
|
1334
|
+
line: `${CSI2}2K`,
|
|
1335
|
+
lineEnd: `${CSI2}K`,
|
|
1336
|
+
lineStart: `${CSI2}1K`,
|
|
1337
|
+
lines(count) {
|
|
1338
|
+
let clear = "";
|
|
1339
|
+
for (let i2 = 0;i2 < count; i2++)
|
|
1340
|
+
clear += this.line + (i2 < count - 1 ? cursor.up() : "");
|
|
1341
|
+
if (count)
|
|
1342
|
+
clear += cursor.left;
|
|
1343
|
+
return clear;
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
module.exports = { cursor, scroll, erase, beep };
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1275
1349
|
// src/core/sync/webui.ts
|
|
1276
1350
|
var exports_webui = {};
|
|
1277
1351
|
__export(exports_webui, {
|
|
1352
|
+
warmWebui: () => warmWebui,
|
|
1353
|
+
setWebuiBundle: () => setWebuiBundle,
|
|
1278
1354
|
serveWebui: () => serveWebui
|
|
1279
1355
|
});
|
|
1280
1356
|
import { fileURLToPath } from "url";
|
|
1357
|
+
function setWebuiBundle(js) {
|
|
1358
|
+
cachedJs = js;
|
|
1359
|
+
}
|
|
1281
1360
|
async function getJs() {
|
|
1282
1361
|
if (cachedJs != null)
|
|
1283
1362
|
return cachedJs;
|
|
@@ -1293,6 +1372,11 @@ async function getJs() {
|
|
|
1293
1372
|
cachedJs = await res.outputs[0].text();
|
|
1294
1373
|
return cachedJs;
|
|
1295
1374
|
}
|
|
1375
|
+
async function warmWebui() {
|
|
1376
|
+
try {
|
|
1377
|
+
await getJs();
|
|
1378
|
+
} catch {}
|
|
1379
|
+
}
|
|
1296
1380
|
async function serveWebui(req) {
|
|
1297
1381
|
if (req.method !== "GET")
|
|
1298
1382
|
return null;
|
|
@@ -1305,8 +1389,8 @@ async function serveWebui(req) {
|
|
|
1305
1389
|
return new Response(await getJs(), {
|
|
1306
1390
|
headers: { "content-type": "text/javascript; charset=utf-8" }
|
|
1307
1391
|
});
|
|
1308
|
-
} catch (
|
|
1309
|
-
return new Response(`webui build failed: ${
|
|
1392
|
+
} catch (e3) {
|
|
1393
|
+
return new Response(`webui build failed: ${e3}`, { status: 500 });
|
|
1310
1394
|
}
|
|
1311
1395
|
}
|
|
1312
1396
|
return null;
|
|
@@ -1319,7 +1403,13 @@ var HTML = `<!doctype html>
|
|
|
1319
1403
|
<title>Metahub</title>
|
|
1320
1404
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
1321
1405
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
1322
|
-
|
|
1406
|
+
<!-- Load web fonts without blocking first paint: media="print" keeps the
|
|
1407
|
+
stylesheet non-render-blocking, then onload flips it to "all". The page
|
|
1408
|
+
paints instantly with the system-font fallbacks baked into --ui/--mono and
|
|
1409
|
+
swaps to the web fonts when they arrive (or stays on system fonts offline).
|
|
1410
|
+
This is what kept the desktop cold-start white for seconds on slow networks. -->
|
|
1411
|
+
<link rel="stylesheet" media="print" onload="this.media='all'" href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap">
|
|
1412
|
+
<noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"></noscript>
|
|
1323
1413
|
<script>try{document.documentElement.dataset.theme=localStorage.getItem('mh-theme')||'system'}catch(e){}</script>
|
|
1324
1414
|
<style>
|
|
1325
1415
|
:root {
|
|
@@ -1429,6 +1519,9 @@ var HTML = `<!doctype html>
|
|
|
1429
1519
|
.set-section { margin-top:34px; }
|
|
1430
1520
|
.set-section-head { font-size:12px; font-weight:600; text-transform:uppercase; letter-spacing:.07em; color:var(--muted); margin-bottom:4px; }
|
|
1431
1521
|
.set-section-desc { color:var(--fg-soft); margin-bottom:16px; }
|
|
1522
|
+
.set-footer { margin-top:40px; padding-top:20px; border-top:1px solid var(--line); display:flex;
|
|
1523
|
+
justify-content:center; gap:8px; color:var(--muted); font-size:12px; }
|
|
1524
|
+
.set-footer-sep { opacity:.5; }
|
|
1432
1525
|
.theme-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:12px; }
|
|
1433
1526
|
.theme-card { position:relative; display:flex; flex-direction:column; align-items:flex-start; gap:10px;
|
|
1434
1527
|
text-align:left; padding:16px; border:1px solid var(--line-strong); border-radius:var(--radius);
|
|
@@ -1448,9 +1541,25 @@ var HTML = `<!doctype html>
|
|
|
1448
1541
|
.theme-card.sel .tc-check { opacity:1; transform:scale(1); }
|
|
1449
1542
|
@media (max-width:560px) { .theme-grid { grid-template-columns:1fr; } }
|
|
1450
1543
|
|
|
1544
|
+
/* sync devices */
|
|
1545
|
+
.peer-actions { display:flex; gap:10px; margin-bottom:16px; flex-wrap:wrap; }
|
|
1546
|
+
.peer-actions .btn { display:inline-flex; align-items:center; gap:6px; }
|
|
1547
|
+
.peer-list { display:flex; flex-direction:column; gap:8px; }
|
|
1548
|
+
.peer-row { display:flex; align-items:center; gap:12px; padding:12px 14px; border:1px solid var(--line);
|
|
1549
|
+
border-radius:10px; background:var(--surface); }
|
|
1550
|
+
.peer-dot { width:9px; height:9px; border-radius:50%; flex:none; background:var(--muted); }
|
|
1551
|
+
.peer-dot.on { background:#3fb950; } .peer-dot.err { background:#f85149; } .peer-dot.off { background:var(--muted); }
|
|
1552
|
+
.peer-main { flex:1; min-width:0; }
|
|
1553
|
+
.peer-url { font-weight:600; font-size:14px; color:var(--fg); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
1554
|
+
.peer-sub { font-size:12px; color:var(--muted); margin-top:2px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
1555
|
+
.peer-menu { flex:none; padding:6px; }
|
|
1556
|
+
.pair-code { font-family:var(--mono); font-size:26px; font-weight:700; letter-spacing:.12em;
|
|
1557
|
+
text-align:center; padding:16px; border:1px dashed var(--line); border-radius:10px; background:var(--surface); color:var(--fg); }
|
|
1558
|
+
|
|
1451
1559
|
/* main */
|
|
1452
1560
|
.main { flex:1; display:flex; flex-direction:column; overflow:hidden; min-width:0; }
|
|
1453
1561
|
.topbar { display:flex; align-items:center; gap:6px; padding:10px 18px; border-bottom:1px solid var(--line); min-height:49px; }
|
|
1562
|
+
.topbar.bare { border-bottom:0; }
|
|
1454
1563
|
.topbar .hamburger { display:none; }
|
|
1455
1564
|
.topbar .hamburger.show-collapsed { display:grid; }
|
|
1456
1565
|
.sidebar.collapsed { overflow:hidden; }
|
|
@@ -1775,7 +1884,71 @@ var HTML = `<!doctype html>
|
|
|
1775
1884
|
@keyframes slidein { from { opacity:0; transform:translateX(-12px); } }
|
|
1776
1885
|
|
|
1777
1886
|
/* misc */
|
|
1778
|
-
.empty { display:
|
|
1887
|
+
.empty { display:flex; flex-direction:column; align-items:center; justify-content:center; height:100%;
|
|
1888
|
+
color:var(--muted); text-align:center; padding:40px; }
|
|
1889
|
+
|
|
1890
|
+
/* ---- empty state: \u77E5\u8BC6\u751F\u957F\u63D2\u753B ---- */
|
|
1891
|
+
.estate-art { width:240px; height:208px; overflow:visible; animation:idleFloat 5s ease-in-out 1.6s infinite; }
|
|
1892
|
+
.estate-art .stroke-a { fill:none; stroke:var(--accent); stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round; }
|
|
1893
|
+
.estate-art .stroke { fill:none; stroke:var(--line-strong); stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round; }
|
|
1894
|
+
.estate-art .fill-a { fill:var(--accent); }
|
|
1895
|
+
.estate-art .fill-soft { fill:var(--accent-soft); }
|
|
1896
|
+
.estate-art .fill-surface { fill:var(--bg); }
|
|
1897
|
+
.estate-art .ground { fill:var(--accent); }
|
|
1898
|
+
.estate-title { margin:28px 0 0; font-size:18px; font-weight:600; letter-spacing:.2px; color:var(--fg);
|
|
1899
|
+
opacity:0; animation:fadeUp .5s ease forwards 1.0s; }
|
|
1900
|
+
.estate-hint { margin:9px 0 0; font-size:13.5px; line-height:1.6; color:var(--muted); max-width:300px;
|
|
1901
|
+
opacity:0; animation:fadeUp .5s ease forwards 1.15s; }
|
|
1902
|
+
.estate-link { margin:18px 0 0; font-size:14px; font-weight:600; color:var(--accent);
|
|
1903
|
+
display:inline-flex; align-items:center; gap:6px; padding:9px 8px; border-radius:var(--radius-sm);
|
|
1904
|
+
opacity:0; animation:fadeUp .5s ease forwards 1.3s; transition:opacity .15s; }
|
|
1905
|
+
.estate-link:hover { opacity:.7; }
|
|
1906
|
+
/* intro */
|
|
1907
|
+
.draw { stroke-dasharray:100; stroke-dashoffset:100; }
|
|
1908
|
+
@keyframes draw { to { stroke-dashoffset:0; } }
|
|
1909
|
+
@keyframes leafGrow { from { opacity:0; transform:scale(.2) rotate(-10deg); } to { opacity:1; transform:scale(1) rotate(0); } }
|
|
1910
|
+
@keyframes cardUp { from { opacity:0; transform:translateY(16px) scale(.96); } to { opacity:1; transform:translateY(0) scale(1); } }
|
|
1911
|
+
@keyframes groundIn { to { opacity:.1; } }
|
|
1912
|
+
@keyframes glowIn { to { opacity:.6; } }
|
|
1913
|
+
@keyframes fadeUp { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:translateY(0); } }
|
|
1914
|
+
/* idle loops \u2014 each 0% equals the settled rest state to avoid a jump */
|
|
1915
|
+
@keyframes idleFloat { 0%,100% { transform:translateY(0); } 50% { transform:translateY(-5px); } }
|
|
1916
|
+
@keyframes plantSway { 0%,100% { transform:rotate(0deg); } 25% { transform:rotate(-1.3deg); } 75% { transform:rotate(1.3deg); } }
|
|
1917
|
+
@keyframes leafSwayA { 0%,100% { transform:rotate(0deg); } 25% { transform:rotate(-2.6deg); } 75% { transform:rotate(2.6deg); } }
|
|
1918
|
+
@keyframes leafSwayB { 0%,100% { transform:rotate(0deg); } 25% { transform:rotate(2.6deg); } 75% { transform:rotate(-2.6deg); } }
|
|
1919
|
+
@keyframes groundBreathe { 0%,100% { transform:scale(1); opacity:.10; } 50% { transform:scale(1.07); opacity:.15; } }
|
|
1920
|
+
@keyframes glowPulse { 0%,100% { transform:scale(1); opacity:.6; } 50% { transform:scale(1.1); opacity:.85; } }
|
|
1921
|
+
@keyframes particle { 0% { opacity:0; transform:translate(0,0) scale(.4); } 35% { opacity:.85; transform:translate(2px,-4px) scale(1); } 70% { opacity:0; transform:translate(4px,-9px) scale(.5); } 100% { opacity:0; transform:translate(0,0) scale(.4); } }
|
|
1922
|
+
/* wiring */
|
|
1923
|
+
.kg-ground { transform-box:fill-box; transform-origin:center; opacity:0;
|
|
1924
|
+
animation:groundIn .5s ease forwards .1s, groundBreathe 6s ease-in-out 1.6s infinite; }
|
|
1925
|
+
.kg-glow { filter:blur(9px); transform-box:fill-box; transform-origin:center; opacity:0;
|
|
1926
|
+
animation:glowIn .6s ease forwards .1s, glowPulse 4.6s ease-in-out 1.6s infinite; }
|
|
1927
|
+
.kg-cardB { transform-box:fill-box; transform-origin:center; opacity:0; animation:cardUp .55s cubic-bezier(.2,.8,.2,1) forwards .2s; }
|
|
1928
|
+
.kg-cardA { transform-box:fill-box; transform-origin:center; opacity:0; animation:cardUp .55s cubic-bezier(.2,.8,.2,1) forwards .32s; }
|
|
1929
|
+
.kg-plant { transform-box:fill-box; transform-origin:50% 100%; animation:plantSway 6s ease-in-out 1.7s infinite; }
|
|
1930
|
+
.kg-stem { animation:draw .9s ease-out forwards .4s; }
|
|
1931
|
+
.kg-leaf { transform-box:fill-box; }
|
|
1932
|
+
.kg-grow { transform-box:fill-box; opacity:0; }
|
|
1933
|
+
.kg-leaf.l1 { transform-origin:90% 100%; animation:leafSwayA 4.6s ease-in-out 1.7s infinite; }
|
|
1934
|
+
.kg-leaf.l2 { transform-origin:0% 80%; animation:leafSwayB 5.2s ease-in-out 1.8s infinite; }
|
|
1935
|
+
.kg-leaf.l3 { transform-origin:0% 100%; animation:leafSwayA 5.0s ease-in-out 1.9s infinite; }
|
|
1936
|
+
.kg-leaf.l1 .kg-grow { transform-origin:90% 100%; animation:leafGrow .52s cubic-bezier(.18,.9,.24,1.22) forwards .85s; }
|
|
1937
|
+
.kg-leaf.l2 .kg-grow { transform-origin:0% 80%; animation:leafGrow .52s cubic-bezier(.18,.9,.24,1.22) forwards 1.0s; }
|
|
1938
|
+
.kg-leaf.l3 .kg-grow { transform-origin:0% 100%; animation:leafGrow .52s cubic-bezier(.18,.9,.24,1.22) forwards 1.15s; }
|
|
1939
|
+
.kg-particle { transform-box:fill-box; transform-origin:center; opacity:0; }
|
|
1940
|
+
.kg-particle.p1 { animation:particle 4.5s ease-in-out 1.6s infinite; }
|
|
1941
|
+
.kg-particle.p2 { animation:particle 5.2s ease-in-out 2.0s infinite; }
|
|
1942
|
+
.kg-particle.p3 { animation:particle 4.8s ease-in-out 2.6s infinite; }
|
|
1943
|
+
.kg-particle.p4 { animation:particle 5.6s ease-in-out 3.1s infinite; }
|
|
1944
|
+
.kg-particle.p5 { animation:particle 4.2s ease-in-out 2.3s infinite; }
|
|
1945
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1946
|
+
.estate-art, .estate-art *, .estate-title, .estate-hint, .estate-link { animation:none !important; opacity:1 !important; transform:none !important; }
|
|
1947
|
+
.draw { stroke-dashoffset:0 !important; }
|
|
1948
|
+
.kg-ground { opacity:.1 !important; }
|
|
1949
|
+
.kg-glow { opacity:.6 !important; }
|
|
1950
|
+
.kg-particle { opacity:0 !important; }
|
|
1951
|
+
}
|
|
1779
1952
|
.error-bar { margin:12px 24px; padding:10px 14px; background:var(--danger-soft); color:var(--danger); border-radius:var(--radius-sm); font-size:13px; cursor:pointer; }
|
|
1780
1953
|
.view-placeholder { padding:40px; text-align:center; border:1px dashed var(--line-strong); border-radius:var(--radius); color:var(--muted); margin-top:8px; }
|
|
1781
1954
|
.ico.flip { transform:scaleX(-1); }
|
|
@@ -1884,6 +2057,49 @@ var HTML = `<!doctype html>
|
|
|
1884
2057
|
.db { padding:16px 14px 90px; }
|
|
1885
2058
|
.peek { width:100%; }
|
|
1886
2059
|
}
|
|
2060
|
+
|
|
2061
|
+
/* Quick Notes window (desktop, loaded at #quick). A transparent body lets the
|
|
2062
|
+
macOS vibrancy blur show through; a translucent panel keeps text readable.
|
|
2063
|
+
The top bar is the window drag region; its buttons opt back out. */
|
|
2064
|
+
body.quicknote { background:transparent; }
|
|
2065
|
+
/* The main app lays #app out as a flex row (sidebar + main); the quick-note
|
|
2066
|
+
route has a single child, so override to a full-bleed block or the panel
|
|
2067
|
+
shrinks to its content width and the window's vibrancy shows through. */
|
|
2068
|
+
body.quicknote #app { display:block; width:100vw; height:100vh; }
|
|
2069
|
+
.qn { display:flex; flex-direction:column; width:100%; height:100vh; overflow:hidden;
|
|
2070
|
+
background:color-mix(in srgb, var(--bg) 20%, transparent); }
|
|
2071
|
+
.qn-bar { display:flex; align-items:center; gap:6px; padding:4px 10px 4px 76px;
|
|
2072
|
+
user-select:none; -webkit-app-region:drag;
|
|
2073
|
+
background:color-mix(in srgb, var(--surface) 30%, transparent); }
|
|
2074
|
+
.qn-brand { flex:1; min-width:0; font-size:12px; font-weight:600; color:var(--fg-soft);
|
|
2075
|
+
white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
|
2076
|
+
.qn-actions { display:flex; gap:2px; -webkit-app-region:no-drag; }
|
|
2077
|
+
.qn-actions .iconbtn { width:26px; height:26px; }
|
|
2078
|
+
.qn-actions .iconbtn.active { color:var(--accent); background:var(--hover-2); }
|
|
2079
|
+
.qn-body { flex:1; overflow:auto; -webkit-app-region:no-drag; }
|
|
2080
|
+
.qn-body .doc { max-width:none; margin:0; padding:14px 22px 30vh; }
|
|
2081
|
+
.qn-body .doc-title { font-size:24px; margin-bottom:2px; }
|
|
2082
|
+
.qn-body .doc-meta { display:none; }
|
|
2083
|
+
/* Compact menus inside the quick-note window only (dropdown + slash command
|
|
2084
|
+
share .pop/.item/.lico with the main app; scope under body.quicknote so the
|
|
2085
|
+
main app keeps its roomier density). */
|
|
2086
|
+
body.quicknote .pop { padding:5px; border-radius:9px; }
|
|
2087
|
+
body.quicknote .pop .lbl { padding:4px 8px 2px; font-size:10px; }
|
|
2088
|
+
body.quicknote .pop .item { padding:5px 9px; gap:8px; font-size:12.5px; }
|
|
2089
|
+
body.quicknote .pop .item .t { font-size:12.5px; }
|
|
2090
|
+
body.quicknote .pop .item .d { font-size:10.5px; }
|
|
2091
|
+
body.quicknote .pop .item .lico { width:22px; height:22px; }
|
|
2092
|
+
body.quicknote .pop .item .lico.plain { width:20px; height:20px; }
|
|
2093
|
+
body.quicknote .pop .sep { margin:4px 4px; }
|
|
2094
|
+
|
|
2095
|
+
/* Quick Notes settings rows (desktop only). */
|
|
2096
|
+
.qn-set-row { display:flex; align-items:center; gap:14px; padding:12px 0; border-top:1px solid var(--line); }
|
|
2097
|
+
.qn-set-row:first-of-type { border-top:0; }
|
|
2098
|
+
.qn-set-main { flex:1; min-width:0; }
|
|
2099
|
+
.qn-set-name { font-weight:600; }
|
|
2100
|
+
.qn-set-desc { color:var(--muted); font-size:13px; margin-top:2px; }
|
|
2101
|
+
.qn-shortcut { min-width:140px; justify-content:center; font-family:var(--mono); }
|
|
2102
|
+
.qn-shortcut.capturing { outline:2px solid var(--accent); color:var(--accent); }
|
|
1887
2103
|
</style>
|
|
1888
2104
|
</head>
|
|
1889
2105
|
<body>
|
|
@@ -3324,7 +3540,7 @@ async function runMain(cmd, opts = {}) {
|
|
|
3324
3540
|
// package.json
|
|
3325
3541
|
var package_default = {
|
|
3326
3542
|
name: "@tensorix/metahub",
|
|
3327
|
-
version: "0.1.
|
|
3543
|
+
version: "0.1.4",
|
|
3328
3544
|
description: "Metahub core library and CLI",
|
|
3329
3545
|
type: "module",
|
|
3330
3546
|
main: "./dist/index.js",
|
|
@@ -3350,9 +3566,11 @@ var package_default = {
|
|
|
3350
3566
|
build: "bun run scripts/build.ts",
|
|
3351
3567
|
"build:binaries": "bun run scripts/compile-binaries.ts",
|
|
3352
3568
|
prepublishOnly: "bun run build",
|
|
3353
|
-
test: "bun test"
|
|
3569
|
+
test: "bun test",
|
|
3570
|
+
release: "bun run scripts/tag-release.ts"
|
|
3354
3571
|
},
|
|
3355
3572
|
dependencies: {
|
|
3573
|
+
"@clack/prompts": "^1.5.0",
|
|
3356
3574
|
citty: "^0.1.6",
|
|
3357
3575
|
"highlight.js": "^11.11.1",
|
|
3358
3576
|
preact: "^10.29.2",
|
|
@@ -3389,11 +3607,41 @@ CREATE TABLE IF NOT EXISTS crdt_changes (
|
|
|
3389
3607
|
CREATE INDEX IF NOT EXISTS idx_changes_hlc ON crdt_changes(hlc);
|
|
3390
3608
|
|
|
3391
3609
|
-- Per-peer replication cursors (SQLite rowids: monotonic in insertion order,
|
|
3392
|
-
-- so no change is ever skipped regardless of HLC/clock skew).
|
|
3610
|
+
-- so no change is ever skipped regardless of HLC/clock skew). Outbound side of
|
|
3611
|
+
-- a pairing: "token" is the credential the *remote* issued to us (sent as a
|
|
3612
|
+
-- Bearer header when we sync to them). See migratePeers in db.ts for the columns
|
|
3613
|
+
-- added to legacy databases.
|
|
3393
3614
|
CREATE TABLE IF NOT EXISTS peers (
|
|
3394
|
-
url
|
|
3395
|
-
pull_cursor
|
|
3396
|
-
push_cursor
|
|
3615
|
+
url TEXT PRIMARY KEY,
|
|
3616
|
+
pull_cursor INTEGER NOT NULL DEFAULT 0,
|
|
3617
|
+
push_cursor INTEGER NOT NULL DEFAULT 0,
|
|
3618
|
+
token TEXT,
|
|
3619
|
+
label TEXT,
|
|
3620
|
+
node_id TEXT,
|
|
3621
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
3622
|
+
last_sync_at INTEGER,
|
|
3623
|
+
last_status TEXT,
|
|
3624
|
+
last_error TEXT
|
|
3625
|
+
);
|
|
3626
|
+
|
|
3627
|
+
-- Inbound side of a pairing: credentials *we* issued to remote devices. A peer
|
|
3628
|
+
-- presents one of these on /sync; acceptsSyncToken() checks it alongside the
|
|
3629
|
+
-- managed master token. Revoke by deleting the row.
|
|
3630
|
+
CREATE TABLE IF NOT EXISTS peer_grants (
|
|
3631
|
+
token TEXT PRIMARY KEY,
|
|
3632
|
+
peer_url TEXT,
|
|
3633
|
+
node_id TEXT,
|
|
3634
|
+
created_at INTEGER
|
|
3635
|
+
);
|
|
3636
|
+
|
|
3637
|
+
-- One-time pairing codes minted by "mh config peer code" / POST /api/pair/new.
|
|
3638
|
+
-- Short-lived and single-use: redeemed during the pairing handshake, then the
|
|
3639
|
+
-- two devices exchange durable peer_grants. used/exp gate redemption.
|
|
3640
|
+
CREATE TABLE IF NOT EXISTS pairing_codes (
|
|
3641
|
+
code TEXT PRIMARY KEY,
|
|
3642
|
+
exp INTEGER NOT NULL,
|
|
3643
|
+
used INTEGER NOT NULL DEFAULT 0,
|
|
3644
|
+
created_at INTEGER
|
|
3397
3645
|
);
|
|
3398
3646
|
|
|
3399
3647
|
CREATE TABLE IF NOT EXISTS databases (
|
|
@@ -3520,9 +3768,20 @@ function deleteDatabase(db, id) {
|
|
|
3520
3768
|
if (!getDatabase(db, id))
|
|
3521
3769
|
return false;
|
|
3522
3770
|
emit(db, "databases", id, "__deleted", 1);
|
|
3771
|
+
const tombstone = (table) => {
|
|
3772
|
+
for (const r3 of liveChildren(db, table, "database_id", id))
|
|
3773
|
+
emit(db, table, r3, "__deleted", 1);
|
|
3774
|
+
};
|
|
3775
|
+
tombstone("properties");
|
|
3776
|
+
tombstone("records");
|
|
3777
|
+
for (const d2 of liveChildren(db, "documents", "database_id", id))
|
|
3778
|
+
emit(db, "documents", d2, "database_id", null);
|
|
3523
3779
|
db.query("DELETE FROM meta WHERE key = 'current_db' AND value = ?").run(id);
|
|
3524
3780
|
return true;
|
|
3525
3781
|
}
|
|
3782
|
+
function liveChildren(db, table, col, parentId) {
|
|
3783
|
+
return db.query(`SELECT id FROM ${table} WHERE ${col} = ? AND __deleted = 0`).all(parentId).map((r3) => r3.id);
|
|
3784
|
+
}
|
|
3526
3785
|
|
|
3527
3786
|
// src/core/properties.ts
|
|
3528
3787
|
init_ids();
|
|
@@ -3651,9 +3910,13 @@ function setPropertyWidth(db, id, width) {
|
|
|
3651
3910
|
return updateProperty(db, id, { config: { ...cur.config ?? {}, width: w2 } });
|
|
3652
3911
|
}
|
|
3653
3912
|
function removeProperty(db, id) {
|
|
3654
|
-
|
|
3913
|
+
const prop = getProperty(db, id);
|
|
3914
|
+
if (!prop)
|
|
3655
3915
|
return false;
|
|
3656
3916
|
emit(db, "properties", id, "__deleted", 1);
|
|
3917
|
+
const rows = db.query("SELECT id FROM records WHERE database_id = ? AND __deleted = 0 AND data ->> ? IS NOT NULL").all(prop.database_id, id);
|
|
3918
|
+
for (const r3 of rows)
|
|
3919
|
+
emit(db, "records", r3.id, id, undefined);
|
|
3657
3920
|
return true;
|
|
3658
3921
|
}
|
|
3659
3922
|
|
|
@@ -4106,12 +4369,28 @@ function migrateRecords(db) {
|
|
|
4106
4369
|
tx();
|
|
4107
4370
|
backfillRecordOrderKeys(db);
|
|
4108
4371
|
}
|
|
4372
|
+
function migratePeers(db) {
|
|
4373
|
+
const add = [
|
|
4374
|
+
["token", "TEXT"],
|
|
4375
|
+
["label", "TEXT"],
|
|
4376
|
+
["node_id", "TEXT"],
|
|
4377
|
+
["enabled", "INTEGER NOT NULL DEFAULT 1"],
|
|
4378
|
+
["last_sync_at", "INTEGER"],
|
|
4379
|
+
["last_status", "TEXT"],
|
|
4380
|
+
["last_error", "TEXT"]
|
|
4381
|
+
];
|
|
4382
|
+
for (const [col, decl] of add) {
|
|
4383
|
+
if (!hasColumn(db, "peers", col))
|
|
4384
|
+
db.exec(`ALTER TABLE peers ADD COLUMN ${col} ${decl}`);
|
|
4385
|
+
}
|
|
4386
|
+
}
|
|
4109
4387
|
function openMetahub() {
|
|
4110
4388
|
ensureDirs();
|
|
4111
4389
|
const db = new Database(dbPath(), { create: true });
|
|
4112
4390
|
db.exec("PRAGMA journal_mode = WAL;");
|
|
4113
4391
|
runSchema(db);
|
|
4114
4392
|
migrateRecords(db);
|
|
4393
|
+
migratePeers(db);
|
|
4115
4394
|
return db;
|
|
4116
4395
|
}
|
|
4117
4396
|
|
|
@@ -4354,9 +4633,12 @@ function getDocument(db, id) {
|
|
|
4354
4633
|
return db.query("SELECT id, title, body, database_id, parent_id, created_hlc FROM documents WHERE id = ? AND __deleted = 0").get(id);
|
|
4355
4634
|
}
|
|
4356
4635
|
function listDocuments(db, opts = {}) {
|
|
4636
|
+
const cols = "SELECT id, title, database_id, parent_id, created_hlc FROM documents WHERE __deleted = 0";
|
|
4637
|
+
if (opts.parent_id !== undefined)
|
|
4638
|
+
return db.query(`${cols} AND parent_id = ? ORDER BY created_hlc`).all(opts.parent_id);
|
|
4357
4639
|
if (opts.database_id)
|
|
4358
|
-
return db.query(
|
|
4359
|
-
return db.query(
|
|
4640
|
+
return db.query(`${cols} AND database_id = ? ORDER BY created_hlc`).all(opts.database_id);
|
|
4641
|
+
return db.query(`${cols} ORDER BY created_hlc`).all();
|
|
4360
4642
|
}
|
|
4361
4643
|
function updateDocument(db, id, fields) {
|
|
4362
4644
|
if (!getDocument(db, id))
|
|
@@ -4440,8 +4722,15 @@ function deleteDocument(db, id) {
|
|
|
4440
4722
|
if (!getDocument(db, id))
|
|
4441
4723
|
return false;
|
|
4442
4724
|
emit(db, "documents", id, "__deleted", 1);
|
|
4725
|
+
for (const b2 of liveChildIds(db, "doc_blocks", "doc_id", id))
|
|
4726
|
+
emit(db, "doc_blocks", b2, "__deleted", 1);
|
|
4727
|
+
for (const c3 of liveChildIds(db, "documents", "parent_id", id))
|
|
4728
|
+
emit(db, "documents", c3, "parent_id", null);
|
|
4443
4729
|
return true;
|
|
4444
4730
|
}
|
|
4731
|
+
function liveChildIds(db, table2, col, parentId) {
|
|
4732
|
+
return db.query(`SELECT id FROM ${table2} WHERE ${col} = ? AND __deleted = 0`).all(parentId).map((r3) => r3.id);
|
|
4733
|
+
}
|
|
4445
4734
|
function countOccurrences(haystack, needle) {
|
|
4446
4735
|
if (needle === "")
|
|
4447
4736
|
return 0;
|
|
@@ -4675,6 +4964,7 @@ function docTree(rows) {
|
|
|
4675
4964
|
if (rows.length === 0)
|
|
4676
4965
|
return "(empty)";
|
|
4677
4966
|
const ids = new Set(rows.map((r3) => r3.id));
|
|
4967
|
+
const idWidth = Math.max(...rows.map((r3) => r3.id.length));
|
|
4678
4968
|
const children = new Map;
|
|
4679
4969
|
const roots = [];
|
|
4680
4970
|
for (const r3 of rows) {
|
|
@@ -4685,13 +4975,15 @@ function docTree(rows) {
|
|
|
4685
4975
|
(children.get(key) ?? children.set(key, []).get(key)).push(r3);
|
|
4686
4976
|
}
|
|
4687
4977
|
const lines = [];
|
|
4688
|
-
const walk = (node,
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
walk(
|
|
4978
|
+
const walk = (node, prefix, isRoot, isLast) => {
|
|
4979
|
+
const branch = isRoot ? "" : isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
4980
|
+
const title = node.title || "(untitled)";
|
|
4981
|
+
lines.push(`${node.id.padEnd(idWidth)} ${prefix}${branch}${title}`);
|
|
4982
|
+
const kids = children.get(node.id) ?? [];
|
|
4983
|
+
const childPrefix = isRoot ? "" : prefix + (isLast ? " " : "\u2502 ");
|
|
4984
|
+
kids.forEach((child, i2) => walk(child, childPrefix, false, i2 === kids.length - 1));
|
|
4985
|
+
};
|
|
4986
|
+
roots.forEach((root) => walk(root, "", true, true));
|
|
4695
4987
|
return lines.join(`
|
|
4696
4988
|
`);
|
|
4697
4989
|
}
|
|
@@ -4963,26 +5255,106 @@ var edit_default = defineCommand({
|
|
|
4963
5255
|
});
|
|
4964
5256
|
|
|
4965
5257
|
// src/core/search.ts
|
|
5258
|
+
init_crdt();
|
|
4966
5259
|
var TEXT_TYPES = "('text','url','select','multi_select','date')";
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
5260
|
+
var SEARCH_INDEX_VERSION = "1";
|
|
5261
|
+
function readMeta(db, key) {
|
|
5262
|
+
return db.query("SELECT value FROM meta WHERE key = ?").get(key)?.value ?? null;
|
|
5263
|
+
}
|
|
5264
|
+
function writeMeta(db, key, value) {
|
|
5265
|
+
db.query("INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(key, value);
|
|
5266
|
+
}
|
|
5267
|
+
function reindexDocument(db, id) {
|
|
5268
|
+
db.query("DELETE FROM search_fts WHERE kind = 'document' AND id = ?").run(id);
|
|
5269
|
+
const d2 = db.query("SELECT id, title, body, database_id FROM documents WHERE id = ? AND __deleted = 0").get(id);
|
|
5270
|
+
if (d2)
|
|
4976
5271
|
db.query("INSERT INTO search_fts (kind, id, database_id, title, body) VALUES ('document', ?, ?, ?, ?)").run(d2.id, d2.database_id, d2.title ?? "", d2.body ?? "");
|
|
4977
|
-
|
|
4978
|
-
|
|
5272
|
+
}
|
|
5273
|
+
function recordBodyRows(db, where, ...args) {
|
|
5274
|
+
return db.query(`SELECT r.id AS id, r.database_id AS database_id, group_concat(je.value, ' ') AS body
|
|
4979
5275
|
FROM records r, json_each(r.data) je
|
|
4980
5276
|
JOIN properties p ON p.id = je.key AND p.__deleted = 0
|
|
4981
|
-
WHERE r.__deleted = 0 AND p.type IN ${TEXT_TYPES}
|
|
4982
|
-
GROUP BY r.id`).all()
|
|
4983
|
-
|
|
5277
|
+
WHERE ${where} AND r.__deleted = 0 AND p.type IN ${TEXT_TYPES}
|
|
5278
|
+
GROUP BY r.id`).all(...args);
|
|
5279
|
+
}
|
|
5280
|
+
function insertRecordRow(db, r3) {
|
|
5281
|
+
db.query("INSERT INTO search_fts (kind, id, database_id, title, body) VALUES ('record', ?, ?, ?, ?)").run(r3.id, r3.database_id, r3.id, r3.body ?? "");
|
|
5282
|
+
}
|
|
5283
|
+
function reindexRecord(db, id) {
|
|
5284
|
+
db.query("DELETE FROM search_fts WHERE kind = 'record' AND id = ?").run(id);
|
|
5285
|
+
const r3 = recordBodyRows(db, "r.id = ?", id)[0];
|
|
5286
|
+
if (r3)
|
|
5287
|
+
insertRecordRow(db, r3);
|
|
5288
|
+
}
|
|
5289
|
+
function reindexDatabaseRecords(db, databaseId) {
|
|
5290
|
+
db.query("DELETE FROM search_fts WHERE kind = 'record' AND database_id = ?").run(databaseId);
|
|
5291
|
+
for (const r3 of recordBodyRows(db, "r.database_id = ?", databaseId))
|
|
5292
|
+
insertRecordRow(db, r3);
|
|
5293
|
+
}
|
|
5294
|
+
function fullRebuild(db) {
|
|
5295
|
+
db.query("DELETE FROM search_fts").run();
|
|
5296
|
+
for (const d2 of db.query("SELECT id, title, body, database_id FROM documents WHERE __deleted = 0").all())
|
|
5297
|
+
db.query("INSERT INTO search_fts (kind, id, database_id, title, body) VALUES ('document', ?, ?, ?, ?)").run(d2.id, d2.database_id, d2.title ?? "", d2.body ?? "");
|
|
5298
|
+
for (const r3 of recordBodyRows(db, "1 = 1"))
|
|
5299
|
+
insertRecordRow(db, r3);
|
|
5300
|
+
const max = db.query("SELECT MAX(rowid) AS m FROM crdt_changes").get().m ?? 0;
|
|
5301
|
+
writeMeta(db, "search_seq", String(max));
|
|
5302
|
+
writeMeta(db, "search_index_version", SEARCH_INDEX_VERSION);
|
|
5303
|
+
}
|
|
5304
|
+
function incrementalUpdate(db) {
|
|
5305
|
+
const cursor = Number(readMeta(db, "search_seq") ?? "0");
|
|
5306
|
+
const { changes, cursor: next } = changesAfterSeq(db, cursor);
|
|
5307
|
+
if (changes.length === 0)
|
|
5308
|
+
return;
|
|
5309
|
+
const docIds = new Set;
|
|
5310
|
+
const recordIds = new Set;
|
|
5311
|
+
const dbIds = new Set;
|
|
5312
|
+
for (const c3 of changes) {
|
|
5313
|
+
switch (c3.dataset) {
|
|
5314
|
+
case "documents":
|
|
5315
|
+
docIds.add(c3.row_id);
|
|
5316
|
+
break;
|
|
5317
|
+
case "doc_blocks": {
|
|
5318
|
+
const blk = db.query("SELECT doc_id FROM doc_blocks WHERE id = ?").get(c3.row_id);
|
|
5319
|
+
if (blk?.doc_id)
|
|
5320
|
+
docIds.add(blk.doc_id);
|
|
5321
|
+
break;
|
|
5322
|
+
}
|
|
5323
|
+
case "records":
|
|
5324
|
+
recordIds.add(c3.row_id);
|
|
5325
|
+
break;
|
|
5326
|
+
case "properties":
|
|
5327
|
+
if (c3.col === "type" || c3.col === "__deleted" || c3.col === "database_id") {
|
|
5328
|
+
const p = db.query("SELECT database_id FROM properties WHERE id = ?").get(c3.row_id);
|
|
5329
|
+
if (p?.database_id)
|
|
5330
|
+
dbIds.add(p.database_id);
|
|
5331
|
+
}
|
|
5332
|
+
break;
|
|
5333
|
+
}
|
|
5334
|
+
}
|
|
5335
|
+
for (const id of docIds)
|
|
5336
|
+
reindexDocument(db, id);
|
|
5337
|
+
for (const dbId of dbIds)
|
|
5338
|
+
reindexDatabaseRecords(db, dbId);
|
|
5339
|
+
for (const id of recordIds) {
|
|
5340
|
+
const r3 = db.query("SELECT database_id FROM records WHERE id = ?").get(id);
|
|
5341
|
+
if (r3?.database_id && dbIds.has(r3.database_id))
|
|
5342
|
+
continue;
|
|
5343
|
+
reindexRecord(db, id);
|
|
4984
5344
|
}
|
|
4985
|
-
db
|
|
5345
|
+
writeMeta(db, "search_seq", String(next));
|
|
5346
|
+
}
|
|
5347
|
+
function ensureIndex(db) {
|
|
5348
|
+
if (!ftsAvailable(db))
|
|
5349
|
+
return false;
|
|
5350
|
+
const version = readMeta(db, "search_index_version");
|
|
5351
|
+
const seq = readMeta(db, "search_seq");
|
|
5352
|
+
db.transaction(() => {
|
|
5353
|
+
if (version !== SEARCH_INDEX_VERSION || seq === null)
|
|
5354
|
+
fullRebuild(db);
|
|
5355
|
+
else
|
|
5356
|
+
incrementalUpdate(db);
|
|
5357
|
+
})();
|
|
4986
5358
|
return true;
|
|
4987
5359
|
}
|
|
4988
5360
|
function ftsSearch(db, query, limit) {
|
|
@@ -19674,25 +20046,40 @@ var HealthResponseSchema = exports_external.object({
|
|
|
19674
20046
|
ok: exports_external.boolean(),
|
|
19675
20047
|
node: exports_external.string().describe("Server's node id")
|
|
19676
20048
|
});
|
|
20049
|
+
var PairRequestSchema = exports_external.object({
|
|
20050
|
+
code: exports_external.string().describe("One-time pairing code minted by this server"),
|
|
20051
|
+
node_id: exports_external.string().describe("Calling device's node id"),
|
|
20052
|
+
grant: exports_external.string().describe("Durable credential the caller issues to us"),
|
|
20053
|
+
self_url: exports_external.string().optional().describe("Caller's reachable server URL (for mutual sync)")
|
|
20054
|
+
});
|
|
20055
|
+
var PairResponseSchema = exports_external.object({
|
|
20056
|
+
node_id: exports_external.string().describe("This server's node id"),
|
|
20057
|
+
grant: exports_external.string().describe("Durable credential we issue to the caller")
|
|
20058
|
+
});
|
|
19677
20059
|
var SYNC_PATH = "/sync";
|
|
19678
20060
|
var HEALTH_PATH = "/health";
|
|
19679
20061
|
var RENEW_PATH = "/auth/token";
|
|
20062
|
+
var PAIR_PATH = "/api/pair";
|
|
19680
20063
|
|
|
19681
20064
|
// src/core/sync/client.ts
|
|
19682
20065
|
function getPeer(db, url2) {
|
|
19683
|
-
const row = db.query("SELECT pull_cursor, push_cursor FROM peers WHERE url = ?").get(url2);
|
|
19684
|
-
return row ?? { pull_cursor: 0, push_cursor: 0 };
|
|
20066
|
+
const row = db.query("SELECT pull_cursor, push_cursor, token FROM peers WHERE url = ?").get(url2);
|
|
20067
|
+
return row ?? { pull_cursor: 0, push_cursor: 0, token: null };
|
|
19685
20068
|
}
|
|
19686
20069
|
function setPeer(db, url2, c3) {
|
|
19687
20070
|
db.query("INSERT INTO peers (url, pull_cursor, push_cursor) VALUES (?, ?, ?) ON CONFLICT(url) DO UPDATE SET pull_cursor = excluded.pull_cursor, push_cursor = excluded.push_cursor").run(url2, c3.pull_cursor, c3.push_cursor);
|
|
19688
20071
|
}
|
|
19689
|
-
async function syncWithPeer(db, url2) {
|
|
20072
|
+
async function syncWithPeer(db, url2, token) {
|
|
19690
20073
|
const node = getNodeId(db);
|
|
19691
20074
|
const peer = getPeer(db, url2);
|
|
20075
|
+
const bearer = token ?? peer.token ?? undefined;
|
|
19692
20076
|
const toPush = changesAfterSeq(db, peer.push_cursor);
|
|
20077
|
+
const headers = { "content-type": "application/json" };
|
|
20078
|
+
if (bearer)
|
|
20079
|
+
headers.authorization = `Bearer ${bearer}`;
|
|
19693
20080
|
const res = await fetch(new URL(SYNC_PATH, url2), {
|
|
19694
20081
|
method: "POST",
|
|
19695
|
-
headers
|
|
20082
|
+
headers,
|
|
19696
20083
|
body: JSON.stringify({ node_id: node, since: peer.pull_cursor, changes: toPush.changes })
|
|
19697
20084
|
});
|
|
19698
20085
|
if (!res.ok)
|
|
@@ -19704,6 +20091,55 @@ async function syncWithPeer(db, url2) {
|
|
|
19704
20091
|
return { pushed: toPush.changes.length, pulled };
|
|
19705
20092
|
}
|
|
19706
20093
|
|
|
20094
|
+
// src/core/sync/peers.ts
|
|
20095
|
+
function listPeers(db) {
|
|
20096
|
+
return db.query("SELECT url, pull_cursor, push_cursor, token, label, node_id, enabled, last_sync_at, last_status, last_error FROM peers ORDER BY url").all();
|
|
20097
|
+
}
|
|
20098
|
+
function addPeer(db, input) {
|
|
20099
|
+
db.query(`INSERT INTO peers (url, token, label, node_id, enabled, pull_cursor, push_cursor)
|
|
20100
|
+
VALUES (?, ?, ?, ?, 1, 0, 0)
|
|
20101
|
+
ON CONFLICT(url) DO UPDATE SET
|
|
20102
|
+
token = coalesce(excluded.token, peers.token),
|
|
20103
|
+
label = coalesce(excluded.label, peers.label),
|
|
20104
|
+
node_id = coalesce(excluded.node_id, peers.node_id),
|
|
20105
|
+
enabled = 1`).run(input.url, input.token ?? null, input.label ?? null, input.node_id ?? null);
|
|
20106
|
+
}
|
|
20107
|
+
function removePeer(db, url2) {
|
|
20108
|
+
const tx = db.transaction(() => {
|
|
20109
|
+
const changed = db.query("DELETE FROM peers WHERE url = ?").run(url2).changes > 0;
|
|
20110
|
+
db.query("DELETE FROM peer_grants WHERE peer_url = ?").run(url2);
|
|
20111
|
+
return changed;
|
|
20112
|
+
});
|
|
20113
|
+
return tx();
|
|
20114
|
+
}
|
|
20115
|
+
function setPeerEnabled(db, url2, enabled) {
|
|
20116
|
+
return db.query("UPDATE peers SET enabled = ? WHERE url = ?").run(enabled ? 1 : 0, url2).changes > 0;
|
|
20117
|
+
}
|
|
20118
|
+
function setPeerLabel(db, url2, label) {
|
|
20119
|
+
return db.query("UPDATE peers SET label = ? WHERE url = ?").run(label, url2).changes > 0;
|
|
20120
|
+
}
|
|
20121
|
+
function updatePeerStatus(db, url2, status, error51) {
|
|
20122
|
+
db.query("UPDATE peers SET last_sync_at = ?, last_status = ?, last_error = ? WHERE url = ?").run(Date.now(), status, error51 ?? null, url2);
|
|
20123
|
+
}
|
|
20124
|
+
async function syncPeer(db, url2) {
|
|
20125
|
+
try {
|
|
20126
|
+
const result = await syncWithPeer(db, url2);
|
|
20127
|
+
updatePeerStatus(db, url2, "ok", null);
|
|
20128
|
+
return { url: url2, ok: true, pushed: result.pushed, pulled: result.pulled };
|
|
20129
|
+
} catch (e2) {
|
|
20130
|
+
const error51 = e2.message;
|
|
20131
|
+
updatePeerStatus(db, url2, "error", error51);
|
|
20132
|
+
return { url: url2, ok: false, error: error51 };
|
|
20133
|
+
}
|
|
20134
|
+
}
|
|
20135
|
+
async function syncAllPeers(db) {
|
|
20136
|
+
const peers = listPeers(db).filter((p) => p.enabled);
|
|
20137
|
+
const out = [];
|
|
20138
|
+
for (const p of peers)
|
|
20139
|
+
out.push(await syncPeer(db, p.url));
|
|
20140
|
+
return out;
|
|
20141
|
+
}
|
|
20142
|
+
|
|
19707
20143
|
// src/core/csv.ts
|
|
19708
20144
|
var NEEDS_QUOTE = /[",\r\n]/;
|
|
19709
20145
|
function encodeCell(value) {
|
|
@@ -19856,6 +20292,31 @@ async function syncFiles(db, src2, dst) {
|
|
|
19856
20292
|
}
|
|
19857
20293
|
|
|
19858
20294
|
// src/cli/commands/sync.ts
|
|
20295
|
+
function isAuthError(e2) {
|
|
20296
|
+
return e2 instanceof Error && /\b401\b|unauthorized/i.test(e2.message);
|
|
20297
|
+
}
|
|
20298
|
+
var isTTY = () => Boolean(process.stdout.isTTY && process.stdin.isTTY);
|
|
20299
|
+
async function peerSync(db, url2, token) {
|
|
20300
|
+
try {
|
|
20301
|
+
const result = await syncWithPeer(db, url2, token);
|
|
20302
|
+
if (token)
|
|
20303
|
+
addPeer(db, { url: url2, token });
|
|
20304
|
+
return result;
|
|
20305
|
+
} catch (e2) {
|
|
20306
|
+
if (token || !isAuthError(e2))
|
|
20307
|
+
throw e2;
|
|
20308
|
+
if (!isTTY()) {
|
|
20309
|
+
throw new Error(`${e2.message}
|
|
20310
|
+
this server requires a token \u2014 pass --token <token> or run in an interactive terminal`);
|
|
20311
|
+
}
|
|
20312
|
+
const entered = (globalThis.prompt("\u8BE5\u670D\u52A1\u5668\u9700\u8981\u4EE4\u724C\uFF0C\u8BF7\u8F93\u5165 token:") ?? "").trim();
|
|
20313
|
+
if (!entered)
|
|
20314
|
+
throw new Error("no token provided");
|
|
20315
|
+
const result = await syncWithPeer(db, url2, entered);
|
|
20316
|
+
addPeer(db, { url: url2, token: entered });
|
|
20317
|
+
return result;
|
|
20318
|
+
}
|
|
20319
|
+
}
|
|
19859
20320
|
var sync_default = defineCommand({
|
|
19860
20321
|
meta: {
|
|
19861
20322
|
name: "sync",
|
|
@@ -19871,12 +20332,16 @@ var sync_default = defineCommand({
|
|
|
19871
20332
|
type: "positional",
|
|
19872
20333
|
required: false,
|
|
19873
20334
|
description: "File path or entity ref; when given, export/import instead of peer sync"
|
|
20335
|
+
},
|
|
20336
|
+
token: {
|
|
20337
|
+
type: "string",
|
|
20338
|
+
description: "Auth token for a protected sync server (saved for next time)"
|
|
19874
20339
|
}
|
|
19875
20340
|
},
|
|
19876
20341
|
run: guard(async (args) => {
|
|
19877
20342
|
const db = openMetahub();
|
|
19878
20343
|
if (args.dst == null) {
|
|
19879
|
-
const result = await
|
|
20344
|
+
const result = await peerSync(db, args.src, args.token);
|
|
19880
20345
|
return print(result, () => `pushed ${result.pushed}, pulled ${result.pulled}`);
|
|
19881
20346
|
}
|
|
19882
20347
|
const r3 = await syncFiles(db, args.src, args.dst);
|
|
@@ -19888,11 +20353,262 @@ var sync_default = defineCommand({
|
|
|
19888
20353
|
init_crdt();
|
|
19889
20354
|
import { readdirSync, existsSync } from "fs";
|
|
19890
20355
|
import { join as join5 } from "path";
|
|
20356
|
+
|
|
20357
|
+
// src/core/integrity.ts
|
|
20358
|
+
init_crdt();
|
|
20359
|
+
var REF_RULES = [
|
|
20360
|
+
{ srcTable: "properties", fk: "database_id", tgtTable: "databases", fix: "tombstone-src" },
|
|
20361
|
+
{ srcTable: "records", fk: "database_id", tgtTable: "databases", fix: "tombstone-src" },
|
|
20362
|
+
{ srcTable: "documents", fk: "database_id", tgtTable: "databases", fix: "null-fk" },
|
|
20363
|
+
{ srcTable: "documents", fk: "parent_id", tgtTable: "documents", fix: "null-fk" },
|
|
20364
|
+
{ srcTable: "doc_blocks", fk: "doc_id", tgtTable: "documents", fix: "tombstone-src" },
|
|
20365
|
+
{ srcTable: "site_files", fk: "site_id", tgtTable: "sites", fix: "tombstone-src" }
|
|
20366
|
+
];
|
|
20367
|
+
function brokenRefRows(db, r3) {
|
|
20368
|
+
return db.query(`SELECT s.id AS id, s.${r3.fk} AS ref FROM ${r3.srcTable} s
|
|
20369
|
+
JOIN ${r3.tgtTable} t ON t.id = s.${r3.fk} AND t.__deleted = 1
|
|
20370
|
+
WHERE s.__deleted = 0 AND s.${r3.fk} IS NOT NULL
|
|
20371
|
+
ORDER BY s.id`).all();
|
|
20372
|
+
}
|
|
20373
|
+
function detectBrokenRefs(db) {
|
|
20374
|
+
const out = [];
|
|
20375
|
+
for (const r3 of REF_RULES) {
|
|
20376
|
+
for (const row of brokenRefRows(db, r3)) {
|
|
20377
|
+
out.push({
|
|
20378
|
+
category: "broken_ref",
|
|
20379
|
+
entity: r3.srcTable,
|
|
20380
|
+
id: row.id,
|
|
20381
|
+
detail: `${r3.fk} \u2192 deleted ${r3.tgtTable} ${row.ref} (${r3.fix === "tombstone-src" ? "tombstone" : `clear ${r3.fk}`})`,
|
|
20382
|
+
fixable: true
|
|
20383
|
+
});
|
|
20384
|
+
}
|
|
20385
|
+
}
|
|
20386
|
+
return out;
|
|
20387
|
+
}
|
|
20388
|
+
function repairBrokenRefs(db) {
|
|
20389
|
+
let n2 = 0;
|
|
20390
|
+
for (const r3 of REF_RULES) {
|
|
20391
|
+
for (const row of brokenRefRows(db, r3)) {
|
|
20392
|
+
if (r3.fix === "tombstone-src")
|
|
20393
|
+
emit(db, r3.srcTable, row.id, "__deleted", 1);
|
|
20394
|
+
else
|
|
20395
|
+
emit(db, r3.srcTable, row.id, r3.fk, null);
|
|
20396
|
+
n2++;
|
|
20397
|
+
}
|
|
20398
|
+
}
|
|
20399
|
+
return n2;
|
|
20400
|
+
}
|
|
20401
|
+
function orphanCellRows(db) {
|
|
20402
|
+
return db.query(`SELECT r.id AS rec, j.key AS prop
|
|
20403
|
+
FROM records r, json_each(coalesce(r.data, '{}')) j
|
|
20404
|
+
JOIN properties p ON p.id = j.key AND p.__deleted = 1
|
|
20405
|
+
WHERE r.__deleted = 0
|
|
20406
|
+
ORDER BY r.id, j.key`).all();
|
|
20407
|
+
}
|
|
20408
|
+
function detectOrphanCells(db) {
|
|
20409
|
+
return orphanCellRows(db).map((row) => ({
|
|
20410
|
+
category: "orphan_cell",
|
|
20411
|
+
entity: "records",
|
|
20412
|
+
id: row.rec,
|
|
20413
|
+
detail: `cell for deleted property ${row.prop}`,
|
|
20414
|
+
fixable: true
|
|
20415
|
+
}));
|
|
20416
|
+
}
|
|
20417
|
+
function repairOrphanCells(db) {
|
|
20418
|
+
const rows = orphanCellRows(db);
|
|
20419
|
+
for (const row of rows)
|
|
20420
|
+
emit(db, "records", row.rec, row.prop, undefined);
|
|
20421
|
+
return rows.length;
|
|
20422
|
+
}
|
|
20423
|
+
function dupPathLosers(db) {
|
|
20424
|
+
const rows = db.query(`SELECT id, site_id, path FROM site_files
|
|
20425
|
+
WHERE __deleted = 0 AND site_id IS NOT NULL AND path IS NOT NULL
|
|
20426
|
+
ORDER BY site_id, path, created_hlc, id`).all();
|
|
20427
|
+
const losers = [];
|
|
20428
|
+
let group = null;
|
|
20429
|
+
for (const r3 of rows) {
|
|
20430
|
+
const key = `${r3.site_id}\x00${r3.path}`;
|
|
20431
|
+
if (key === group)
|
|
20432
|
+
losers.push({ id: r3.id });
|
|
20433
|
+
else
|
|
20434
|
+
group = key;
|
|
20435
|
+
}
|
|
20436
|
+
return losers;
|
|
20437
|
+
}
|
|
20438
|
+
function detectDupPaths(db) {
|
|
20439
|
+
return dupPathLosers(db).map((row) => ({
|
|
20440
|
+
category: "dup_path",
|
|
20441
|
+
entity: "site_files",
|
|
20442
|
+
id: row.id,
|
|
20443
|
+
detail: "redundant duplicate (site_id, path); loser tombstoned",
|
|
20444
|
+
fixable: true
|
|
20445
|
+
}));
|
|
20446
|
+
}
|
|
20447
|
+
function repairDupPaths(db) {
|
|
20448
|
+
const losers = dupPathLosers(db);
|
|
20449
|
+
for (const row of losers)
|
|
20450
|
+
emit(db, "site_files", row.id, "__deleted", 1);
|
|
20451
|
+
return losers.length;
|
|
20452
|
+
}
|
|
20453
|
+
function detectParentCycles(db) {
|
|
20454
|
+
const rows = db.query(`SELECT d.id AS id, d.parent_id AS parent, d.created_hlc AS hlc
|
|
20455
|
+
FROM documents d
|
|
20456
|
+
WHERE d.__deleted = 0 AND d.parent_id IS NOT NULL
|
|
20457
|
+
AND EXISTS (SELECT 1 FROM documents p WHERE p.id = d.parent_id AND p.__deleted = 0)
|
|
20458
|
+
ORDER BY d.id`).all();
|
|
20459
|
+
const parent = new Map(rows.map((r3) => [r3.id, r3.parent]));
|
|
20460
|
+
const hlc = new Map(rows.map((r3) => [r3.id, r3.hlc]));
|
|
20461
|
+
const seen = new Set;
|
|
20462
|
+
const cycles = [];
|
|
20463
|
+
for (const start of parent.keys()) {
|
|
20464
|
+
if (seen.has(start))
|
|
20465
|
+
continue;
|
|
20466
|
+
const path = [];
|
|
20467
|
+
const pos = new Map;
|
|
20468
|
+
let cur = start;
|
|
20469
|
+
while (cur != null && parent.has(cur) && !seen.has(cur)) {
|
|
20470
|
+
if (pos.has(cur)) {
|
|
20471
|
+
cycles.push(path.slice(pos.get(cur)));
|
|
20472
|
+
break;
|
|
20473
|
+
}
|
|
20474
|
+
pos.set(cur, path.length);
|
|
20475
|
+
path.push(cur);
|
|
20476
|
+
cur = parent.get(cur);
|
|
20477
|
+
}
|
|
20478
|
+
for (const n2 of path)
|
|
20479
|
+
seen.add(n2);
|
|
20480
|
+
}
|
|
20481
|
+
return { cycles, hlc };
|
|
20482
|
+
}
|
|
20483
|
+
function geHlcId(hlc, a2, b2) {
|
|
20484
|
+
const ha = hlc.get(a2) ?? "";
|
|
20485
|
+
const hb = hlc.get(b2) ?? "";
|
|
20486
|
+
if (ha !== hb)
|
|
20487
|
+
return ha > hb;
|
|
20488
|
+
return a2 >= b2;
|
|
20489
|
+
}
|
|
20490
|
+
function detectCycleIssues(db) {
|
|
20491
|
+
const { cycles } = detectParentCycles(db);
|
|
20492
|
+
return cycles.map((cyc) => ({
|
|
20493
|
+
category: "parent_cycle",
|
|
20494
|
+
entity: "documents",
|
|
20495
|
+
id: [...cyc].sort()[0],
|
|
20496
|
+
detail: `parent cycle: ${[...cyc].sort().join(" \u2192 ")}`,
|
|
20497
|
+
fixable: true
|
|
20498
|
+
}));
|
|
20499
|
+
}
|
|
20500
|
+
function repairParentCycles(db) {
|
|
20501
|
+
const { cycles, hlc } = detectParentCycles(db);
|
|
20502
|
+
for (const cyc of cycles) {
|
|
20503
|
+
const brk = cyc.reduce((a2, b2) => geHlcId(hlc, a2, b2) ? a2 : b2);
|
|
20504
|
+
emit(db, "documents", brk, "parent_id", null);
|
|
20505
|
+
}
|
|
20506
|
+
return cycles.length;
|
|
20507
|
+
}
|
|
20508
|
+
function detectDupNames(db) {
|
|
20509
|
+
const out = [];
|
|
20510
|
+
const props = db.query(`SELECT database_id AS db, lower(name) AS n, count(*) AS c, group_concat(id) AS ids
|
|
20511
|
+
FROM properties
|
|
20512
|
+
WHERE __deleted = 0 AND name IS NOT NULL AND database_id IS NOT NULL
|
|
20513
|
+
GROUP BY database_id, lower(name) HAVING c > 1
|
|
20514
|
+
ORDER BY database_id, n`).all();
|
|
20515
|
+
for (const r3 of props)
|
|
20516
|
+
out.push({
|
|
20517
|
+
category: "dup_name",
|
|
20518
|
+
entity: "properties",
|
|
20519
|
+
id: r3.ids.split(",").sort()[0],
|
|
20520
|
+
detail: `${r3.c} properties named "${r3.n}" in database ${r3.db}: ${r3.ids}`,
|
|
20521
|
+
fixable: false
|
|
20522
|
+
});
|
|
20523
|
+
const dbs = db.query(`SELECT lower(name) AS n, count(*) AS c, group_concat(id) AS ids
|
|
20524
|
+
FROM databases WHERE __deleted = 0 AND name IS NOT NULL
|
|
20525
|
+
GROUP BY lower(name) HAVING c > 1 ORDER BY n`).all();
|
|
20526
|
+
for (const r3 of dbs)
|
|
20527
|
+
out.push({
|
|
20528
|
+
category: "dup_name",
|
|
20529
|
+
entity: "databases",
|
|
20530
|
+
id: r3.ids.split(",").sort()[0],
|
|
20531
|
+
detail: `${r3.c} databases named "${r3.n}": ${r3.ids}`,
|
|
20532
|
+
fixable: false
|
|
20533
|
+
});
|
|
20534
|
+
return out;
|
|
20535
|
+
}
|
|
20536
|
+
function configIssue(type, config2) {
|
|
20537
|
+
if (!PROP_TYPES.has(type))
|
|
20538
|
+
return `unknown property type: ${type}`;
|
|
20539
|
+
if (type === "select" || type === "multi_select") {
|
|
20540
|
+
const opts = config2?.options;
|
|
20541
|
+
if (!Array.isArray(opts) || opts.length === 0 || !opts.every((o3) => typeof o3 === "string"))
|
|
20542
|
+
return `${type} missing config.options: string[]`;
|
|
20543
|
+
}
|
|
20544
|
+
if (type === "relation" && typeof config2?.database !== "string")
|
|
20545
|
+
return "relation missing config.database";
|
|
20546
|
+
return null;
|
|
20547
|
+
}
|
|
20548
|
+
function detectBadConfig(db) {
|
|
20549
|
+
const rows = db.query("SELECT id, type, config FROM properties WHERE __deleted = 0 ORDER BY id").all();
|
|
20550
|
+
const out = [];
|
|
20551
|
+
for (const r3 of rows) {
|
|
20552
|
+
let config2 = null;
|
|
20553
|
+
try {
|
|
20554
|
+
config2 = r3.config ? JSON.parse(r3.config) : null;
|
|
20555
|
+
} catch {
|
|
20556
|
+
out.push({ category: "bad_config", entity: "properties", id: r3.id, detail: "config is not valid JSON", fixable: false });
|
|
20557
|
+
continue;
|
|
20558
|
+
}
|
|
20559
|
+
const problem = configIssue(r3.type, config2);
|
|
20560
|
+
if (problem)
|
|
20561
|
+
out.push({ category: "bad_config", entity: "properties", id: r3.id, detail: problem, fixable: false });
|
|
20562
|
+
}
|
|
20563
|
+
return out;
|
|
20564
|
+
}
|
|
20565
|
+
function validateHub(db) {
|
|
20566
|
+
const issues = [
|
|
20567
|
+
...detectBrokenRefs(db),
|
|
20568
|
+
...detectOrphanCells(db),
|
|
20569
|
+
...detectDupPaths(db),
|
|
20570
|
+
...detectCycleIssues(db),
|
|
20571
|
+
...detectDupNames(db),
|
|
20572
|
+
...detectBadConfig(db)
|
|
20573
|
+
];
|
|
20574
|
+
const counts = {};
|
|
20575
|
+
for (const i2 of issues)
|
|
20576
|
+
counts[i2.category] = (counts[i2.category] ?? 0) + 1;
|
|
20577
|
+
return { ok: issues.length === 0, total: issues.length, counts, issues };
|
|
20578
|
+
}
|
|
20579
|
+
function repairPass(db) {
|
|
20580
|
+
const fixed = {};
|
|
20581
|
+
const bump = (cat, n2) => {
|
|
20582
|
+
if (n2)
|
|
20583
|
+
fixed[cat] = (fixed[cat] ?? 0) + n2;
|
|
20584
|
+
};
|
|
20585
|
+
bump("broken_ref", repairBrokenRefs(db));
|
|
20586
|
+
bump("orphan_cell", repairOrphanCells(db));
|
|
20587
|
+
bump("dup_path", repairDupPaths(db));
|
|
20588
|
+
bump("parent_cycle", repairParentCycles(db));
|
|
20589
|
+
const applied = Object.values(fixed).reduce((a2, b2) => a2 + b2, 0);
|
|
20590
|
+
return { applied, fixed };
|
|
20591
|
+
}
|
|
20592
|
+
function repairHub(db) {
|
|
20593
|
+
const fixed = {};
|
|
20594
|
+
let applied = 0;
|
|
20595
|
+
for (let i2 = 0;i2 < 20; i2++) {
|
|
20596
|
+
const pass = repairPass(db);
|
|
20597
|
+
if (pass.applied === 0)
|
|
20598
|
+
break;
|
|
20599
|
+
applied += pass.applied;
|
|
20600
|
+
for (const [cat, n2] of Object.entries(pass.fixed))
|
|
20601
|
+
fixed[cat] = (fixed[cat] ?? 0) + n2;
|
|
20602
|
+
}
|
|
20603
|
+
return { applied, fixed, remaining: validateHub(db) };
|
|
20604
|
+
}
|
|
20605
|
+
|
|
20606
|
+
// src/core/snapshot.ts
|
|
19891
20607
|
init_paths();
|
|
19892
20608
|
init_cache();
|
|
19893
20609
|
var SNAPSHOT_FORMAT = "metahub-snapshot";
|
|
19894
20610
|
var SNAPSHOT_VERSION = 1;
|
|
19895
|
-
function
|
|
20611
|
+
function readMeta2(db, key) {
|
|
19896
20612
|
const row = db.query("SELECT value FROM meta WHERE key = ?").get(key);
|
|
19897
20613
|
return row?.value ?? null;
|
|
19898
20614
|
}
|
|
@@ -19909,8 +20625,8 @@ async function readBlobs() {
|
|
|
19909
20625
|
}
|
|
19910
20626
|
async function createSnapshot(db) {
|
|
19911
20627
|
const changes = changesAfterSeq(db, 0).changes;
|
|
19912
|
-
const node_id =
|
|
19913
|
-
const hlc =
|
|
20628
|
+
const node_id = readMeta2(db, "node_id");
|
|
20629
|
+
const hlc = readMeta2(db, "hlc");
|
|
19914
20630
|
const peers = db.query("SELECT url, pull_cursor, push_cursor FROM peers").all();
|
|
19915
20631
|
const blobs = await readBlobs();
|
|
19916
20632
|
return {
|
|
@@ -19971,8 +20687,9 @@ async function restoreSnapshot(db, pkg, opts = {}) {
|
|
|
19971
20687
|
if (!opts.reset) {
|
|
19972
20688
|
const applied2 = ingest(db, pkg.changes);
|
|
19973
20689
|
rebuildDeclaredIndexes(db);
|
|
20690
|
+
const repaired2 = repairHub(db).applied;
|
|
19974
20691
|
const blobs2 = await writeBlobs(pkg.blobs);
|
|
19975
|
-
return { mode: "merge", applied: applied2, blobs: blobs2 };
|
|
20692
|
+
return { mode: "merge", applied: applied2, blobs: blobs2, repaired: repaired2 };
|
|
19976
20693
|
}
|
|
19977
20694
|
if (!opts.force)
|
|
19978
20695
|
throw new Error("refusing to replace local data without --force (a safety snapshot is saved first)");
|
|
@@ -19990,7 +20707,7 @@ async function restoreSnapshot(db, pkg, opts = {}) {
|
|
|
19990
20707
|
"peers"
|
|
19991
20708
|
])
|
|
19992
20709
|
db.query(`DELETE FROM ${t2}`).run();
|
|
19993
|
-
db.query("DELETE FROM meta WHERE key
|
|
20710
|
+
db.query("DELETE FROM meta WHERE key IN ('search_seq', 'search_index_version')").run();
|
|
19994
20711
|
if (ftsAvailable(db))
|
|
19995
20712
|
db.query("DELETE FROM search_fts").run();
|
|
19996
20713
|
const setMeta2 = db.query("INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value");
|
|
@@ -20004,8 +20721,9 @@ async function restoreSnapshot(db, pkg, opts = {}) {
|
|
|
20004
20721
|
});
|
|
20005
20722
|
tx();
|
|
20006
20723
|
rebuildDeclaredIndexes(db);
|
|
20724
|
+
const repaired = repairHub(db).applied;
|
|
20007
20725
|
const blobs = await writeBlobs(pkg.blobs);
|
|
20008
|
-
return { mode: "reset", applied, blobs, safetyPath };
|
|
20726
|
+
return { mode: "reset", applied, blobs, repaired, safetyPath };
|
|
20009
20727
|
}
|
|
20010
20728
|
|
|
20011
20729
|
// src/cli/commands/snapshot.ts
|
|
@@ -20054,93 +20772,1980 @@ var restore_default = defineCommand({
|
|
|
20054
20772
|
reset: !!args.reset,
|
|
20055
20773
|
force: !!args.force
|
|
20056
20774
|
});
|
|
20057
|
-
print(result, () => result.mode === "reset" ? `Restored (reset) from ${args.file}: ${result.applied} changes, ${result.blobs} blobs. Safety snapshot: ${result.safetyPath}` : `Merged ${args.file}: ${result.applied} new changes, ${result.blobs} blobs`);
|
|
20775
|
+
print(result, () => result.mode === "reset" ? `Restored (reset) from ${args.file}: ${result.applied} changes, ${result.blobs} blobs, ${result.repaired} repairs. Safety snapshot: ${result.safetyPath}` : `Merged ${args.file}: ${result.applied} new changes, ${result.blobs} blobs, ${result.repaired} repairs`);
|
|
20058
20776
|
})
|
|
20059
20777
|
});
|
|
20060
20778
|
|
|
20061
|
-
// src/
|
|
20062
|
-
|
|
20063
|
-
|
|
20064
|
-
|
|
20065
|
-
|
|
20779
|
+
// src/cli/commands/doctor.ts
|
|
20780
|
+
var doctor_default = defineCommand({
|
|
20781
|
+
meta: {
|
|
20782
|
+
name: "doctor",
|
|
20783
|
+
description: "Scan the hub for logical-integrity issues (read-only; run `mh repair` to fix)"
|
|
20784
|
+
},
|
|
20785
|
+
run: () => {
|
|
20786
|
+
const report = validateHub(openMetahub());
|
|
20787
|
+
print(report, () => {
|
|
20788
|
+
if (report.ok)
|
|
20789
|
+
return "No integrity issues found.";
|
|
20790
|
+
const head = `${report.total} issue(s): ${Object.entries(report.counts).map(([k2, v2]) => `${k2}=${v2}`).join(", ")}`;
|
|
20791
|
+
const rows = report.issues.map((i2) => ({
|
|
20792
|
+
category: i2.category,
|
|
20793
|
+
entity: i2.entity,
|
|
20794
|
+
id: i2.id,
|
|
20795
|
+
fix: i2.fixable ? "auto" : "report",
|
|
20796
|
+
detail: i2.detail
|
|
20797
|
+
}));
|
|
20798
|
+
return `${head}
|
|
20066
20799
|
|
|
20067
|
-
|
|
20068
|
-
|
|
20069
|
-
|
|
20070
|
-
name: exports_external.string(),
|
|
20071
|
-
icon: exports_external.string().nullable(),
|
|
20072
|
-
created_hlc: exports_external.string()
|
|
20073
|
-
});
|
|
20074
|
-
var PropertySchema = exports_external.object({
|
|
20075
|
-
id: exports_external.string(),
|
|
20076
|
-
database_id: exports_external.string(),
|
|
20077
|
-
name: exports_external.string(),
|
|
20078
|
-
type: exports_external.string(),
|
|
20079
|
-
config: exports_external.any().nullable(),
|
|
20080
|
-
position: exports_external.number()
|
|
20081
|
-
});
|
|
20082
|
-
var RecordSchema = exports_external.object({
|
|
20083
|
-
id: exports_external.string(),
|
|
20084
|
-
database_id: exports_external.string(),
|
|
20085
|
-
values: exports_external.record(exports_external.string(), exports_external.any())
|
|
20086
|
-
});
|
|
20087
|
-
var MoveRecordReq = exports_external.object({
|
|
20088
|
-
target: exports_external.string(),
|
|
20089
|
-
where: exports_external.enum(["before", "after"])
|
|
20090
|
-
});
|
|
20091
|
-
var DocumentSummarySchema = exports_external.object({
|
|
20092
|
-
id: exports_external.string(),
|
|
20093
|
-
title: exports_external.string(),
|
|
20094
|
-
database_id: exports_external.string().nullable(),
|
|
20095
|
-
parent_id: exports_external.string().nullable(),
|
|
20096
|
-
created_hlc: exports_external.string()
|
|
20097
|
-
});
|
|
20098
|
-
var DocumentSchema = DocumentSummarySchema.extend({ body: exports_external.string().nullable() });
|
|
20099
|
-
var SearchHitSchema = exports_external.object({
|
|
20100
|
-
type: exports_external.string(),
|
|
20101
|
-
id: exports_external.string(),
|
|
20102
|
-
database_id: exports_external.string().nullable(),
|
|
20103
|
-
title: exports_external.string().optional(),
|
|
20104
|
-
snippet: exports_external.string()
|
|
20105
|
-
});
|
|
20106
|
-
var OkSchema = exports_external.object({ ok: exports_external.boolean() });
|
|
20107
|
-
var CreateDatabaseReq = exports_external.object({ name: exports_external.string(), icon: exports_external.string().optional() });
|
|
20108
|
-
var UpdateDatabaseReq = exports_external.object({
|
|
20109
|
-
name: exports_external.string().optional(),
|
|
20110
|
-
icon: exports_external.string().nullable().optional()
|
|
20111
|
-
});
|
|
20112
|
-
var CreatePropertyReq = exports_external.object({
|
|
20113
|
-
db: exports_external.string(),
|
|
20114
|
-
name: exports_external.string(),
|
|
20115
|
-
type: exports_external.string(),
|
|
20116
|
-
config: exports_external.any().optional()
|
|
20117
|
-
});
|
|
20118
|
-
var UpdatePropertyReq = exports_external.object({
|
|
20119
|
-
name: exports_external.string().optional(),
|
|
20120
|
-
type: exports_external.string().optional(),
|
|
20121
|
-
config: exports_external.any().optional(),
|
|
20122
|
-
position: exports_external.number().optional()
|
|
20123
|
-
});
|
|
20124
|
-
var SetWidthReq = exports_external.object({ width: exports_external.number() });
|
|
20125
|
-
var RecordValuesReq = exports_external.record(exports_external.string(), exports_external.any());
|
|
20126
|
-
var CreateDocumentReq = exports_external.object({
|
|
20127
|
-
title: exports_external.string(),
|
|
20128
|
-
body: exports_external.string().optional(),
|
|
20129
|
-
database_id: exports_external.string().optional()
|
|
20800
|
+
${table(rows)}`;
|
|
20801
|
+
});
|
|
20802
|
+
}
|
|
20130
20803
|
});
|
|
20131
|
-
|
|
20132
|
-
|
|
20133
|
-
|
|
20134
|
-
|
|
20804
|
+
|
|
20805
|
+
// src/cli/commands/repair.ts
|
|
20806
|
+
var repair_default = defineCommand({
|
|
20807
|
+
meta: {
|
|
20808
|
+
name: "repair",
|
|
20809
|
+
description: "Deterministically fix logical-integrity issues (idempotent; replicates over sync)"
|
|
20810
|
+
},
|
|
20811
|
+
args: {
|
|
20812
|
+
"dry-run": {
|
|
20813
|
+
type: "boolean",
|
|
20814
|
+
description: "Report what would be fixed without changing anything (same as `mh doctor`)"
|
|
20815
|
+
}
|
|
20816
|
+
},
|
|
20817
|
+
run: ({ args }) => {
|
|
20818
|
+
const db = openMetahub();
|
|
20819
|
+
if (args["dry-run"]) {
|
|
20820
|
+
const report = validateHub(db);
|
|
20821
|
+
const fixable = report.issues.filter((i2) => i2.fixable);
|
|
20822
|
+
print({ dryRun: true, ...report }, () => report.ok ? "No integrity issues found." : `Would fix ${fixable.length} of ${report.total} issue(s); ${report.total - fixable.length} report-only.
|
|
20823
|
+
|
|
20824
|
+
` + table(report.issues.map((i2) => ({
|
|
20825
|
+
category: i2.category,
|
|
20826
|
+
id: i2.id,
|
|
20827
|
+
fix: i2.fixable ? "auto" : "report",
|
|
20828
|
+
detail: i2.detail
|
|
20829
|
+
}))));
|
|
20830
|
+
return;
|
|
20831
|
+
}
|
|
20832
|
+
const result = repairHub(db);
|
|
20833
|
+
print(result, () => {
|
|
20834
|
+
const fixed = Object.entries(result.fixed).map(([k2, v2]) => `${k2}=${v2}`).join(", ");
|
|
20835
|
+
const head = result.applied === 0 ? "No fixable issues." : `Applied ${result.applied} repair(s): ${fixed}`;
|
|
20836
|
+
const left = result.remaining.issues;
|
|
20837
|
+
if (left.length === 0)
|
|
20838
|
+
return head;
|
|
20839
|
+
return `${head}
|
|
20840
|
+
${left.length} report-only issue(s) need manual attention:
|
|
20841
|
+
|
|
20842
|
+
` + table(left.map((i2) => ({ category: i2.category, id: i2.id, detail: i2.detail })));
|
|
20843
|
+
});
|
|
20844
|
+
}
|
|
20135
20845
|
});
|
|
20136
|
-
|
|
20137
|
-
|
|
20138
|
-
|
|
20139
|
-
|
|
20140
|
-
|
|
20846
|
+
|
|
20847
|
+
// src/cli/commands/config.ts
|
|
20848
|
+
init_node();
|
|
20849
|
+
|
|
20850
|
+
// src/core/config.ts
|
|
20851
|
+
var K_HOST = "cfg_host";
|
|
20852
|
+
var K_PORT = "cfg_port";
|
|
20853
|
+
var K_SYNC_INTERVAL = "cfg_sync_interval";
|
|
20854
|
+
var K_AUTO_SYNC = "cfg_auto_sync";
|
|
20855
|
+
var DEFAULT_CONFIG = {
|
|
20856
|
+
host: "127.0.0.1",
|
|
20857
|
+
port: 7777,
|
|
20858
|
+
syncIntervalMs: parseDuration(process.env.METAHUB_SYNC_INTERVAL, 30000),
|
|
20859
|
+
autoSync: true
|
|
20860
|
+
};
|
|
20861
|
+
function getMeta2(db, key) {
|
|
20862
|
+
const row = db.query("SELECT value FROM meta WHERE key = ?").get(key);
|
|
20863
|
+
return row ? row.value : null;
|
|
20141
20864
|
}
|
|
20142
|
-
function
|
|
20143
|
-
|
|
20865
|
+
function setMeta2(db, key, value) {
|
|
20866
|
+
db.query("INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(key, value);
|
|
20867
|
+
}
|
|
20868
|
+
function getServerConfig(db) {
|
|
20869
|
+
const host = getMeta2(db, K_HOST);
|
|
20870
|
+
const port = getMeta2(db, K_PORT);
|
|
20871
|
+
const interval = getMeta2(db, K_SYNC_INTERVAL);
|
|
20872
|
+
const auto = getMeta2(db, K_AUTO_SYNC);
|
|
20873
|
+
return {
|
|
20874
|
+
host: host ?? DEFAULT_CONFIG.host,
|
|
20875
|
+
port: port != null ? Number(port) : DEFAULT_CONFIG.port,
|
|
20876
|
+
syncIntervalMs: interval != null ? Number(interval) : DEFAULT_CONFIG.syncIntervalMs,
|
|
20877
|
+
autoSync: auto != null ? auto === "1" : DEFAULT_CONFIG.autoSync
|
|
20878
|
+
};
|
|
20879
|
+
}
|
|
20880
|
+
function setServerConfig(db, partial2) {
|
|
20881
|
+
if (partial2.host != null)
|
|
20882
|
+
setMeta2(db, K_HOST, partial2.host);
|
|
20883
|
+
if (partial2.port != null)
|
|
20884
|
+
setMeta2(db, K_PORT, String(partial2.port));
|
|
20885
|
+
if (partial2.syncIntervalMs != null)
|
|
20886
|
+
setMeta2(db, K_SYNC_INTERVAL, String(partial2.syncIntervalMs));
|
|
20887
|
+
if (partial2.autoSync != null)
|
|
20888
|
+
setMeta2(db, K_AUTO_SYNC, partial2.autoSync ? "1" : "0");
|
|
20889
|
+
return getServerConfig(db);
|
|
20890
|
+
}
|
|
20891
|
+
|
|
20892
|
+
// src/core/sync/pairing.ts
|
|
20893
|
+
init_ids();
|
|
20894
|
+
var DEFAULT_PAIR_TTL_MS = parseDuration(process.env.METAHUB_PAIR_TTL, 10 * 60000);
|
|
20895
|
+
function generatePairingCode(db, ttlMs = DEFAULT_PAIR_TTL_MS) {
|
|
20896
|
+
const now = Date.now();
|
|
20897
|
+
db.query("DELETE FROM pairing_codes WHERE used = 1 OR exp < ?").run(now);
|
|
20898
|
+
const code = randomSuffix(12);
|
|
20899
|
+
const exp = now + ttlMs;
|
|
20900
|
+
db.query("INSERT INTO pairing_codes (code, exp, used, created_at) VALUES (?, ?, 0, ?)").run(code, exp, now);
|
|
20901
|
+
return { code, exp };
|
|
20902
|
+
}
|
|
20903
|
+
function redeemPairingCode(db, code) {
|
|
20904
|
+
return db.query("UPDATE pairing_codes SET used = 1 WHERE code = ? AND used = 0 AND exp >= ?").run(code, Date.now()).changes > 0;
|
|
20905
|
+
}
|
|
20906
|
+
function mintGrant(db, peerUrl, nodeId) {
|
|
20907
|
+
const token = randomSuffix(32);
|
|
20908
|
+
db.query("INSERT INTO peer_grants (token, peer_url, node_id, created_at) VALUES (?, ?, ?, ?)").run(token, peerUrl, nodeId, Date.now());
|
|
20909
|
+
return token;
|
|
20910
|
+
}
|
|
20911
|
+
function isAcceptedGrant(db, token) {
|
|
20912
|
+
return db.query("SELECT 1 FROM peer_grants WHERE token = ?").get(token) != null;
|
|
20913
|
+
}
|
|
20914
|
+
function listGrants(db) {
|
|
20915
|
+
return db.query("SELECT token, peer_url, node_id, created_at FROM peer_grants ORDER BY created_at DESC").all();
|
|
20916
|
+
}
|
|
20917
|
+
function revokeGrant(db, tokenOrPrefix) {
|
|
20918
|
+
return db.query("DELETE FROM peer_grants WHERE token = ? OR token LIKE ? || '%'").run(tokenOrPrefix, tokenOrPrefix).changes;
|
|
20919
|
+
}
|
|
20920
|
+
function handlePairRequest(db, node, body) {
|
|
20921
|
+
if (!redeemPairingCode(db, body.code)) {
|
|
20922
|
+
throw new Error("invalid or expired pairing code");
|
|
20923
|
+
}
|
|
20924
|
+
if (body.self_url) {
|
|
20925
|
+
addPeer(db, { url: body.self_url, token: body.grant, node_id: body.node_id });
|
|
20926
|
+
}
|
|
20927
|
+
const grant = mintGrant(db, body.self_url ?? null, body.node_id);
|
|
20928
|
+
return { node_id: node, grant };
|
|
20929
|
+
}
|
|
20930
|
+
async function performPairing(db, node, remoteUrl, code, selfUrl) {
|
|
20931
|
+
const ourGrant = mintGrant(db, remoteUrl, null);
|
|
20932
|
+
const reqBody = { code, node_id: node, grant: ourGrant, self_url: selfUrl };
|
|
20933
|
+
const res = await fetch(new URL(PAIR_PATH, remoteUrl), {
|
|
20934
|
+
method: "POST",
|
|
20935
|
+
headers: { "content-type": "application/json" },
|
|
20936
|
+
body: JSON.stringify(reqBody)
|
|
20937
|
+
});
|
|
20938
|
+
if (!res.ok)
|
|
20939
|
+
throw new Error(`pairing failed: ${res.status} ${await res.text()}`);
|
|
20940
|
+
const data = await res.json();
|
|
20941
|
+
addPeer(db, { url: remoteUrl, token: data.grant, node_id: data.node_id });
|
|
20942
|
+
return { node_id: data.node_id, url: remoteUrl };
|
|
20943
|
+
}
|
|
20944
|
+
|
|
20945
|
+
// node_modules/@clack/core/dist/index.mjs
|
|
20946
|
+
import { styleText as v2 } from "util";
|
|
20947
|
+
import { stdout as x2, stdin as D2 } from "process";
|
|
20948
|
+
import * as b2 from "readline";
|
|
20949
|
+
import G3 from "readline";
|
|
20950
|
+
|
|
20951
|
+
// node_modules/fast-string-truncated-width/dist/utils.js
|
|
20952
|
+
var getCodePointsLength = (() => {
|
|
20953
|
+
const SURROGATE_PAIR_RE = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
|
|
20954
|
+
return (input) => {
|
|
20955
|
+
let surrogatePairsNr = 0;
|
|
20956
|
+
SURROGATE_PAIR_RE.lastIndex = 0;
|
|
20957
|
+
while (SURROGATE_PAIR_RE.test(input)) {
|
|
20958
|
+
surrogatePairsNr += 1;
|
|
20959
|
+
}
|
|
20960
|
+
return input.length - surrogatePairsNr;
|
|
20961
|
+
};
|
|
20962
|
+
})();
|
|
20963
|
+
var isFullWidth2 = (x2) => {
|
|
20964
|
+
return x2 === 12288 || x2 >= 65281 && x2 <= 65376 || x2 >= 65504 && x2 <= 65510;
|
|
20965
|
+
};
|
|
20966
|
+
var isWideNotCJKTNotEmoji = (x2) => {
|
|
20967
|
+
return x2 === 8987 || x2 === 9001 || x2 >= 12272 && x2 <= 12287 || x2 >= 12289 && x2 <= 12350 || x2 >= 12441 && x2 <= 12543 || x2 >= 12549 && x2 <= 12591 || x2 >= 12593 && x2 <= 12686 || x2 >= 12688 && x2 <= 12771 || x2 >= 12783 && x2 <= 12830 || x2 >= 12832 && x2 <= 12871 || x2 >= 12880 && x2 <= 19903 || x2 >= 65040 && x2 <= 65049 || x2 >= 65072 && x2 <= 65106 || x2 >= 65108 && x2 <= 65126 || x2 >= 65128 && x2 <= 65131 || x2 >= 127488 && x2 <= 127490 || x2 >= 127504 && x2 <= 127547 || x2 >= 127552 && x2 <= 127560 || x2 >= 131072 && x2 <= 196605 || x2 >= 196608 && x2 <= 262141;
|
|
20968
|
+
};
|
|
20969
|
+
|
|
20970
|
+
// node_modules/fast-string-truncated-width/dist/index.js
|
|
20971
|
+
var ANSI_RE = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y;
|
|
20972
|
+
var CONTROL_RE = /[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y;
|
|
20973
|
+
var CJKT_WIDE_RE = /(?:(?![\uFF61-\uFF9F\uFF00-\uFFEF])[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Tangut}]){1,1000}/yu;
|
|
20974
|
+
var TAB_RE = /\t{1,1000}/y;
|
|
20975
|
+
var EMOJI_RE = /[\u{1F1E6}-\u{1F1FF}]{2}|\u{1F3F4}[\u{E0061}-\u{E007A}]{2}[\u{E0030}-\u{E0039}\u{E0061}-\u{E007A}]{1,3}\u{E007F}|(?:\p{Emoji}\uFE0F\u20E3?|\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation})(?:\u200D(?:\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F\u20E3?))*/yu;
|
|
20976
|
+
var LATIN_RE = /(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y;
|
|
20977
|
+
var MODIFIER_RE = /\p{M}+/gu;
|
|
20978
|
+
var NO_TRUNCATION = { limit: Infinity, ellipsis: "" };
|
|
20979
|
+
var getStringTruncatedWidth = (input, truncationOptions = {}, widthOptions = {}) => {
|
|
20980
|
+
const LIMIT = truncationOptions.limit ?? Infinity;
|
|
20981
|
+
const ELLIPSIS = truncationOptions.ellipsis ?? "";
|
|
20982
|
+
const ELLIPSIS_WIDTH = truncationOptions?.ellipsisWidth ?? (ELLIPSIS ? getStringTruncatedWidth(ELLIPSIS, NO_TRUNCATION, widthOptions).width : 0);
|
|
20983
|
+
const ANSI_WIDTH = 0;
|
|
20984
|
+
const CONTROL_WIDTH = widthOptions.controlWidth ?? 0;
|
|
20985
|
+
const TAB_WIDTH = widthOptions.tabWidth ?? 8;
|
|
20986
|
+
const EMOJI_WIDTH = widthOptions.emojiWidth ?? 2;
|
|
20987
|
+
const FULL_WIDTH_WIDTH = 2;
|
|
20988
|
+
const REGULAR_WIDTH = widthOptions.regularWidth ?? 1;
|
|
20989
|
+
const WIDE_WIDTH = widthOptions.wideWidth ?? FULL_WIDTH_WIDTH;
|
|
20990
|
+
const PARSE_BLOCKS = [
|
|
20991
|
+
[LATIN_RE, REGULAR_WIDTH],
|
|
20992
|
+
[ANSI_RE, ANSI_WIDTH],
|
|
20993
|
+
[CONTROL_RE, CONTROL_WIDTH],
|
|
20994
|
+
[TAB_RE, TAB_WIDTH],
|
|
20995
|
+
[EMOJI_RE, EMOJI_WIDTH],
|
|
20996
|
+
[CJKT_WIDE_RE, WIDE_WIDTH]
|
|
20997
|
+
];
|
|
20998
|
+
let indexPrev = 0;
|
|
20999
|
+
let index = 0;
|
|
21000
|
+
let length = input.length;
|
|
21001
|
+
let lengthExtra = 0;
|
|
21002
|
+
let truncationEnabled = false;
|
|
21003
|
+
let truncationIndex = length;
|
|
21004
|
+
let truncationLimit = Math.max(0, LIMIT - ELLIPSIS_WIDTH);
|
|
21005
|
+
let unmatchedStart = 0;
|
|
21006
|
+
let unmatchedEnd = 0;
|
|
21007
|
+
let width = 0;
|
|
21008
|
+
let widthExtra = 0;
|
|
21009
|
+
outer:
|
|
21010
|
+
while (true) {
|
|
21011
|
+
if (unmatchedEnd > unmatchedStart || index >= length && index > indexPrev) {
|
|
21012
|
+
const unmatched = input.slice(unmatchedStart, unmatchedEnd) || input.slice(indexPrev, index);
|
|
21013
|
+
lengthExtra = 0;
|
|
21014
|
+
for (const char of unmatched.replaceAll(MODIFIER_RE, "")) {
|
|
21015
|
+
const codePoint = char.codePointAt(0) || 0;
|
|
21016
|
+
if (isFullWidth2(codePoint)) {
|
|
21017
|
+
widthExtra = FULL_WIDTH_WIDTH;
|
|
21018
|
+
} else if (isWideNotCJKTNotEmoji(codePoint)) {
|
|
21019
|
+
widthExtra = WIDE_WIDTH;
|
|
21020
|
+
} else {
|
|
21021
|
+
widthExtra = REGULAR_WIDTH;
|
|
21022
|
+
}
|
|
21023
|
+
if (width + widthExtra > truncationLimit) {
|
|
21024
|
+
truncationIndex = Math.min(truncationIndex, Math.max(unmatchedStart, indexPrev) + lengthExtra);
|
|
21025
|
+
}
|
|
21026
|
+
if (width + widthExtra > LIMIT) {
|
|
21027
|
+
truncationEnabled = true;
|
|
21028
|
+
break outer;
|
|
21029
|
+
}
|
|
21030
|
+
lengthExtra += char.length;
|
|
21031
|
+
width += widthExtra;
|
|
21032
|
+
}
|
|
21033
|
+
unmatchedStart = unmatchedEnd = 0;
|
|
21034
|
+
}
|
|
21035
|
+
if (index >= length) {
|
|
21036
|
+
break outer;
|
|
21037
|
+
}
|
|
21038
|
+
for (let i2 = 0, l2 = PARSE_BLOCKS.length;i2 < l2; i2++) {
|
|
21039
|
+
const [BLOCK_RE, BLOCK_WIDTH] = PARSE_BLOCKS[i2];
|
|
21040
|
+
BLOCK_RE.lastIndex = index;
|
|
21041
|
+
if (BLOCK_RE.test(input)) {
|
|
21042
|
+
lengthExtra = BLOCK_RE === CJKT_WIDE_RE ? getCodePointsLength(input.slice(index, BLOCK_RE.lastIndex)) : BLOCK_RE === EMOJI_RE ? 1 : BLOCK_RE.lastIndex - index;
|
|
21043
|
+
widthExtra = lengthExtra * BLOCK_WIDTH;
|
|
21044
|
+
if (width + widthExtra > truncationLimit) {
|
|
21045
|
+
truncationIndex = Math.min(truncationIndex, index + Math.floor((truncationLimit - width) / BLOCK_WIDTH));
|
|
21046
|
+
}
|
|
21047
|
+
if (width + widthExtra > LIMIT) {
|
|
21048
|
+
truncationEnabled = true;
|
|
21049
|
+
break outer;
|
|
21050
|
+
}
|
|
21051
|
+
width += widthExtra;
|
|
21052
|
+
unmatchedStart = indexPrev;
|
|
21053
|
+
unmatchedEnd = index;
|
|
21054
|
+
index = indexPrev = BLOCK_RE.lastIndex;
|
|
21055
|
+
continue outer;
|
|
21056
|
+
}
|
|
21057
|
+
}
|
|
21058
|
+
index += 1;
|
|
21059
|
+
}
|
|
21060
|
+
return {
|
|
21061
|
+
width: truncationEnabled ? truncationLimit : width,
|
|
21062
|
+
index: truncationEnabled ? truncationIndex : length,
|
|
21063
|
+
truncated: truncationEnabled,
|
|
21064
|
+
ellipsed: truncationEnabled && LIMIT >= ELLIPSIS_WIDTH
|
|
21065
|
+
};
|
|
21066
|
+
};
|
|
21067
|
+
var dist_default = getStringTruncatedWidth;
|
|
21068
|
+
|
|
21069
|
+
// node_modules/fast-string-width/dist/index.js
|
|
21070
|
+
var NO_TRUNCATION2 = {
|
|
21071
|
+
limit: Infinity,
|
|
21072
|
+
ellipsis: "",
|
|
21073
|
+
ellipsisWidth: 0
|
|
21074
|
+
};
|
|
21075
|
+
var fastStringWidth = (input, options = {}) => {
|
|
21076
|
+
return dist_default(input, NO_TRUNCATION2, options).width;
|
|
21077
|
+
};
|
|
21078
|
+
var dist_default2 = fastStringWidth;
|
|
21079
|
+
|
|
21080
|
+
// node_modules/fast-wrap-ansi/lib/main.js
|
|
21081
|
+
var ESC = "\x1B";
|
|
21082
|
+
var CSI = "\x9B";
|
|
21083
|
+
var END_CODE = 39;
|
|
21084
|
+
var ANSI_ESCAPE_BELL = "\x07";
|
|
21085
|
+
var ANSI_CSI = "[";
|
|
21086
|
+
var ANSI_OSC = "]";
|
|
21087
|
+
var ANSI_SGR_TERMINATOR = "m";
|
|
21088
|
+
var ANSI_ESCAPE_LINK = `${ANSI_OSC}8;;`;
|
|
21089
|
+
var GROUP_REGEX = new RegExp(`(?:\\${ANSI_CSI}(?<code>\\d+)m|\\${ANSI_ESCAPE_LINK}(?<uri>.*)${ANSI_ESCAPE_BELL})`, "y");
|
|
21090
|
+
var getClosingCode = (openingCode) => {
|
|
21091
|
+
if (openingCode >= 30 && openingCode <= 37)
|
|
21092
|
+
return 39;
|
|
21093
|
+
if (openingCode >= 90 && openingCode <= 97)
|
|
21094
|
+
return 39;
|
|
21095
|
+
if (openingCode >= 40 && openingCode <= 47)
|
|
21096
|
+
return 49;
|
|
21097
|
+
if (openingCode >= 100 && openingCode <= 107)
|
|
21098
|
+
return 49;
|
|
21099
|
+
if (openingCode === 1 || openingCode === 2)
|
|
21100
|
+
return 22;
|
|
21101
|
+
if (openingCode === 3)
|
|
21102
|
+
return 23;
|
|
21103
|
+
if (openingCode === 4)
|
|
21104
|
+
return 24;
|
|
21105
|
+
if (openingCode === 7)
|
|
21106
|
+
return 27;
|
|
21107
|
+
if (openingCode === 8)
|
|
21108
|
+
return 28;
|
|
21109
|
+
if (openingCode === 9)
|
|
21110
|
+
return 29;
|
|
21111
|
+
if (openingCode === 0)
|
|
21112
|
+
return 0;
|
|
21113
|
+
return;
|
|
21114
|
+
};
|
|
21115
|
+
var wrapAnsiCode = (code) => `${ESC}${ANSI_CSI}${code}${ANSI_SGR_TERMINATOR}`;
|
|
21116
|
+
var wrapAnsiHyperlink = (url2) => `${ESC}${ANSI_ESCAPE_LINK}${url2}${ANSI_ESCAPE_BELL}`;
|
|
21117
|
+
var wrapWord = (rows, word, columns) => {
|
|
21118
|
+
const characters = word[Symbol.iterator]();
|
|
21119
|
+
let isInsideEscape = false;
|
|
21120
|
+
let isInsideLinkEscape = false;
|
|
21121
|
+
let lastRow = rows.at(-1);
|
|
21122
|
+
let visible = lastRow === undefined ? 0 : dist_default2(lastRow);
|
|
21123
|
+
let currentCharacter = characters.next();
|
|
21124
|
+
let nextCharacter = characters.next();
|
|
21125
|
+
let rawCharacterIndex = 0;
|
|
21126
|
+
while (!currentCharacter.done) {
|
|
21127
|
+
const character = currentCharacter.value;
|
|
21128
|
+
const characterLength = dist_default2(character);
|
|
21129
|
+
if (visible + characterLength <= columns) {
|
|
21130
|
+
rows[rows.length - 1] += character;
|
|
21131
|
+
} else {
|
|
21132
|
+
rows.push(character);
|
|
21133
|
+
visible = 0;
|
|
21134
|
+
}
|
|
21135
|
+
if (character === ESC || character === CSI) {
|
|
21136
|
+
isInsideEscape = true;
|
|
21137
|
+
isInsideLinkEscape = word.startsWith(ANSI_ESCAPE_LINK, rawCharacterIndex + 1);
|
|
21138
|
+
}
|
|
21139
|
+
if (isInsideEscape) {
|
|
21140
|
+
if (isInsideLinkEscape) {
|
|
21141
|
+
if (character === ANSI_ESCAPE_BELL) {
|
|
21142
|
+
isInsideEscape = false;
|
|
21143
|
+
isInsideLinkEscape = false;
|
|
21144
|
+
}
|
|
21145
|
+
} else if (character === ANSI_SGR_TERMINATOR) {
|
|
21146
|
+
isInsideEscape = false;
|
|
21147
|
+
}
|
|
21148
|
+
} else {
|
|
21149
|
+
visible += characterLength;
|
|
21150
|
+
if (visible === columns && !nextCharacter.done) {
|
|
21151
|
+
rows.push("");
|
|
21152
|
+
visible = 0;
|
|
21153
|
+
}
|
|
21154
|
+
}
|
|
21155
|
+
currentCharacter = nextCharacter;
|
|
21156
|
+
nextCharacter = characters.next();
|
|
21157
|
+
rawCharacterIndex += character.length;
|
|
21158
|
+
}
|
|
21159
|
+
lastRow = rows.at(-1);
|
|
21160
|
+
if (!visible && lastRow !== undefined && lastRow.length && rows.length > 1) {
|
|
21161
|
+
rows[rows.length - 2] += rows.pop();
|
|
21162
|
+
}
|
|
21163
|
+
};
|
|
21164
|
+
var stringVisibleTrimSpacesRight = (string4) => {
|
|
21165
|
+
const words = string4.split(" ");
|
|
21166
|
+
let last = words.length;
|
|
21167
|
+
while (last) {
|
|
21168
|
+
if (dist_default2(words[last - 1])) {
|
|
21169
|
+
break;
|
|
21170
|
+
}
|
|
21171
|
+
last--;
|
|
21172
|
+
}
|
|
21173
|
+
if (last === words.length) {
|
|
21174
|
+
return string4;
|
|
21175
|
+
}
|
|
21176
|
+
return words.slice(0, last).join(" ") + words.slice(last).join("");
|
|
21177
|
+
};
|
|
21178
|
+
var exec = (string4, columns, options = {}) => {
|
|
21179
|
+
if (options.trim !== false && string4.trim() === "") {
|
|
21180
|
+
return "";
|
|
21181
|
+
}
|
|
21182
|
+
let returnValue = "";
|
|
21183
|
+
let escapeCode;
|
|
21184
|
+
let escapeUrl;
|
|
21185
|
+
const words = string4.split(" ");
|
|
21186
|
+
let rows = [""];
|
|
21187
|
+
let rowLength = 0;
|
|
21188
|
+
for (let index = 0;index < words.length; index++) {
|
|
21189
|
+
const word = words[index];
|
|
21190
|
+
if (options.trim !== false) {
|
|
21191
|
+
const row = rows.at(-1) ?? "";
|
|
21192
|
+
const trimmed = row.trimStart();
|
|
21193
|
+
if (row.length !== trimmed.length) {
|
|
21194
|
+
rows[rows.length - 1] = trimmed;
|
|
21195
|
+
rowLength = dist_default2(trimmed);
|
|
21196
|
+
}
|
|
21197
|
+
}
|
|
21198
|
+
if (index !== 0) {
|
|
21199
|
+
if (rowLength >= columns && (options.wordWrap === false || options.trim === false)) {
|
|
21200
|
+
rows.push("");
|
|
21201
|
+
rowLength = 0;
|
|
21202
|
+
}
|
|
21203
|
+
if (rowLength || options.trim === false) {
|
|
21204
|
+
rows[rows.length - 1] += " ";
|
|
21205
|
+
rowLength++;
|
|
21206
|
+
}
|
|
21207
|
+
}
|
|
21208
|
+
const wordLength = dist_default2(word);
|
|
21209
|
+
if (options.hard && wordLength > columns) {
|
|
21210
|
+
const remainingColumns = columns - rowLength;
|
|
21211
|
+
const breaksStartingThisLine = 1 + Math.floor((wordLength - remainingColumns - 1) / columns);
|
|
21212
|
+
const breaksStartingNextLine = Math.floor((wordLength - 1) / columns);
|
|
21213
|
+
if (breaksStartingNextLine < breaksStartingThisLine) {
|
|
21214
|
+
rows.push("");
|
|
21215
|
+
}
|
|
21216
|
+
wrapWord(rows, word, columns);
|
|
21217
|
+
rowLength = dist_default2(rows.at(-1) ?? "");
|
|
21218
|
+
continue;
|
|
21219
|
+
}
|
|
21220
|
+
if (rowLength + wordLength > columns && rowLength && wordLength) {
|
|
21221
|
+
if (options.wordWrap === false && rowLength < columns) {
|
|
21222
|
+
wrapWord(rows, word, columns);
|
|
21223
|
+
rowLength = dist_default2(rows.at(-1) ?? "");
|
|
21224
|
+
continue;
|
|
21225
|
+
}
|
|
21226
|
+
rows.push("");
|
|
21227
|
+
rowLength = 0;
|
|
21228
|
+
}
|
|
21229
|
+
if (rowLength + wordLength > columns && options.wordWrap === false) {
|
|
21230
|
+
wrapWord(rows, word, columns);
|
|
21231
|
+
rowLength = dist_default2(rows.at(-1) ?? "");
|
|
21232
|
+
continue;
|
|
21233
|
+
}
|
|
21234
|
+
rows[rows.length - 1] += word;
|
|
21235
|
+
rowLength += wordLength;
|
|
21236
|
+
}
|
|
21237
|
+
if (options.trim !== false) {
|
|
21238
|
+
rows = rows.map((row) => stringVisibleTrimSpacesRight(row));
|
|
21239
|
+
}
|
|
21240
|
+
const preString = rows.join(`
|
|
21241
|
+
`);
|
|
21242
|
+
let inSurrogate = false;
|
|
21243
|
+
for (let i2 = 0;i2 < preString.length; i2++) {
|
|
21244
|
+
const character = preString[i2];
|
|
21245
|
+
returnValue += character;
|
|
21246
|
+
if (!inSurrogate) {
|
|
21247
|
+
inSurrogate = character >= "\uD800" && character <= "\uDBFF";
|
|
21248
|
+
if (inSurrogate) {
|
|
21249
|
+
continue;
|
|
21250
|
+
}
|
|
21251
|
+
} else {
|
|
21252
|
+
inSurrogate = false;
|
|
21253
|
+
}
|
|
21254
|
+
if (character === ESC || character === CSI) {
|
|
21255
|
+
GROUP_REGEX.lastIndex = i2 + 1;
|
|
21256
|
+
const groupsResult = GROUP_REGEX.exec(preString);
|
|
21257
|
+
const groups = groupsResult?.groups;
|
|
21258
|
+
if (groups?.code !== undefined) {
|
|
21259
|
+
const code = Number.parseFloat(groups.code);
|
|
21260
|
+
escapeCode = code === END_CODE ? undefined : code;
|
|
21261
|
+
} else if (groups?.uri !== undefined) {
|
|
21262
|
+
escapeUrl = groups.uri.length === 0 ? undefined : groups.uri;
|
|
21263
|
+
}
|
|
21264
|
+
}
|
|
21265
|
+
if (preString[i2 + 1] === `
|
|
21266
|
+
`) {
|
|
21267
|
+
if (escapeUrl) {
|
|
21268
|
+
returnValue += wrapAnsiHyperlink("");
|
|
21269
|
+
}
|
|
21270
|
+
const closingCode = escapeCode ? getClosingCode(escapeCode) : undefined;
|
|
21271
|
+
if (escapeCode && closingCode) {
|
|
21272
|
+
returnValue += wrapAnsiCode(closingCode);
|
|
21273
|
+
}
|
|
21274
|
+
} else if (character === `
|
|
21275
|
+
`) {
|
|
21276
|
+
if (escapeCode && getClosingCode(escapeCode)) {
|
|
21277
|
+
returnValue += wrapAnsiCode(escapeCode);
|
|
21278
|
+
}
|
|
21279
|
+
if (escapeUrl) {
|
|
21280
|
+
returnValue += wrapAnsiHyperlink(escapeUrl);
|
|
21281
|
+
}
|
|
21282
|
+
}
|
|
21283
|
+
}
|
|
21284
|
+
return returnValue;
|
|
21285
|
+
};
|
|
21286
|
+
var CRLF_OR_LF = /\r?\n/;
|
|
21287
|
+
function wrapAnsi(string4, columns, options) {
|
|
21288
|
+
return String(string4).normalize().split(CRLF_OR_LF).map((line) => exec(line, columns, options)).join(`
|
|
21289
|
+
`);
|
|
21290
|
+
}
|
|
21291
|
+
|
|
21292
|
+
// node_modules/@clack/core/dist/index.mjs
|
|
21293
|
+
var import_sisteransi = __toESM(require_src(), 1);
|
|
21294
|
+
import { ReadStream as O3 } from "tty";
|
|
21295
|
+
function f3(r3, t2, s2) {
|
|
21296
|
+
if (!s2.some((o3) => !o3.disabled))
|
|
21297
|
+
return r3;
|
|
21298
|
+
const e2 = r3 + t2, i2 = Math.max(s2.length - 1, 0), n2 = e2 < 0 ? i2 : e2 > i2 ? 0 : e2;
|
|
21299
|
+
return s2[n2].disabled ? f3(n2, t2 < 0 ? -1 : 1, s2) : n2;
|
|
21300
|
+
}
|
|
21301
|
+
function I3(r3, t2, s2, e2) {
|
|
21302
|
+
const i2 = e2.split(`
|
|
21303
|
+
`);
|
|
21304
|
+
let n2 = 0, o3 = r3;
|
|
21305
|
+
for (const a2 of i2) {
|
|
21306
|
+
if (o3 <= a2.length)
|
|
21307
|
+
break;
|
|
21308
|
+
o3 -= a2.length + 1, n2++;
|
|
21309
|
+
}
|
|
21310
|
+
for (n2 = Math.max(0, Math.min(i2.length - 1, n2 + s2)), o3 = Math.min(o3, i2[n2].length) + t2;o3 < 0 && n2 > 0; )
|
|
21311
|
+
n2--, o3 += i2[n2].length + 1;
|
|
21312
|
+
for (;o3 > i2[n2].length && n2 < i2.length - 1; )
|
|
21313
|
+
o3 -= i2[n2].length + 1, n2++;
|
|
21314
|
+
o3 = Math.max(0, Math.min(i2[n2].length, o3));
|
|
21315
|
+
let u3 = 0;
|
|
21316
|
+
for (let a2 = 0;a2 < n2; a2++)
|
|
21317
|
+
u3 += i2[a2].length + 1;
|
|
21318
|
+
return u3 + o3;
|
|
21319
|
+
}
|
|
21320
|
+
var K = ["up", "down", "left", "right", "space", "enter", "cancel"];
|
|
21321
|
+
var j = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
|
21322
|
+
var h2 = { actions: new Set(K), aliases: new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"], ["\x03", "cancel"], ["escape", "cancel"]]), messages: { cancel: "Canceled", error: "Something went wrong" }, withGuide: true, date: { monthNames: [...j], messages: { required: "Please enter a valid date", invalidMonth: "There are only 12 months in a year", invalidDay: (r3, t2) => `There are only ${r3} days in ${t2}`, afterMin: (r3) => `Date must be on or after ${r3.toISOString().slice(0, 10)}`, beforeMax: (r3) => `Date must be on or before ${r3.toISOString().slice(0, 10)}` } } };
|
|
21323
|
+
function C3(r3, t2) {
|
|
21324
|
+
if (typeof r3 == "string")
|
|
21325
|
+
return h2.aliases.get(r3) === t2;
|
|
21326
|
+
for (const s2 of r3)
|
|
21327
|
+
if (s2 !== undefined && C3(s2, t2))
|
|
21328
|
+
return true;
|
|
21329
|
+
return false;
|
|
21330
|
+
}
|
|
21331
|
+
function Y2(r3, t2) {
|
|
21332
|
+
if (r3 === t2)
|
|
21333
|
+
return;
|
|
21334
|
+
const s2 = r3.split(`
|
|
21335
|
+
`), e2 = t2.split(`
|
|
21336
|
+
`), i2 = Math.max(s2.length, e2.length), n2 = [];
|
|
21337
|
+
for (let o3 = 0;o3 < i2; o3++)
|
|
21338
|
+
s2[o3] !== e2[o3] && n2.push(o3);
|
|
21339
|
+
return { lines: n2, numLinesBefore: s2.length, numLinesAfter: e2.length, numLines: i2 };
|
|
21340
|
+
}
|
|
21341
|
+
var q2 = globalThis.process.platform.startsWith("win");
|
|
21342
|
+
var k2 = Symbol("clack:cancel");
|
|
21343
|
+
function R3(r3) {
|
|
21344
|
+
return r3 === k2;
|
|
21345
|
+
}
|
|
21346
|
+
function w2(r3, t2) {
|
|
21347
|
+
const s2 = r3;
|
|
21348
|
+
s2.isTTY && s2.setRawMode(t2);
|
|
21349
|
+
}
|
|
21350
|
+
function W2({ input: r3 = D2, output: t2 = x2, overwrite: s2 = true, hideCursor: e2 = true } = {}) {
|
|
21351
|
+
const i2 = b2.createInterface({ input: r3, output: t2, prompt: "", tabSize: 1 });
|
|
21352
|
+
b2.emitKeypressEvents(r3, i2), r3 instanceof O3 && r3.isTTY && r3.setRawMode(true);
|
|
21353
|
+
const n2 = (o3, { name: u3, sequence: a2 }) => {
|
|
21354
|
+
const l2 = String(o3);
|
|
21355
|
+
if (C3([l2, u3, a2], "cancel")) {
|
|
21356
|
+
e2 && t2.write(import_sisteransi.cursor.show), process.exit(0);
|
|
21357
|
+
return;
|
|
21358
|
+
}
|
|
21359
|
+
if (!s2)
|
|
21360
|
+
return;
|
|
21361
|
+
const c3 = u3 === "return" ? 0 : -1, y3 = u3 === "return" ? -1 : 0;
|
|
21362
|
+
b2.moveCursor(t2, c3, y3, () => {
|
|
21363
|
+
b2.clearLine(t2, 1, () => {
|
|
21364
|
+
r3.once("keypress", n2);
|
|
21365
|
+
});
|
|
21366
|
+
});
|
|
21367
|
+
};
|
|
21368
|
+
return e2 && t2.write(import_sisteransi.cursor.hide), r3.once("keypress", n2), () => {
|
|
21369
|
+
r3.off("keypress", n2), e2 && t2.write(import_sisteransi.cursor.show), r3 instanceof O3 && r3.isTTY && !q2 && r3.setRawMode(false), i2.terminal = false, i2.close();
|
|
21370
|
+
};
|
|
21371
|
+
}
|
|
21372
|
+
var A3 = (r3) => ("columns" in r3) && typeof r3.columns == "number" ? r3.columns : 80;
|
|
21373
|
+
var L3 = (r3) => ("rows" in r3) && typeof r3.rows == "number" ? r3.rows : 20;
|
|
21374
|
+
function B2(r3, t2, s2, e2 = s2, i2 = s2, n2) {
|
|
21375
|
+
const o3 = A3(r3 ?? x2);
|
|
21376
|
+
return wrapAnsi(t2, o3 - s2.length, { hard: true, trim: false }).split(`
|
|
21377
|
+
`).map((u3, a2, l2) => {
|
|
21378
|
+
const c3 = n2 ? n2(u3, a2) : u3;
|
|
21379
|
+
return a2 === 0 ? `${e2}${c3}` : a2 === l2.length - 1 ? `${i2}${c3}` : `${s2}${c3}`;
|
|
21380
|
+
}).join(`
|
|
21381
|
+
`);
|
|
21382
|
+
}
|
|
21383
|
+
function P3(r3, t2) {
|
|
21384
|
+
if ("~standard" in r3) {
|
|
21385
|
+
const s2 = r3["~standard"].validate(t2);
|
|
21386
|
+
if (s2 instanceof Promise)
|
|
21387
|
+
throw new TypeError("Schema validation must be synchronous. Update `validate()` and remove any asynchronous logic.");
|
|
21388
|
+
return s2.issues?.at(0)?.message;
|
|
21389
|
+
}
|
|
21390
|
+
return r3(t2);
|
|
21391
|
+
}
|
|
21392
|
+
|
|
21393
|
+
class m2 {
|
|
21394
|
+
input;
|
|
21395
|
+
output;
|
|
21396
|
+
_abortSignal;
|
|
21397
|
+
rl;
|
|
21398
|
+
opts;
|
|
21399
|
+
_render;
|
|
21400
|
+
_track = false;
|
|
21401
|
+
_prevFrame = "";
|
|
21402
|
+
_subscribers = new Map;
|
|
21403
|
+
_cursor = 0;
|
|
21404
|
+
state = "initial";
|
|
21405
|
+
error = "";
|
|
21406
|
+
value;
|
|
21407
|
+
userInput = "";
|
|
21408
|
+
constructor(t2, s2 = true) {
|
|
21409
|
+
const { input: e2 = D2, output: i2 = x2, render: n2, signal: o3, ...u3 } = t2;
|
|
21410
|
+
this.opts = u3, this.onKeypress = this.onKeypress.bind(this), this.close = this.close.bind(this), this.render = this.render.bind(this), this._render = n2.bind(this), this._track = s2, this._abortSignal = o3, this.input = e2, this.output = i2;
|
|
21411
|
+
}
|
|
21412
|
+
unsubscribe() {
|
|
21413
|
+
this._subscribers.clear();
|
|
21414
|
+
}
|
|
21415
|
+
setSubscriber(t2, s2) {
|
|
21416
|
+
const e2 = this._subscribers.get(t2) ?? [];
|
|
21417
|
+
e2.push(s2), this._subscribers.set(t2, e2);
|
|
21418
|
+
}
|
|
21419
|
+
on(t2, s2) {
|
|
21420
|
+
this.setSubscriber(t2, { cb: s2 });
|
|
21421
|
+
}
|
|
21422
|
+
once(t2, s2) {
|
|
21423
|
+
this.setSubscriber(t2, { cb: s2, once: true });
|
|
21424
|
+
}
|
|
21425
|
+
emit(t2, ...s2) {
|
|
21426
|
+
const e2 = this._subscribers.get(t2) ?? [], i2 = [];
|
|
21427
|
+
for (const n2 of e2)
|
|
21428
|
+
n2.cb(...s2), n2.once && i2.push(() => e2.splice(e2.indexOf(n2), 1));
|
|
21429
|
+
for (const n2 of i2)
|
|
21430
|
+
n2();
|
|
21431
|
+
}
|
|
21432
|
+
prompt() {
|
|
21433
|
+
return new Promise((t2) => {
|
|
21434
|
+
if (this._abortSignal) {
|
|
21435
|
+
if (this._abortSignal.aborted)
|
|
21436
|
+
return this.state = "cancel", this.close(), t2(k2);
|
|
21437
|
+
this._abortSignal.addEventListener("abort", () => {
|
|
21438
|
+
this.state = "cancel", this.close();
|
|
21439
|
+
}, { once: true });
|
|
21440
|
+
}
|
|
21441
|
+
this.rl = G3.createInterface({ input: this.input, tabSize: 2, prompt: "", escapeCodeTimeout: 50, terminal: true }), this.rl.prompt(), this.opts.initialUserInput !== undefined && this._setUserInput(this.opts.initialUserInput, true), this.input.on("keypress", this.onKeypress), w2(this.input, true), this.output.on("resize", this.render), this.render(), this.once("submit", () => {
|
|
21442
|
+
this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), w2(this.input, false), t2(this.value);
|
|
21443
|
+
}), this.once("cancel", () => {
|
|
21444
|
+
this.output.write(import_sisteransi.cursor.show), this.output.off("resize", this.render), w2(this.input, false), t2(k2);
|
|
21445
|
+
});
|
|
21446
|
+
});
|
|
21447
|
+
}
|
|
21448
|
+
_isActionKey(t2, s2) {
|
|
21449
|
+
return t2 === "\t";
|
|
21450
|
+
}
|
|
21451
|
+
_shouldSubmit(t2, s2) {
|
|
21452
|
+
return true;
|
|
21453
|
+
}
|
|
21454
|
+
_setValue(t2) {
|
|
21455
|
+
this.value = t2, this.emit("value", this.value);
|
|
21456
|
+
}
|
|
21457
|
+
_setUserInput(t2, s2) {
|
|
21458
|
+
this.userInput = t2 ?? "", this.emit("userInput", this.userInput), s2 && this._track && this.rl && (this.rl.write(this.userInput), this._cursor = this.rl.cursor);
|
|
21459
|
+
}
|
|
21460
|
+
_clearUserInput() {
|
|
21461
|
+
this.rl?.write(null, { ctrl: true, name: "u" }), this._setUserInput("");
|
|
21462
|
+
}
|
|
21463
|
+
onKeypress(t2, s2) {
|
|
21464
|
+
if (this._track && s2.name !== "return" && (s2.name && this._isActionKey(t2, s2) && this.rl?.write(null, { ctrl: true, name: "h" }), this._cursor = this.rl?.cursor ?? 0, this._setUserInput(this.rl?.line)), this.state === "error" && (this.state = "active"), s2?.name && (!this._track && h2.aliases.has(s2.name) && this.emit("cursor", h2.aliases.get(s2.name)), h2.actions.has(s2.name) && this.emit("cursor", s2.name)), t2 && (t2.toLowerCase() === "y" || t2.toLowerCase() === "n") && this.emit("confirm", t2.toLowerCase() === "y"), this.emit("key", t2, s2), s2?.name === "return" && this._shouldSubmit(t2, s2)) {
|
|
21465
|
+
if (this.opts.validate) {
|
|
21466
|
+
const e2 = P3(this.opts.validate, this.value);
|
|
21467
|
+
e2 && (this.error = e2 instanceof Error ? e2.message : e2, this.state = "error", this.rl?.write(this.userInput));
|
|
21468
|
+
}
|
|
21469
|
+
this.state !== "error" && (this.state = "submit");
|
|
21470
|
+
}
|
|
21471
|
+
C3([t2, s2?.name, s2?.sequence], "cancel") && (this.state = "cancel"), (this.state === "submit" || this.state === "cancel") && this.emit("finalize"), this.render(), (this.state === "submit" || this.state === "cancel") && this.close();
|
|
21472
|
+
}
|
|
21473
|
+
close() {
|
|
21474
|
+
this.input.unpipe(), this.input.removeListener("keypress", this.onKeypress), this.output.write(`
|
|
21475
|
+
`), w2(this.input, false), this.rl?.close(), this.rl = undefined, this.emit(`${this.state}`, this.value), this.unsubscribe();
|
|
21476
|
+
}
|
|
21477
|
+
restoreCursor() {
|
|
21478
|
+
const t2 = wrapAnsi(this._prevFrame, process.stdout.columns, { hard: true, trim: false }).split(`
|
|
21479
|
+
`).length - 1;
|
|
21480
|
+
this.output.write(import_sisteransi.cursor.move(-999, t2 * -1));
|
|
21481
|
+
}
|
|
21482
|
+
render() {
|
|
21483
|
+
const t2 = wrapAnsi(this._render(this) ?? "", process.stdout.columns, { hard: true, trim: false });
|
|
21484
|
+
if (t2 !== this._prevFrame) {
|
|
21485
|
+
if (this.state === "initial")
|
|
21486
|
+
this.output.write(import_sisteransi.cursor.hide);
|
|
21487
|
+
else {
|
|
21488
|
+
const s2 = Y2(this._prevFrame, t2), e2 = L3(this.output);
|
|
21489
|
+
if (this.restoreCursor(), s2) {
|
|
21490
|
+
const i2 = Math.max(0, s2.numLinesAfter - e2), n2 = Math.max(0, s2.numLinesBefore - e2);
|
|
21491
|
+
let o3 = s2.lines.find((u3) => u3 >= i2);
|
|
21492
|
+
if (o3 === undefined) {
|
|
21493
|
+
this._prevFrame = t2;
|
|
21494
|
+
return;
|
|
21495
|
+
}
|
|
21496
|
+
if (s2.lines.length === 1) {
|
|
21497
|
+
this.output.write(import_sisteransi.cursor.move(0, o3 - n2)), this.output.write(import_sisteransi.erase.lines(1));
|
|
21498
|
+
const u3 = t2.split(`
|
|
21499
|
+
`);
|
|
21500
|
+
this.output.write(u3[o3]), this._prevFrame = t2, this.output.write(import_sisteransi.cursor.move(0, u3.length - o3 - 1));
|
|
21501
|
+
return;
|
|
21502
|
+
} else if (s2.lines.length > 1) {
|
|
21503
|
+
if (i2 < n2)
|
|
21504
|
+
o3 = i2;
|
|
21505
|
+
else {
|
|
21506
|
+
const a2 = o3 - n2;
|
|
21507
|
+
a2 > 0 && this.output.write(import_sisteransi.cursor.move(0, a2));
|
|
21508
|
+
}
|
|
21509
|
+
this.output.write(import_sisteransi.erase.down());
|
|
21510
|
+
const u3 = t2.split(`
|
|
21511
|
+
`).slice(o3);
|
|
21512
|
+
this.output.write(u3.join(`
|
|
21513
|
+
`)), this._prevFrame = t2;
|
|
21514
|
+
return;
|
|
21515
|
+
}
|
|
21516
|
+
}
|
|
21517
|
+
this.output.write(import_sisteransi.erase.down());
|
|
21518
|
+
}
|
|
21519
|
+
this.output.write(t2), this.state === "initial" && (this.state = "active"), this._prevFrame = t2;
|
|
21520
|
+
}
|
|
21521
|
+
}
|
|
21522
|
+
}
|
|
21523
|
+
function J2(r3, t2) {
|
|
21524
|
+
if (r3 === undefined || t2.length === 0)
|
|
21525
|
+
return 0;
|
|
21526
|
+
const s2 = t2.findIndex((e2) => e2.value === r3);
|
|
21527
|
+
return s2 !== -1 ? s2 : 0;
|
|
21528
|
+
}
|
|
21529
|
+
function H(r3, t2) {
|
|
21530
|
+
return (t2.label ?? String(t2.value)).toLowerCase().includes(r3.toLowerCase());
|
|
21531
|
+
}
|
|
21532
|
+
function Q2(r3, t2) {
|
|
21533
|
+
if (t2)
|
|
21534
|
+
return r3 ? t2 : t2[0];
|
|
21535
|
+
}
|
|
21536
|
+
var X2 = class extends m2 {
|
|
21537
|
+
filteredOptions;
|
|
21538
|
+
multiple;
|
|
21539
|
+
isNavigating = false;
|
|
21540
|
+
selectedValues = [];
|
|
21541
|
+
focusedValue;
|
|
21542
|
+
#s = 0;
|
|
21543
|
+
#r = "";
|
|
21544
|
+
#t;
|
|
21545
|
+
#n;
|
|
21546
|
+
#u;
|
|
21547
|
+
get cursor() {
|
|
21548
|
+
return this.#s;
|
|
21549
|
+
}
|
|
21550
|
+
get userInputWithCursor() {
|
|
21551
|
+
if (!this.userInput)
|
|
21552
|
+
return v2(["inverse", "hidden"], "_");
|
|
21553
|
+
if (this._cursor >= this.userInput.length)
|
|
21554
|
+
return `${this.userInput}\u2588`;
|
|
21555
|
+
const t2 = this.userInput.slice(0, this._cursor), [s2, ...e2] = this.userInput.slice(this._cursor);
|
|
21556
|
+
return `${t2}${v2("inverse", s2)}${e2.join("")}`;
|
|
21557
|
+
}
|
|
21558
|
+
get options() {
|
|
21559
|
+
return typeof this.#n == "function" ? this.#n() : this.#n;
|
|
21560
|
+
}
|
|
21561
|
+
constructor(t2) {
|
|
21562
|
+
super(t2), this.#n = t2.options, this.#u = t2.placeholder;
|
|
21563
|
+
const s2 = this.options;
|
|
21564
|
+
this.filteredOptions = [...s2], this.multiple = t2.multiple === true, this.#t = typeof t2.options == "function" ? t2.filter : t2.filter ?? H;
|
|
21565
|
+
let e2;
|
|
21566
|
+
if (t2.initialValue && Array.isArray(t2.initialValue) ? this.multiple ? e2 = t2.initialValue : e2 = t2.initialValue.slice(0, 1) : !this.multiple && this.options.length > 0 && (e2 = [this.options[0].value]), e2)
|
|
21567
|
+
for (const i2 of e2) {
|
|
21568
|
+
const n2 = s2.findIndex((o3) => o3.value === i2);
|
|
21569
|
+
n2 !== -1 && (this.toggleSelected(i2), this.#s = n2);
|
|
21570
|
+
}
|
|
21571
|
+
this.focusedValue = this.options[this.#s]?.value, this.on("key", (i2, n2) => this.#e(i2, n2)), this.on("userInput", (i2) => this.#i(i2));
|
|
21572
|
+
}
|
|
21573
|
+
_isActionKey(t2, s2) {
|
|
21574
|
+
return t2 === "\t" || this.multiple && this.isNavigating && s2.name === "space" && t2 !== undefined && t2 !== "";
|
|
21575
|
+
}
|
|
21576
|
+
#e(t2, s2) {
|
|
21577
|
+
const e2 = s2.name === "up", i2 = s2.name === "down", n2 = s2.name === "return", o3 = this.userInput === "" || this.userInput === "\t", u3 = this.#u, a2 = this.options, l2 = u3 !== undefined && u3 !== "" && a2.some((c3) => !c3.disabled && (this.#t ? this.#t(u3, c3) : true));
|
|
21578
|
+
if (s2.name === "tab" && o3 && l2) {
|
|
21579
|
+
this.userInput === "\t" && this._clearUserInput(), this._setUserInput(u3, true), this.isNavigating = false;
|
|
21580
|
+
return;
|
|
21581
|
+
}
|
|
21582
|
+
e2 || i2 ? (this.#s = f3(this.#s, e2 ? -1 : 1, this.filteredOptions), this.focusedValue = this.filteredOptions[this.#s]?.value, this.multiple || (this.selectedValues = [this.focusedValue]), this.isNavigating = true) : n2 ? this.value = Q2(this.multiple, this.selectedValues) : this.multiple ? this.focusedValue !== undefined && (s2.name === "tab" || this.isNavigating && s2.name === "space") ? this.toggleSelected(this.focusedValue) : this.isNavigating = false : (this.focusedValue && (this.selectedValues = [this.focusedValue]), this.isNavigating = false);
|
|
21583
|
+
}
|
|
21584
|
+
deselectAll() {
|
|
21585
|
+
this.selectedValues = [];
|
|
21586
|
+
}
|
|
21587
|
+
toggleSelected(t2) {
|
|
21588
|
+
this.filteredOptions.length !== 0 && (this.multiple ? this.selectedValues.includes(t2) ? this.selectedValues = this.selectedValues.filter((s2) => s2 !== t2) : this.selectedValues = [...this.selectedValues, t2] : this.selectedValues = [t2]);
|
|
21589
|
+
}
|
|
21590
|
+
#i(t2) {
|
|
21591
|
+
if (t2 !== this.#r) {
|
|
21592
|
+
this.#r = t2;
|
|
21593
|
+
const s2 = this.options;
|
|
21594
|
+
t2 && this.#t ? this.filteredOptions = s2.filter((n2) => this.#t?.(t2, n2)) : this.filteredOptions = [...s2];
|
|
21595
|
+
const e2 = J2(this.focusedValue, this.filteredOptions);
|
|
21596
|
+
this.#s = f3(e2, 0, this.filteredOptions);
|
|
21597
|
+
const i2 = this.filteredOptions[this.#s];
|
|
21598
|
+
i2 && !i2.disabled ? this.focusedValue = i2.value : this.focusedValue = undefined, this.multiple || (this.focusedValue !== undefined ? this.toggleSelected(this.focusedValue) : this.deselectAll());
|
|
21599
|
+
}
|
|
21600
|
+
}
|
|
21601
|
+
};
|
|
21602
|
+
|
|
21603
|
+
class Z extends m2 {
|
|
21604
|
+
get cursor() {
|
|
21605
|
+
return this.value ? 0 : 1;
|
|
21606
|
+
}
|
|
21607
|
+
get _value() {
|
|
21608
|
+
return this.cursor === 0;
|
|
21609
|
+
}
|
|
21610
|
+
constructor(t2) {
|
|
21611
|
+
super(t2, false), this.value = !!t2.initialValue, this.on("userInput", () => {
|
|
21612
|
+
this.value = this._value;
|
|
21613
|
+
}), this.on("confirm", (s2) => {
|
|
21614
|
+
this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = s2, this.state = "submit", this.close();
|
|
21615
|
+
}), this.on("cursor", () => {
|
|
21616
|
+
this.value = !this.value;
|
|
21617
|
+
});
|
|
21618
|
+
}
|
|
21619
|
+
}
|
|
21620
|
+
var tt = { Y: { type: "year", len: 4 }, M: { type: "month", len: 2 }, D: { type: "day", len: 2 } };
|
|
21621
|
+
function F3(r3) {
|
|
21622
|
+
return [...r3].map((t2) => tt[t2]);
|
|
21623
|
+
}
|
|
21624
|
+
function st(r3) {
|
|
21625
|
+
const t2 = new Intl.DateTimeFormat(r3, { year: "numeric", month: "2-digit", day: "2-digit" }).formatToParts(new Date(2000, 0, 15)), s2 = [];
|
|
21626
|
+
let e2 = "/";
|
|
21627
|
+
for (const i2 of t2)
|
|
21628
|
+
i2.type === "literal" ? e2 = i2.value.trim() || i2.value : (i2.type === "year" || i2.type === "month" || i2.type === "day") && s2.push({ type: i2.type, len: i2.type === "year" ? 4 : 2 });
|
|
21629
|
+
return { segments: s2, separator: e2 };
|
|
21630
|
+
}
|
|
21631
|
+
function $(r3) {
|
|
21632
|
+
return Number.parseInt((r3 || "0").replace(/_/g, "0"), 10) || 0;
|
|
21633
|
+
}
|
|
21634
|
+
function S3(r3) {
|
|
21635
|
+
return { year: $(r3.year), month: $(r3.month), day: $(r3.day) };
|
|
21636
|
+
}
|
|
21637
|
+
function U(r3, t2) {
|
|
21638
|
+
return new Date(r3 || 2001, t2 || 1, 0).getDate();
|
|
21639
|
+
}
|
|
21640
|
+
function N3(r3) {
|
|
21641
|
+
const { year: t2, month: s2, day: e2 } = S3(r3);
|
|
21642
|
+
if (!t2 || t2 < 0 || t2 > 9999 || !s2 || s2 < 1 || s2 > 12 || !e2 || e2 < 1)
|
|
21643
|
+
return;
|
|
21644
|
+
const i2 = new Date(Date.UTC(t2, s2 - 1, e2));
|
|
21645
|
+
if (!(i2.getUTCFullYear() !== t2 || i2.getUTCMonth() !== s2 - 1 || i2.getUTCDate() !== e2))
|
|
21646
|
+
return { year: t2, month: s2, day: e2 };
|
|
21647
|
+
}
|
|
21648
|
+
function E(r3) {
|
|
21649
|
+
const t2 = N3(r3);
|
|
21650
|
+
return t2 ? new Date(Date.UTC(t2.year, t2.month - 1, t2.day)) : undefined;
|
|
21651
|
+
}
|
|
21652
|
+
function et(r3, t2, s2, e2) {
|
|
21653
|
+
const i2 = s2 ? { year: s2.getUTCFullYear(), month: s2.getUTCMonth() + 1, day: s2.getUTCDate() } : null, n2 = e2 ? { year: e2.getUTCFullYear(), month: e2.getUTCMonth() + 1, day: e2.getUTCDate() } : null;
|
|
21654
|
+
return r3 === "year" ? { min: i2?.year ?? 1, max: n2?.year ?? 9999 } : r3 === "month" ? { min: i2 && t2.year === i2.year ? i2.month : 1, max: n2 && t2.year === n2.year ? n2.month : 12 } : { min: i2 && t2.year === i2.year && t2.month === i2.month ? i2.day : 1, max: n2 && t2.year === n2.year && t2.month === n2.month ? n2.day : U(t2.year, t2.month) };
|
|
21655
|
+
}
|
|
21656
|
+
|
|
21657
|
+
class it extends m2 {
|
|
21658
|
+
#s;
|
|
21659
|
+
#r;
|
|
21660
|
+
#t;
|
|
21661
|
+
#n;
|
|
21662
|
+
#u;
|
|
21663
|
+
#e = { segmentIndex: 0, positionInSegment: 0 };
|
|
21664
|
+
#i = true;
|
|
21665
|
+
#o = null;
|
|
21666
|
+
inlineError = "";
|
|
21667
|
+
get segmentCursor() {
|
|
21668
|
+
return { ...this.#e };
|
|
21669
|
+
}
|
|
21670
|
+
get segmentValues() {
|
|
21671
|
+
return { ...this.#t };
|
|
21672
|
+
}
|
|
21673
|
+
get segments() {
|
|
21674
|
+
return this.#s;
|
|
21675
|
+
}
|
|
21676
|
+
get separator() {
|
|
21677
|
+
return this.#r;
|
|
21678
|
+
}
|
|
21679
|
+
get formattedValue() {
|
|
21680
|
+
return this.#c(this.#t);
|
|
21681
|
+
}
|
|
21682
|
+
#c(t2) {
|
|
21683
|
+
return this.#s.map((s2) => t2[s2.type]).join(this.#r);
|
|
21684
|
+
}
|
|
21685
|
+
#a() {
|
|
21686
|
+
this._setUserInput(this.#c(this.#t)), this._setValue(E(this.#t) ?? undefined);
|
|
21687
|
+
}
|
|
21688
|
+
constructor(t2) {
|
|
21689
|
+
const s2 = t2.format ? { segments: F3(t2.format), separator: t2.separator ?? "/" } : st(t2.locale), e2 = t2.separator ?? s2.separator, i2 = t2.format ? F3(t2.format) : s2.segments, n2 = t2.initialValue ?? t2.defaultValue, o3 = n2 ? { year: String(n2.getUTCFullYear()).padStart(4, "0"), month: String(n2.getUTCMonth() + 1).padStart(2, "0"), day: String(n2.getUTCDate()).padStart(2, "0") } : { year: "____", month: "__", day: "__" }, u3 = i2.map((a2) => o3[a2.type]).join(e2);
|
|
21690
|
+
super({ ...t2, initialUserInput: u3 }, false), this.#s = i2, this.#r = e2, this.#t = o3, this.#n = t2.minDate, this.#u = t2.maxDate, this.#a(), this.on("cursor", (a2) => this.#d(a2)), this.on("key", (a2, l2) => this.#f(a2, l2)), this.on("finalize", () => this.#g(t2));
|
|
21691
|
+
}
|
|
21692
|
+
#h() {
|
|
21693
|
+
const t2 = Math.max(0, Math.min(this.#e.segmentIndex, this.#s.length - 1)), s2 = this.#s[t2];
|
|
21694
|
+
if (s2)
|
|
21695
|
+
return this.#e.positionInSegment = Math.max(0, Math.min(this.#e.positionInSegment, s2.len - 1)), { segment: s2, index: t2 };
|
|
21696
|
+
}
|
|
21697
|
+
#l(t2) {
|
|
21698
|
+
this.inlineError = "", this.#o = null;
|
|
21699
|
+
const s2 = this.#h();
|
|
21700
|
+
s2 && (this.#e.segmentIndex = Math.max(0, Math.min(this.#s.length - 1, s2.index + t2)), this.#e.positionInSegment = 0, this.#i = true);
|
|
21701
|
+
}
|
|
21702
|
+
#p(t2) {
|
|
21703
|
+
const s2 = this.#h();
|
|
21704
|
+
if (!s2)
|
|
21705
|
+
return;
|
|
21706
|
+
const { segment: e2 } = s2, i2 = this.#t[e2.type], n2 = !i2 || i2.replace(/_/g, "") === "", o3 = Number.parseInt((i2 || "0").replace(/_/g, "0"), 10) || 0, u3 = et(e2.type, S3(this.#t), this.#n, this.#u);
|
|
21707
|
+
let a2;
|
|
21708
|
+
n2 ? a2 = t2 === 1 ? u3.min : u3.max : a2 = Math.max(Math.min(u3.max, o3 + t2), u3.min), this.#t = { ...this.#t, [e2.type]: a2.toString().padStart(e2.len, "0") }, this.#i = true, this.#o = null, this.#a();
|
|
21709
|
+
}
|
|
21710
|
+
#d(t2) {
|
|
21711
|
+
if (t2)
|
|
21712
|
+
switch (t2) {
|
|
21713
|
+
case "right":
|
|
21714
|
+
return this.#l(1);
|
|
21715
|
+
case "left":
|
|
21716
|
+
return this.#l(-1);
|
|
21717
|
+
case "up":
|
|
21718
|
+
return this.#p(1);
|
|
21719
|
+
case "down":
|
|
21720
|
+
return this.#p(-1);
|
|
21721
|
+
}
|
|
21722
|
+
}
|
|
21723
|
+
#f(t2, s2) {
|
|
21724
|
+
if (s2?.name === "backspace" || s2?.sequence === "\x7F" || s2?.sequence === "\b" || t2 === "\x7F" || t2 === "\b") {
|
|
21725
|
+
this.inlineError = "";
|
|
21726
|
+
const e2 = this.#h();
|
|
21727
|
+
if (!e2)
|
|
21728
|
+
return;
|
|
21729
|
+
if (!this.#t[e2.segment.type].replace(/_/g, "")) {
|
|
21730
|
+
this.#l(-1);
|
|
21731
|
+
return;
|
|
21732
|
+
}
|
|
21733
|
+
this.#t[e2.segment.type] = "_".repeat(e2.segment.len), this.#i = true, this.#e.positionInSegment = 0, this.#a();
|
|
21734
|
+
return;
|
|
21735
|
+
}
|
|
21736
|
+
if (s2?.name === "tab") {
|
|
21737
|
+
this.inlineError = "";
|
|
21738
|
+
const e2 = this.#h();
|
|
21739
|
+
if (!e2)
|
|
21740
|
+
return;
|
|
21741
|
+
const i2 = s2.shift ? -1 : 1, n2 = e2.index + i2;
|
|
21742
|
+
n2 >= 0 && n2 < this.#s.length && (this.#e.segmentIndex = n2, this.#e.positionInSegment = 0, this.#i = true);
|
|
21743
|
+
return;
|
|
21744
|
+
}
|
|
21745
|
+
if (t2 && /^[0-9]$/.test(t2)) {
|
|
21746
|
+
const e2 = this.#h();
|
|
21747
|
+
if (!e2)
|
|
21748
|
+
return;
|
|
21749
|
+
const { segment: i2 } = e2, n2 = !this.#t[i2.type].replace(/_/g, "");
|
|
21750
|
+
if (this.#i && this.#o !== null && !n2) {
|
|
21751
|
+
const d2 = this.#o + t2, g3 = { ...this.#t, [i2.type]: d2 }, _3 = this.#m(g3, i2);
|
|
21752
|
+
if (_3) {
|
|
21753
|
+
this.inlineError = _3, this.#o = null, this.#i = false;
|
|
21754
|
+
return;
|
|
21755
|
+
}
|
|
21756
|
+
this.inlineError = "", this.#t[i2.type] = d2, this.#o = null, this.#i = false, this.#a(), e2.index < this.#s.length - 1 && (this.#e.segmentIndex = e2.index + 1, this.#e.positionInSegment = 0, this.#i = true);
|
|
21757
|
+
return;
|
|
21758
|
+
}
|
|
21759
|
+
this.#i && !n2 && (this.#t[i2.type] = "_".repeat(i2.len), this.#e.positionInSegment = 0), this.#i = false, this.#o = null;
|
|
21760
|
+
const o3 = this.#t[i2.type], u3 = o3.indexOf("_"), a2 = u3 >= 0 ? u3 : Math.min(this.#e.positionInSegment, i2.len - 1);
|
|
21761
|
+
if (a2 < 0 || a2 >= i2.len)
|
|
21762
|
+
return;
|
|
21763
|
+
let l2 = o3.slice(0, a2) + t2 + o3.slice(a2 + 1), c3 = false;
|
|
21764
|
+
if (a2 === 0 && o3 === "__" && (i2.type === "month" || i2.type === "day")) {
|
|
21765
|
+
const d2 = Number.parseInt(t2, 10);
|
|
21766
|
+
l2 = `0${t2}`, c3 = d2 <= (i2.type === "month" ? 1 : 2);
|
|
21767
|
+
}
|
|
21768
|
+
if (i2.type === "year" && (l2 = (o3.replace(/_/g, "") + t2).padStart(i2.len, "_")), !l2.includes("_")) {
|
|
21769
|
+
const d2 = { ...this.#t, [i2.type]: l2 }, g3 = this.#m(d2, i2);
|
|
21770
|
+
if (g3) {
|
|
21771
|
+
this.inlineError = g3;
|
|
21772
|
+
return;
|
|
21773
|
+
}
|
|
21774
|
+
}
|
|
21775
|
+
this.inlineError = "", this.#t[i2.type] = l2;
|
|
21776
|
+
const y3 = l2.includes("_") ? undefined : N3(this.#t);
|
|
21777
|
+
if (y3) {
|
|
21778
|
+
const { year: d2, month: g3 } = y3, _3 = U(d2, g3);
|
|
21779
|
+
this.#t = { year: String(Math.max(0, Math.min(9999, d2))).padStart(4, "0"), month: String(Math.max(1, Math.min(12, g3))).padStart(2, "0"), day: String(Math.max(1, Math.min(_3, y3.day))).padStart(2, "0") };
|
|
21780
|
+
}
|
|
21781
|
+
this.#a();
|
|
21782
|
+
const T3 = l2.indexOf("_");
|
|
21783
|
+
c3 ? (this.#i = true, this.#o = t2) : T3 >= 0 ? this.#e.positionInSegment = T3 : u3 >= 0 && e2.index < this.#s.length - 1 ? (this.#e.segmentIndex = e2.index + 1, this.#e.positionInSegment = 0, this.#i = true) : this.#e.positionInSegment = Math.min(a2 + 1, i2.len - 1);
|
|
21784
|
+
}
|
|
21785
|
+
}
|
|
21786
|
+
#m(t2, s2) {
|
|
21787
|
+
const { month: e2, day: i2 } = S3(t2);
|
|
21788
|
+
if (s2.type === "month" && (e2 < 0 || e2 > 12))
|
|
21789
|
+
return h2.date.messages.invalidMonth;
|
|
21790
|
+
if (s2.type === "day" && (i2 < 0 || i2 > 31))
|
|
21791
|
+
return h2.date.messages.invalidDay(31, "any month");
|
|
21792
|
+
}
|
|
21793
|
+
#g(t2) {
|
|
21794
|
+
const { year: s2, month: e2, day: i2 } = S3(this.#t);
|
|
21795
|
+
if (s2 && e2 && i2) {
|
|
21796
|
+
const n2 = U(s2, e2);
|
|
21797
|
+
this.#t = { ...this.#t, day: String(Math.min(i2, n2)).padStart(2, "0") };
|
|
21798
|
+
}
|
|
21799
|
+
this.value = E(this.#t) ?? t2.defaultValue ?? undefined;
|
|
21800
|
+
}
|
|
21801
|
+
}
|
|
21802
|
+
var rt = class extends m2 {
|
|
21803
|
+
options;
|
|
21804
|
+
cursor = 0;
|
|
21805
|
+
#s;
|
|
21806
|
+
getGroupItems(t2) {
|
|
21807
|
+
return this.options.filter((s2) => s2.group === t2);
|
|
21808
|
+
}
|
|
21809
|
+
isGroupSelected(t2) {
|
|
21810
|
+
const s2 = this.getGroupItems(t2), e2 = this.value;
|
|
21811
|
+
return e2 === undefined ? false : s2.every((i2) => e2.includes(i2.value));
|
|
21812
|
+
}
|
|
21813
|
+
toggleValue() {
|
|
21814
|
+
const t2 = this.options[this.cursor];
|
|
21815
|
+
if (this.value === undefined && (this.value = []), t2.group === true) {
|
|
21816
|
+
const s2 = t2.value, e2 = this.getGroupItems(s2);
|
|
21817
|
+
this.isGroupSelected(s2) ? this.value = this.value.filter((i2) => e2.findIndex((n2) => n2.value === i2) === -1) : this.value = [...this.value, ...e2.map((i2) => i2.value)], this.value = Array.from(new Set(this.value));
|
|
21818
|
+
} else {
|
|
21819
|
+
const s2 = this.value.includes(t2.value);
|
|
21820
|
+
this.value = s2 ? this.value.filter((e2) => e2 !== t2.value) : [...this.value, t2.value];
|
|
21821
|
+
}
|
|
21822
|
+
}
|
|
21823
|
+
constructor(t2) {
|
|
21824
|
+
super(t2, false);
|
|
21825
|
+
const { options: s2 } = t2;
|
|
21826
|
+
this.#s = t2.selectableGroups !== false, this.options = Object.entries(s2).flatMap(([e2, i2]) => [{ value: e2, group: true, label: e2 }, ...i2.map((n2) => ({ ...n2, group: e2 }))]), this.value = [...t2.initialValues ?? []], this.cursor = Math.max(this.options.findIndex(({ value: e2 }) => e2 === t2.cursorAt), this.#s ? 0 : 1), this.on("cursor", (e2) => {
|
|
21827
|
+
switch (e2) {
|
|
21828
|
+
case "left":
|
|
21829
|
+
case "up": {
|
|
21830
|
+
this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
|
|
21831
|
+
const i2 = this.options[this.cursor]?.group === true;
|
|
21832
|
+
!this.#s && i2 && (this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1);
|
|
21833
|
+
break;
|
|
21834
|
+
}
|
|
21835
|
+
case "down":
|
|
21836
|
+
case "right": {
|
|
21837
|
+
this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
|
|
21838
|
+
const i2 = this.options[this.cursor]?.group === true;
|
|
21839
|
+
!this.#s && i2 && (this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1);
|
|
21840
|
+
break;
|
|
21841
|
+
}
|
|
21842
|
+
case "space":
|
|
21843
|
+
this.toggleValue();
|
|
21844
|
+
break;
|
|
21845
|
+
}
|
|
21846
|
+
});
|
|
21847
|
+
}
|
|
21848
|
+
};
|
|
21849
|
+
var nt = new Set(["up", "down", "left", "right"]);
|
|
21850
|
+
|
|
21851
|
+
class ot extends m2 {
|
|
21852
|
+
#s = false;
|
|
21853
|
+
#r;
|
|
21854
|
+
focused = "editor";
|
|
21855
|
+
get userInputWithCursor() {
|
|
21856
|
+
if (this.state === "submit")
|
|
21857
|
+
return this.userInput;
|
|
21858
|
+
const t2 = this.userInput;
|
|
21859
|
+
if (this.cursor >= t2.length)
|
|
21860
|
+
return `${t2}\u2588`;
|
|
21861
|
+
const s2 = t2.slice(0, this.cursor), e2 = t2[this.cursor], i2 = t2.slice(this.cursor + 1);
|
|
21862
|
+
return e2 === `
|
|
21863
|
+
` ? `${s2}\u2588
|
|
21864
|
+
${i2}` : `${s2}${v2("inverse", e2)}${i2}`;
|
|
21865
|
+
}
|
|
21866
|
+
get cursor() {
|
|
21867
|
+
return this._cursor;
|
|
21868
|
+
}
|
|
21869
|
+
#t(t2) {
|
|
21870
|
+
if (this.userInput.length === 0) {
|
|
21871
|
+
this._setUserInput(t2);
|
|
21872
|
+
return;
|
|
21873
|
+
}
|
|
21874
|
+
this._setUserInput(this.userInput.slice(0, this.cursor) + t2 + this.userInput.slice(this.cursor));
|
|
21875
|
+
}
|
|
21876
|
+
#n(t2) {
|
|
21877
|
+
const s2 = this.value ?? "";
|
|
21878
|
+
switch (t2) {
|
|
21879
|
+
case "up":
|
|
21880
|
+
this._cursor = I3(this._cursor, 0, -1, s2);
|
|
21881
|
+
return;
|
|
21882
|
+
case "down":
|
|
21883
|
+
this._cursor = I3(this._cursor, 0, 1, s2);
|
|
21884
|
+
return;
|
|
21885
|
+
case "left":
|
|
21886
|
+
this._cursor = I3(this._cursor, -1, 0, s2);
|
|
21887
|
+
return;
|
|
21888
|
+
case "right":
|
|
21889
|
+
this._cursor = I3(this._cursor, 1, 0, s2);
|
|
21890
|
+
return;
|
|
21891
|
+
}
|
|
21892
|
+
}
|
|
21893
|
+
_shouldSubmit(t2, s2) {
|
|
21894
|
+
if (this.#r)
|
|
21895
|
+
return this.focused === "submit" ? true : (this.#t(`
|
|
21896
|
+
`), this._cursor++, false);
|
|
21897
|
+
const e2 = this.#s;
|
|
21898
|
+
return this.#s = true, e2 ? (this.userInput[this.cursor - 1] === `
|
|
21899
|
+
` && (this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--), true) : (this.#t(`
|
|
21900
|
+
`), this._cursor++, false);
|
|
21901
|
+
}
|
|
21902
|
+
constructor(t2) {
|
|
21903
|
+
super(t2, false), this.#r = t2.showSubmit ?? false, this.on("key", (s2, e2) => {
|
|
21904
|
+
if (e2?.name && nt.has(e2.name)) {
|
|
21905
|
+
this.#n(e2.name);
|
|
21906
|
+
return;
|
|
21907
|
+
}
|
|
21908
|
+
if (s2 === "\t" && this.#r) {
|
|
21909
|
+
this.focused = this.focused === "editor" ? "submit" : "editor";
|
|
21910
|
+
return;
|
|
21911
|
+
}
|
|
21912
|
+
if (e2?.name !== "return") {
|
|
21913
|
+
if (this.#s = false, e2?.name === "backspace" && this.cursor > 0) {
|
|
21914
|
+
this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--;
|
|
21915
|
+
return;
|
|
21916
|
+
}
|
|
21917
|
+
if (e2?.name === "delete" && this.cursor < this.userInput.length) {
|
|
21918
|
+
this._setUserInput(this.userInput.slice(0, this.cursor) + this.userInput.slice(this.cursor + 1));
|
|
21919
|
+
return;
|
|
21920
|
+
}
|
|
21921
|
+
s2 && (this.#r && this.focused === "submit" && (this.focused = "editor"), this.#t(s2 ?? ""), this._cursor++);
|
|
21922
|
+
}
|
|
21923
|
+
}), this.on("userInput", (s2) => {
|
|
21924
|
+
this._setValue(s2);
|
|
21925
|
+
}), this.on("finalize", () => {
|
|
21926
|
+
this.value || (this.value = t2.defaultValue), this.value === undefined && (this.value = "");
|
|
21927
|
+
});
|
|
21928
|
+
}
|
|
21929
|
+
}
|
|
21930
|
+
class ht extends m2 {
|
|
21931
|
+
options;
|
|
21932
|
+
cursor = 0;
|
|
21933
|
+
get _selectedValue() {
|
|
21934
|
+
return this.options[this.cursor];
|
|
21935
|
+
}
|
|
21936
|
+
changeValue() {
|
|
21937
|
+
this.value = this._selectedValue.value;
|
|
21938
|
+
}
|
|
21939
|
+
constructor(t2) {
|
|
21940
|
+
super(t2, false), this.options = t2.options;
|
|
21941
|
+
const s2 = this.options.findIndex(({ value: i2 }) => i2 === t2.initialValue), e2 = s2 === -1 ? 0 : s2;
|
|
21942
|
+
this.cursor = this.options[e2].disabled ? f3(e2, 1, this.options) : e2, this.changeValue(), this.on("cursor", (i2) => {
|
|
21943
|
+
switch (i2) {
|
|
21944
|
+
case "left":
|
|
21945
|
+
case "up":
|
|
21946
|
+
this.cursor = f3(this.cursor, -1, this.options);
|
|
21947
|
+
break;
|
|
21948
|
+
case "down":
|
|
21949
|
+
case "right":
|
|
21950
|
+
this.cursor = f3(this.cursor, 1, this.options);
|
|
21951
|
+
break;
|
|
21952
|
+
}
|
|
21953
|
+
this.changeValue();
|
|
21954
|
+
});
|
|
21955
|
+
}
|
|
21956
|
+
}
|
|
21957
|
+
class ct extends m2 {
|
|
21958
|
+
get userInputWithCursor() {
|
|
21959
|
+
if (this.state === "submit")
|
|
21960
|
+
return this.userInput;
|
|
21961
|
+
const t2 = this.userInput;
|
|
21962
|
+
if (this.cursor >= t2.length)
|
|
21963
|
+
return `${this.userInput}\u2588`;
|
|
21964
|
+
const s2 = t2.slice(0, this.cursor), [e2, ...i2] = t2.slice(this.cursor);
|
|
21965
|
+
return `${s2}${v2("inverse", e2)}${i2.join("")}`;
|
|
21966
|
+
}
|
|
21967
|
+
get cursor() {
|
|
21968
|
+
return this._cursor;
|
|
21969
|
+
}
|
|
21970
|
+
constructor(t2) {
|
|
21971
|
+
super({ ...t2, initialUserInput: t2.initialUserInput ?? t2.initialValue }), this.on("userInput", (s2) => {
|
|
21972
|
+
this._setValue(s2);
|
|
21973
|
+
}), this.on("finalize", () => {
|
|
21974
|
+
this.value || (this.value = t2.defaultValue), this.value === undefined && (this.value = "");
|
|
21975
|
+
});
|
|
21976
|
+
}
|
|
21977
|
+
}
|
|
21978
|
+
|
|
21979
|
+
// node_modules/@clack/prompts/dist/index.mjs
|
|
21980
|
+
import { styleText as e2, stripVTControlCharacters as ot2 } from "util";
|
|
21981
|
+
import V3 from "process";
|
|
21982
|
+
var import_sisteransi2 = __toESM(require_src(), 1);
|
|
21983
|
+
function se() {
|
|
21984
|
+
return V3.platform !== "win32" ? V3.env.TERM !== "linux" : !!V3.env.CI || !!V3.env.WT_SESSION || !!V3.env.TERMINUS_SUBLIME || V3.env.ConEmuTask === "{cmd::Cmder}" || V3.env.TERM_PROGRAM === "Terminus-Sublime" || V3.env.TERM_PROGRAM === "vscode" || V3.env.TERM === "xterm-256color" || V3.env.TERM === "alacritty" || V3.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
|
|
21985
|
+
}
|
|
21986
|
+
var tt2 = se();
|
|
21987
|
+
var at2 = () => process.env.CI === "true";
|
|
21988
|
+
var w3 = (t2, i2) => tt2 ? t2 : i2;
|
|
21989
|
+
var _t = w3("\u25C6", "*");
|
|
21990
|
+
var ut2 = w3("\u25A0", "x");
|
|
21991
|
+
var lt2 = w3("\u25B2", "x");
|
|
21992
|
+
var H2 = w3("\u25C7", "o");
|
|
21993
|
+
var ct2 = w3("\u250C", "T");
|
|
21994
|
+
var $2 = w3("\u2502", "|");
|
|
21995
|
+
var x3 = w3("\u2514", "\u2014");
|
|
21996
|
+
var xt = w3("\u2510", "T");
|
|
21997
|
+
var Et = w3("\u2518", "\u2014");
|
|
21998
|
+
var z4 = w3("\u25CF", ">");
|
|
21999
|
+
var U2 = w3("\u25CB", " ");
|
|
22000
|
+
var et2 = w3("\u25FB", "[\u2022]");
|
|
22001
|
+
var K2 = w3("\u25FC", "[+]");
|
|
22002
|
+
var Y3 = w3("\u25FB", "[ ]");
|
|
22003
|
+
var Gt = w3("\u25AA", "\u2022");
|
|
22004
|
+
var st2 = w3("\u2500", "-");
|
|
22005
|
+
var $t = w3("\u256E", "+");
|
|
22006
|
+
var Mt = w3("\u251C", "+");
|
|
22007
|
+
var dt = w3("\u256F", "+");
|
|
22008
|
+
var ht2 = w3("\u2570", "+");
|
|
22009
|
+
var Ot = w3("\u256D", "+");
|
|
22010
|
+
var pt = w3("\u25CF", "\u2022");
|
|
22011
|
+
var mt = w3("\u25C6", "*");
|
|
22012
|
+
var gt = w3("\u25B2", "!");
|
|
22013
|
+
var yt = w3("\u25A0", "x");
|
|
22014
|
+
var P4 = (t2) => {
|
|
22015
|
+
switch (t2) {
|
|
22016
|
+
case "initial":
|
|
22017
|
+
case "active":
|
|
22018
|
+
return e2("cyan", _t);
|
|
22019
|
+
case "cancel":
|
|
22020
|
+
return e2("red", ut2);
|
|
22021
|
+
case "error":
|
|
22022
|
+
return e2("yellow", lt2);
|
|
22023
|
+
case "submit":
|
|
22024
|
+
return e2("green", H2);
|
|
22025
|
+
}
|
|
22026
|
+
};
|
|
22027
|
+
var ft = (t2) => {
|
|
22028
|
+
switch (t2) {
|
|
22029
|
+
case "initial":
|
|
22030
|
+
case "active":
|
|
22031
|
+
return e2("cyan", $2);
|
|
22032
|
+
case "cancel":
|
|
22033
|
+
return e2("red", $2);
|
|
22034
|
+
case "error":
|
|
22035
|
+
return e2("yellow", $2);
|
|
22036
|
+
case "submit":
|
|
22037
|
+
return e2("green", $2);
|
|
22038
|
+
}
|
|
22039
|
+
};
|
|
22040
|
+
var Pt = (t2, i2, s2, r3, u3, n2 = false) => {
|
|
22041
|
+
let a2 = i2, c3 = 0;
|
|
22042
|
+
if (n2)
|
|
22043
|
+
for (let o3 = r3 - 1;o3 >= s2 && (a2 -= t2[o3].length, c3++, !(a2 <= u3)); o3--)
|
|
22044
|
+
;
|
|
22045
|
+
else
|
|
22046
|
+
for (let o3 = s2;o3 < r3 && (a2 -= t2[o3].length, c3++, !(a2 <= u3)); o3++)
|
|
22047
|
+
;
|
|
22048
|
+
return { lineCount: a2, removals: c3 };
|
|
22049
|
+
};
|
|
22050
|
+
var F4 = ({ cursor: t2, options: i2, style: s2, output: r3 = process.stdout, maxItems: u3 = Number.POSITIVE_INFINITY, columnPadding: n2 = 0, rowPadding: a2 = 4 }) => {
|
|
22051
|
+
const c3 = A3(r3) - n2, o3 = L3(r3), l2 = e2("dim", "..."), d2 = Math.max(o3 - a2, 0), g3 = Math.max(Math.min(u3, d2), 5);
|
|
22052
|
+
let p2 = 0;
|
|
22053
|
+
t2 >= g3 - 3 && (p2 = Math.max(Math.min(t2 - g3 + 3, i2.length - g3), 0));
|
|
22054
|
+
let f4 = g3 < i2.length && p2 > 0, h3 = g3 < i2.length && p2 + g3 < i2.length;
|
|
22055
|
+
const I4 = Math.min(p2 + g3, i2.length), m3 = [];
|
|
22056
|
+
let y3 = 0;
|
|
22057
|
+
f4 && y3++, h3 && y3++;
|
|
22058
|
+
const v3 = p2 + (f4 ? 1 : 0), C4 = I4 - (h3 ? 1 : 0);
|
|
22059
|
+
for (let b3 = v3;b3 < C4; b3++) {
|
|
22060
|
+
const G4 = wrapAnsi(s2(i2[b3], b3 === t2), c3, { hard: true, trim: false }).split(`
|
|
22061
|
+
`);
|
|
22062
|
+
m3.push(G4), y3 += G4.length;
|
|
22063
|
+
}
|
|
22064
|
+
if (y3 > d2) {
|
|
22065
|
+
let b3 = 0, G4 = 0, M = y3;
|
|
22066
|
+
const N4 = t2 - v3;
|
|
22067
|
+
let O4 = d2;
|
|
22068
|
+
const j2 = () => Pt(m3, M, 0, N4, O4), k3 = () => Pt(m3, M, N4 + 1, m3.length, O4, true);
|
|
22069
|
+
f4 ? ({ lineCount: M, removals: b3 } = j2(), M > O4 && (h3 || (O4 -= 1), { lineCount: M, removals: G4 } = k3())) : (h3 || (O4 -= 1), { lineCount: M, removals: G4 } = k3(), M > O4 && (O4 -= 1, { lineCount: M, removals: b3 } = j2())), b3 > 0 && (f4 = true, m3.splice(0, b3)), G4 > 0 && (h3 = true, m3.splice(m3.length - G4, G4));
|
|
22070
|
+
}
|
|
22071
|
+
const S4 = [];
|
|
22072
|
+
f4 && S4.push(l2);
|
|
22073
|
+
for (const b3 of m3)
|
|
22074
|
+
for (const G4 of b3)
|
|
22075
|
+
S4.push(G4);
|
|
22076
|
+
return h3 && S4.push(l2), S4;
|
|
22077
|
+
};
|
|
22078
|
+
var le2 = (t2) => {
|
|
22079
|
+
const i2 = t2.active ?? "Yes", s2 = t2.inactive ?? "No";
|
|
22080
|
+
return new Z({ active: i2, inactive: s2, signal: t2.signal, input: t2.input, output: t2.output, initialValue: t2.initialValue ?? true, render() {
|
|
22081
|
+
const r3 = t2.withGuide ?? h2.withGuide, u3 = `${P4(this.state)} `, n2 = r3 ? `${e2("gray", $2)} ` : "", a2 = B2(t2.output, t2.message, n2, u3), c3 = `${r3 ? `${e2("gray", $2)}
|
|
22082
|
+
` : ""}${a2}
|
|
22083
|
+
`, o3 = this.value ? i2 : s2;
|
|
22084
|
+
switch (this.state) {
|
|
22085
|
+
case "submit": {
|
|
22086
|
+
const l2 = r3 ? `${e2("gray", $2)} ` : "";
|
|
22087
|
+
return `${c3}${l2}${e2("dim", o3)}`;
|
|
22088
|
+
}
|
|
22089
|
+
case "cancel": {
|
|
22090
|
+
const l2 = r3 ? `${e2("gray", $2)} ` : "";
|
|
22091
|
+
return `${c3}${l2}${e2(["strikethrough", "dim"], o3)}${r3 ? `
|
|
22092
|
+
${e2("gray", $2)}` : ""}`;
|
|
22093
|
+
}
|
|
22094
|
+
default: {
|
|
22095
|
+
const l2 = r3 ? `${e2("cyan", $2)} ` : "", d2 = r3 ? e2("cyan", x3) : "";
|
|
22096
|
+
return `${c3}${l2}${this.value ? `${e2("green", z4)} ${i2}` : `${e2("dim", U2)} ${e2("dim", i2)}`}${t2.vertical ? r3 ? `
|
|
22097
|
+
${e2("cyan", $2)} ` : `
|
|
22098
|
+
` : ` ${e2("dim", "/")} `}${this.value ? `${e2("dim", U2)} ${e2("dim", s2)}` : `${e2("green", z4)} ${s2}`}
|
|
22099
|
+
${d2}
|
|
22100
|
+
`;
|
|
22101
|
+
}
|
|
22102
|
+
}
|
|
22103
|
+
} }).prompt();
|
|
22104
|
+
};
|
|
22105
|
+
var ge = (t2 = "", i2) => {
|
|
22106
|
+
const s2 = i2?.output ?? process.stdout, r3 = i2?.withGuide ?? h2.withGuide ? `${e2("gray", x3)} ` : "";
|
|
22107
|
+
s2.write(`${r3}${e2("red", t2)}
|
|
22108
|
+
|
|
22109
|
+
`);
|
|
22110
|
+
};
|
|
22111
|
+
var ye2 = (t2 = "", i2) => {
|
|
22112
|
+
const s2 = i2?.output ?? process.stdout, r3 = i2?.withGuide ?? h2.withGuide ? `${e2("gray", ct2)} ` : "";
|
|
22113
|
+
s2.write(`${r3}${t2}
|
|
22114
|
+
`);
|
|
22115
|
+
};
|
|
22116
|
+
var fe2 = (t2 = "", i2) => {
|
|
22117
|
+
const s2 = i2?.output ?? process.stdout, r3 = i2?.withGuide ?? h2.withGuide ? `${e2("gray", $2)}
|
|
22118
|
+
${e2("gray", x3)} ` : "";
|
|
22119
|
+
s2.write(`${r3}${t2}
|
|
22120
|
+
|
|
22121
|
+
`);
|
|
22122
|
+
};
|
|
22123
|
+
var be = (t2) => e2("dim", t2);
|
|
22124
|
+
var Se = (t2, i2, s2) => {
|
|
22125
|
+
const r3 = { hard: true, trim: false }, u3 = wrapAnsi(t2, i2, r3).split(`
|
|
22126
|
+
`), n2 = u3.reduce((o3, l2) => Math.max(dist_default2(l2), o3), 0), a2 = u3.map(s2).reduce((o3, l2) => Math.max(dist_default2(l2), o3), 0), c3 = i2 - (a2 - n2);
|
|
22127
|
+
return wrapAnsi(t2, c3, r3);
|
|
22128
|
+
};
|
|
22129
|
+
var Ce = (t2 = "", i2 = "", s2) => {
|
|
22130
|
+
const r3 = s2?.output ?? V3.stdout, u3 = s2?.withGuide ?? h2.withGuide, n2 = s2?.format ?? be, a2 = ["", ...Se(t2, A3(r3) - 6, n2).split(`
|
|
22131
|
+
`).map(n2), ""], c3 = dist_default2(i2), o3 = Math.max(a2.reduce((p2, f4) => {
|
|
22132
|
+
const h3 = dist_default2(f4);
|
|
22133
|
+
return h3 > p2 ? h3 : p2;
|
|
22134
|
+
}, 0), c3) + 2, l2 = a2.map((p2) => `${e2("gray", $2)} ${p2}${" ".repeat(o3 - dist_default2(p2))}${e2("gray", $2)}`).join(`
|
|
22135
|
+
`), d2 = u3 ? `${e2("gray", $2)}
|
|
22136
|
+
` : "", g3 = u3 ? Mt : ht2;
|
|
22137
|
+
r3.write(`${d2}${e2("green", H2)} ${e2("reset", i2)} ${e2("gray", st2.repeat(Math.max(o3 - c3 - 1, 1)) + $t)}
|
|
22138
|
+
${l2}
|
|
22139
|
+
${e2("gray", g3 + st2.repeat(o3 + 2) + dt)}
|
|
22140
|
+
`);
|
|
22141
|
+
};
|
|
22142
|
+
var _e = (t2) => e2("magenta", t2);
|
|
22143
|
+
var vt = ({ indicator: t2 = "dots", onCancel: i2, output: s2 = process.stdout, cancelMessage: r3, errorMessage: u3, frames: n2 = tt2 ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"], delay: a2 = tt2 ? 80 : 120, signal: c3, ...o3 } = {}) => {
|
|
22144
|
+
const l2 = at2();
|
|
22145
|
+
let d2, g3, p2 = false, f4 = false, h3 = "", I4, m3 = performance.now();
|
|
22146
|
+
const y3 = A3(s2), v3 = o3?.styleFrame ?? _e, C4 = (_3) => {
|
|
22147
|
+
const A4 = _3 > 1 ? u3 ?? h2.messages.error : r3 ?? h2.messages.cancel;
|
|
22148
|
+
f4 = _3 === 1, p2 && (W3(A4, _3), f4 && typeof i2 == "function" && i2());
|
|
22149
|
+
}, S4 = () => C4(2), b3 = () => C4(1), G4 = () => {
|
|
22150
|
+
process.on("uncaughtExceptionMonitor", S4), process.on("unhandledRejection", S4), process.on("SIGINT", b3), process.on("SIGTERM", b3), process.on("exit", C4), c3 && c3.addEventListener("abort", b3);
|
|
22151
|
+
}, M = () => {
|
|
22152
|
+
process.removeListener("uncaughtExceptionMonitor", S4), process.removeListener("unhandledRejection", S4), process.removeListener("SIGINT", b3), process.removeListener("SIGTERM", b3), process.removeListener("exit", C4), c3 && c3.removeEventListener("abort", b3);
|
|
22153
|
+
}, N4 = () => {
|
|
22154
|
+
if (I4 === undefined)
|
|
22155
|
+
return;
|
|
22156
|
+
l2 && s2.write(`
|
|
22157
|
+
`);
|
|
22158
|
+
const _3 = wrapAnsi(I4, y3, { hard: true, trim: false }).split(`
|
|
22159
|
+
`);
|
|
22160
|
+
_3.length > 1 && s2.write(import_sisteransi2.cursor.up(_3.length - 1)), s2.write(import_sisteransi2.cursor.to(0)), s2.write(import_sisteransi2.erase.down());
|
|
22161
|
+
}, O4 = (_3) => _3.replace(/\.+$/, ""), j2 = (_3) => {
|
|
22162
|
+
const A4 = (performance.now() - _3) / 1000, L4 = Math.floor(A4 / 60), D3 = Math.floor(A4 % 60);
|
|
22163
|
+
return L4 > 0 ? `[${L4}m ${D3}s]` : `[${D3}s]`;
|
|
22164
|
+
}, k3 = o3.withGuide ?? h2.withGuide, rt2 = (_3 = "") => {
|
|
22165
|
+
p2 = true, d2 = W2({ output: s2 }), h3 = O4(_3), m3 = performance.now(), k3 && s2.write(`${e2("gray", $2)}
|
|
22166
|
+
`);
|
|
22167
|
+
let A4 = 0, L4 = 0;
|
|
22168
|
+
G4(), g3 = setInterval(() => {
|
|
22169
|
+
if (l2 && h3 === I4)
|
|
22170
|
+
return;
|
|
22171
|
+
N4(), I4 = h3;
|
|
22172
|
+
const D3 = v3(n2[A4]);
|
|
22173
|
+
let Z2;
|
|
22174
|
+
if (l2)
|
|
22175
|
+
Z2 = `${D3} ${h3}...`;
|
|
22176
|
+
else if (t2 === "timer")
|
|
22177
|
+
Z2 = `${D3} ${h3} ${j2(m3)}`;
|
|
22178
|
+
else {
|
|
22179
|
+
const Lt = ".".repeat(Math.floor(L4)).slice(0, 3);
|
|
22180
|
+
Z2 = `${D3} ${h3}${Lt}`;
|
|
22181
|
+
}
|
|
22182
|
+
const kt = wrapAnsi(Z2, y3, { hard: true, trim: false });
|
|
22183
|
+
s2.write(kt), A4 = A4 + 1 < n2.length ? A4 + 1 : 0, L4 = L4 < 4 ? L4 + 0.125 : 0;
|
|
22184
|
+
}, a2);
|
|
22185
|
+
}, W3 = (_3 = "", A4 = 0, L4 = false) => {
|
|
22186
|
+
if (!p2)
|
|
22187
|
+
return;
|
|
22188
|
+
p2 = false, clearInterval(g3), N4();
|
|
22189
|
+
const D3 = A4 === 0 ? e2("green", H2) : A4 === 1 ? e2("red", ut2) : e2("red", lt2);
|
|
22190
|
+
h3 = _3 ?? h3, L4 || (t2 === "timer" ? s2.write(`${D3} ${h3} ${j2(m3)}
|
|
22191
|
+
`) : s2.write(`${D3} ${h3}
|
|
22192
|
+
`)), M(), d2();
|
|
22193
|
+
};
|
|
22194
|
+
return { start: rt2, stop: (_3 = "") => W3(_3, 0), message: (_3 = "") => {
|
|
22195
|
+
h3 = O4(_3 ?? h3);
|
|
22196
|
+
}, cancel: (_3 = "") => W3(_3, 1), error: (_3 = "") => W3(_3, 2), clear: () => W3("", 0, true), get isCancelled() {
|
|
22197
|
+
return f4;
|
|
22198
|
+
} };
|
|
22199
|
+
};
|
|
22200
|
+
var Nt = { light: w3("\u2500", "-"), heavy: w3("\u2501", "="), block: w3("\u2588", "#") };
|
|
22201
|
+
var it2 = (t2, i2) => t2.includes(`
|
|
22202
|
+
`) ? t2.split(`
|
|
22203
|
+
`).map((s2) => i2(s2)).join(`
|
|
22204
|
+
`) : i2(t2);
|
|
22205
|
+
var Ee = (t2) => {
|
|
22206
|
+
const i2 = (s2, r3) => {
|
|
22207
|
+
const u3 = s2.label ?? String(s2.value);
|
|
22208
|
+
switch (r3) {
|
|
22209
|
+
case "disabled":
|
|
22210
|
+
return `${e2("gray", U2)} ${it2(u3, (n2) => e2("gray", n2))}${s2.hint ? ` ${e2("dim", `(${s2.hint ?? "disabled"})`)}` : ""}`;
|
|
22211
|
+
case "selected":
|
|
22212
|
+
return `${it2(u3, (n2) => e2("dim", n2))}`;
|
|
22213
|
+
case "active":
|
|
22214
|
+
return `${e2("green", z4)} ${u3}${s2.hint ? ` ${e2("dim", `(${s2.hint})`)}` : ""}`;
|
|
22215
|
+
case "cancelled":
|
|
22216
|
+
return `${it2(u3, (n2) => e2(["strikethrough", "dim"], n2))}`;
|
|
22217
|
+
default:
|
|
22218
|
+
return `${e2("dim", U2)} ${it2(u3, (n2) => e2("dim", n2))}`;
|
|
22219
|
+
}
|
|
22220
|
+
};
|
|
22221
|
+
return new ht({ options: t2.options, signal: t2.signal, input: t2.input, output: t2.output, initialValue: t2.initialValue, render() {
|
|
22222
|
+
const s2 = t2.withGuide ?? h2.withGuide, r3 = `${P4(this.state)} `, u3 = `${ft(this.state)} `, n2 = B2(t2.output, t2.message, u3, r3), a2 = `${s2 ? `${e2("gray", $2)}
|
|
22223
|
+
` : ""}${n2}
|
|
22224
|
+
`;
|
|
22225
|
+
switch (this.state) {
|
|
22226
|
+
case "submit": {
|
|
22227
|
+
const c3 = s2 ? `${e2("gray", $2)} ` : "", o3 = B2(t2.output, i2(this.options[this.cursor], "selected"), c3);
|
|
22228
|
+
return `${a2}${o3}`;
|
|
22229
|
+
}
|
|
22230
|
+
case "cancel": {
|
|
22231
|
+
const c3 = s2 ? `${e2("gray", $2)} ` : "", o3 = B2(t2.output, i2(this.options[this.cursor], "cancelled"), c3);
|
|
22232
|
+
return `${a2}${o3}${s2 ? `
|
|
22233
|
+
${e2("gray", $2)}` : ""}`;
|
|
22234
|
+
}
|
|
22235
|
+
default: {
|
|
22236
|
+
const c3 = s2 ? `${e2("cyan", $2)} ` : "", o3 = s2 ? e2("cyan", x3) : "", l2 = a2.split(`
|
|
22237
|
+
`).length, d2 = s2 ? 2 : 1;
|
|
22238
|
+
return `${a2}${c3}${F4({ output: t2.output, cursor: this.cursor, options: this.options, maxItems: t2.maxItems, columnPadding: c3.length, rowPadding: l2 + d2, style: (g3, p2) => i2(g3, g3.disabled ? "disabled" : p2 ? "active" : "inactive") }).join(`
|
|
22239
|
+
${c3}`)}
|
|
22240
|
+
${o3}
|
|
22241
|
+
`;
|
|
22242
|
+
}
|
|
22243
|
+
}
|
|
22244
|
+
} }).prompt();
|
|
22245
|
+
};
|
|
22246
|
+
var Bt = `${e2("gray", $2)} `;
|
|
22247
|
+
var Re = (t2) => new ct({ validate: t2.validate, placeholder: t2.placeholder, defaultValue: t2.defaultValue, initialValue: t2.initialValue, output: t2.output, signal: t2.signal, input: t2.input, render() {
|
|
22248
|
+
const i2 = t2?.withGuide ?? h2.withGuide, s2 = `${`${i2 ? `${e2("gray", $2)}
|
|
22249
|
+
` : ""}${P4(this.state)} `}${t2.message}
|
|
22250
|
+
`, r3 = t2.placeholder ? e2("inverse", t2.placeholder[0]) + e2("dim", t2.placeholder.slice(1)) : e2(["inverse", "hidden"], "_"), u3 = this.userInput ? this.userInputWithCursor : r3, n2 = this.value ?? "";
|
|
22251
|
+
switch (this.state) {
|
|
22252
|
+
case "error": {
|
|
22253
|
+
const a2 = this.error ? ` ${e2("yellow", this.error)}` : "", c3 = i2 ? `${e2("yellow", $2)} ` : "", o3 = i2 ? e2("yellow", x3) : "";
|
|
22254
|
+
return `${s2.trim()}
|
|
22255
|
+
${c3}${u3}
|
|
22256
|
+
${o3}${a2}
|
|
22257
|
+
`;
|
|
22258
|
+
}
|
|
22259
|
+
case "submit": {
|
|
22260
|
+
const a2 = n2 ? ` ${e2("dim", n2)}` : "", c3 = i2 ? e2("gray", $2) : "";
|
|
22261
|
+
return `${s2}${c3}${a2}`;
|
|
22262
|
+
}
|
|
22263
|
+
case "cancel": {
|
|
22264
|
+
const a2 = n2 ? ` ${e2(["strikethrough", "dim"], n2)}` : "", c3 = i2 ? e2("gray", $2) : "";
|
|
22265
|
+
return `${s2}${c3}${a2}${n2.trim() ? `
|
|
22266
|
+
${c3}` : ""}`;
|
|
22267
|
+
}
|
|
22268
|
+
default: {
|
|
22269
|
+
const a2 = i2 ? `${e2("cyan", $2)} ` : "", c3 = i2 ? e2("cyan", x3) : "";
|
|
22270
|
+
return `${s2}${a2}${u3}
|
|
22271
|
+
${c3}
|
|
22272
|
+
`;
|
|
22273
|
+
}
|
|
22274
|
+
}
|
|
22275
|
+
} }).prompt();
|
|
22276
|
+
|
|
22277
|
+
// src/cli/commands/config.ts
|
|
22278
|
+
var iso2 = (ms) => ms ? new Date(ms).toISOString() : "";
|
|
22279
|
+
var isTTY2 = () => process.stdout.isTTY && process.stdin.isTTY;
|
|
22280
|
+
function ask(label, def) {
|
|
22281
|
+
const suffix = def != null && def !== "" ? ` [${def}]` : "";
|
|
22282
|
+
const raw = globalThis.prompt(`${label}${suffix}:`);
|
|
22283
|
+
const val = (raw ?? "").trim();
|
|
22284
|
+
return val === "" && def != null ? def : val;
|
|
22285
|
+
}
|
|
22286
|
+
function flagOrAsk(flag, label) {
|
|
22287
|
+
if (typeof flag === "string" && flag !== "")
|
|
22288
|
+
return flag;
|
|
22289
|
+
if (isTTY2()) {
|
|
22290
|
+
const v3 = ask(label);
|
|
22291
|
+
if (v3)
|
|
22292
|
+
return v3;
|
|
22293
|
+
}
|
|
22294
|
+
throw new Error(`missing --${label}`);
|
|
22295
|
+
}
|
|
22296
|
+
function showConfig(db) {
|
|
22297
|
+
const cfg = getServerConfig(db);
|
|
22298
|
+
const peers = listPeers(db).map(peerView);
|
|
22299
|
+
print({ config: cfg, peers }, () => {
|
|
22300
|
+
const lines = [
|
|
22301
|
+
"server config:",
|
|
22302
|
+
` host: ${cfg.host}`,
|
|
22303
|
+
` port: ${cfg.port}`,
|
|
22304
|
+
` sync-interval: ${Math.round(cfg.syncIntervalMs / 1000)}s`,
|
|
22305
|
+
` auto-sync: ${cfg.autoSync}`,
|
|
22306
|
+
"",
|
|
22307
|
+
"peers:",
|
|
22308
|
+
peers.length ? table(peers) : " (none)"
|
|
22309
|
+
];
|
|
22310
|
+
return lines.join(`
|
|
22311
|
+
`);
|
|
22312
|
+
});
|
|
22313
|
+
}
|
|
22314
|
+
function peerView(p2) {
|
|
22315
|
+
return {
|
|
22316
|
+
url: p2.url,
|
|
22317
|
+
label: p2.label ?? "",
|
|
22318
|
+
enabled: p2.enabled ? "yes" : "no",
|
|
22319
|
+
status: p2.last_status ?? "",
|
|
22320
|
+
last_sync: iso2(p2.last_sync_at)
|
|
22321
|
+
};
|
|
22322
|
+
}
|
|
22323
|
+
function applySet(db, args) {
|
|
22324
|
+
const partial2 = {};
|
|
22325
|
+
if (typeof args.host === "string" && args.host !== "")
|
|
22326
|
+
partial2.host = args.host;
|
|
22327
|
+
if (args.port != null && args.port !== "")
|
|
22328
|
+
partial2.port = Number(args.port);
|
|
22329
|
+
if (args["sync-interval"] != null && args["sync-interval"] !== "")
|
|
22330
|
+
partial2.syncIntervalMs = parseDuration(String(args["sync-interval"]), 30000);
|
|
22331
|
+
if (args["auto-sync"] != null)
|
|
22332
|
+
partial2.autoSync = parseBool(args["auto-sync"]);
|
|
22333
|
+
const next = setServerConfig(db, partial2);
|
|
22334
|
+
print(next, () => `updated. (restart --server to apply host/port/interval changes)`);
|
|
22335
|
+
}
|
|
22336
|
+
function parseBool(v3) {
|
|
22337
|
+
if (typeof v3 === "boolean")
|
|
22338
|
+
return v3;
|
|
22339
|
+
const s2 = String(v3).trim().toLowerCase();
|
|
22340
|
+
return s2 === "1" || s2 === "true" || s2 === "yes" || s2 === "on";
|
|
22341
|
+
}
|
|
22342
|
+
async function peerDispatch(db, action, args) {
|
|
22343
|
+
const node = getNodeId(db);
|
|
22344
|
+
switch (action) {
|
|
22345
|
+
case "add": {
|
|
22346
|
+
const url2 = flagOrAsk(args.url, "url");
|
|
22347
|
+
const code = flagOrAsk(args.code, "code");
|
|
22348
|
+
const selfUrl = typeof args["self-url"] === "string" ? args["self-url"] : undefined;
|
|
22349
|
+
const r3 = await performPairing(db, node, url2, code, selfUrl);
|
|
22350
|
+
const sync = await syncPeer(db, url2);
|
|
22351
|
+
print({ paired: r3, sync }, () => `paired with ${r3.url} (node ${r3.node_id}); ${syncLine(sync)}`);
|
|
22352
|
+
return;
|
|
22353
|
+
}
|
|
22354
|
+
case "code": {
|
|
22355
|
+
const c3 = generatePairingCode(db);
|
|
22356
|
+
print(c3, () => `pairing code: ${c3.code}
|
|
22357
|
+
expires: ${iso2(c3.exp)}
|
|
22358
|
+
(redeem with: mh config peer add --url <this-server-url> --code ${c3.code})`);
|
|
22359
|
+
return;
|
|
22360
|
+
}
|
|
22361
|
+
case "list":
|
|
22362
|
+
print(listPeers(db).map(peerView), () => {
|
|
22363
|
+
const rows = listPeers(db).map(peerView);
|
|
22364
|
+
return rows.length ? table(rows) : "(no peers)";
|
|
22365
|
+
});
|
|
22366
|
+
return;
|
|
22367
|
+
case "rm": {
|
|
22368
|
+
const url2 = flagOrAsk(args.url, "url");
|
|
22369
|
+
print({ ok: removePeer(db, url2) }, () => `removed ${url2}`);
|
|
22370
|
+
return;
|
|
22371
|
+
}
|
|
22372
|
+
case "enable":
|
|
22373
|
+
case "disable": {
|
|
22374
|
+
const url2 = flagOrAsk(args.url, "url");
|
|
22375
|
+
setPeerEnabled(db, url2, action === "enable");
|
|
22376
|
+
print({ ok: true }, () => `${action}d ${url2}`);
|
|
22377
|
+
return;
|
|
22378
|
+
}
|
|
22379
|
+
case "sync": {
|
|
22380
|
+
if (typeof args.url === "string" && args.url !== "") {
|
|
22381
|
+
const r3 = await syncPeer(db, args.url);
|
|
22382
|
+
print(r3, () => syncLine(r3));
|
|
22383
|
+
} else {
|
|
22384
|
+
const r3 = await syncAllPeers(db);
|
|
22385
|
+
print(r3, () => r3.length ? r3.map(syncLine).join(`
|
|
22386
|
+
`) : "(no enabled peers)");
|
|
22387
|
+
}
|
|
22388
|
+
return;
|
|
22389
|
+
}
|
|
22390
|
+
default:
|
|
22391
|
+
throw new Error(`unknown peer action '${action}' (add|code|list|rm|enable|disable|sync)`);
|
|
22392
|
+
}
|
|
22393
|
+
}
|
|
22394
|
+
function syncLine(o3) {
|
|
22395
|
+
return o3.ok ? `${o3.url}: pushed ${o3.pushed}, pulled ${o3.pulled}` : `${o3.url}: error \u2014 ${o3.error}`;
|
|
22396
|
+
}
|
|
22397
|
+
var maskToken = (t2) => t2.length > 10 ? `${t2.slice(0, 8)}\u2026` : t2;
|
|
22398
|
+
function grantView(g3) {
|
|
22399
|
+
return {
|
|
22400
|
+
token: maskToken(g3.token),
|
|
22401
|
+
peer_url: g3.peer_url ?? "(unknown)",
|
|
22402
|
+
node_id: g3.node_id ?? "",
|
|
22403
|
+
issued: iso2(g3.created_at)
|
|
22404
|
+
};
|
|
22405
|
+
}
|
|
22406
|
+
function grantDispatch(db, action, args) {
|
|
22407
|
+
switch (action) {
|
|
22408
|
+
case "list": {
|
|
22409
|
+
const rows = listGrants(db).map(grantView);
|
|
22410
|
+
print(rows, () => rows.length ? table(rows) : "(no issued credentials)");
|
|
22411
|
+
return;
|
|
22412
|
+
}
|
|
22413
|
+
case "revoke": {
|
|
22414
|
+
const token = flagOrAsk(args.token, "token");
|
|
22415
|
+
const n2 = revokeGrant(db, token);
|
|
22416
|
+
print({ revoked: n2 }, () => n2 > 0 ? `revoked ${n2} credential(s)` : "no matching credential");
|
|
22417
|
+
return;
|
|
22418
|
+
}
|
|
22419
|
+
default:
|
|
22420
|
+
throw new Error(`unknown grant action '${action}' (list|revoke)`);
|
|
22421
|
+
}
|
|
22422
|
+
}
|
|
22423
|
+
var secs = (ms) => `${Math.round(ms / 1000)}s`;
|
|
22424
|
+
function cancelled(v3) {
|
|
22425
|
+
if (R3(v3)) {
|
|
22426
|
+
ge("\u5DF2\u53D6\u6D88");
|
|
22427
|
+
return true;
|
|
22428
|
+
}
|
|
22429
|
+
return false;
|
|
22430
|
+
}
|
|
22431
|
+
function peerChoices(peers) {
|
|
22432
|
+
return peers.map((pr) => ({
|
|
22433
|
+
value: pr.url,
|
|
22434
|
+
label: pr.label ? `${pr.label} (${pr.url})` : pr.url,
|
|
22435
|
+
hint: pr.enabled ? "enabled" : "disabled"
|
|
22436
|
+
}));
|
|
22437
|
+
}
|
|
22438
|
+
function grantChoices(grants) {
|
|
22439
|
+
return grants.map((g3) => ({
|
|
22440
|
+
value: g3.token,
|
|
22441
|
+
label: maskToken(g3.token),
|
|
22442
|
+
hint: g3.peer_url ?? "(unknown)"
|
|
22443
|
+
}));
|
|
22444
|
+
}
|
|
22445
|
+
function validatePort(s2) {
|
|
22446
|
+
const n2 = Number(s2);
|
|
22447
|
+
if (!Number.isInteger(n2) || n2 <= 0 || n2 > 65535)
|
|
22448
|
+
return "\u7AEF\u53E3\u5FC5\u987B\u662F 1-65535 \u7684\u6574\u6570";
|
|
22449
|
+
return;
|
|
22450
|
+
}
|
|
22451
|
+
function validateInterval(s2) {
|
|
22452
|
+
return parseDuration(s2, -1) > 0 ? undefined : "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u65F6\u957F (\u5982 30s, 5m)";
|
|
22453
|
+
}
|
|
22454
|
+
var required2 = (v3) => v3 && v3.trim() ? undefined : "\u5FC5\u586B";
|
|
22455
|
+
async function serverWizard(db) {
|
|
22456
|
+
const working = { ...getServerConfig(db) };
|
|
22457
|
+
for (;; ) {
|
|
22458
|
+
const field = await Ee({
|
|
22459
|
+
message: "\u670D\u52A1\u5668\u8BBE\u7F6E",
|
|
22460
|
+
options: [
|
|
22461
|
+
{ value: "host", label: "Host", hint: working.host },
|
|
22462
|
+
{ value: "port", label: "Port", hint: String(working.port) },
|
|
22463
|
+
{ value: "interval", label: "\u540C\u6B65\u95F4\u9694", hint: secs(working.syncIntervalMs) },
|
|
22464
|
+
{ value: "auto", label: "Auto-sync", hint: working.autoSync ? "on" : "off" },
|
|
22465
|
+
{ value: "save", label: "\u4FDD\u5B58\u5E76\u8FD4\u56DE" },
|
|
22466
|
+
{ value: "back", label: "\u8FD4\u56DE (\u653E\u5F03\u4FEE\u6539)" }
|
|
22467
|
+
]
|
|
22468
|
+
});
|
|
22469
|
+
if (cancelled(field) || field === "back")
|
|
22470
|
+
return;
|
|
22471
|
+
if (field === "save") {
|
|
22472
|
+
setServerConfig(db, working);
|
|
22473
|
+
Ce("restart --server to apply host/port/interval changes", "\u2713 \u5DF2\u4FDD\u5B58");
|
|
22474
|
+
return;
|
|
22475
|
+
}
|
|
22476
|
+
if (field === "host") {
|
|
22477
|
+
const v3 = await Re({ message: "Host", initialValue: working.host });
|
|
22478
|
+
if (cancelled(v3))
|
|
22479
|
+
return;
|
|
22480
|
+
working.host = v3;
|
|
22481
|
+
} else if (field === "port") {
|
|
22482
|
+
const v3 = await Re({ message: "Port", initialValue: String(working.port), validate: validatePort });
|
|
22483
|
+
if (cancelled(v3))
|
|
22484
|
+
return;
|
|
22485
|
+
working.port = Number(v3);
|
|
22486
|
+
} else if (field === "interval") {
|
|
22487
|
+
const v3 = await Re({ message: "\u540C\u6B65\u95F4\u9694 (\u5982 30s, 5m)", initialValue: secs(working.syncIntervalMs), validate: validateInterval });
|
|
22488
|
+
if (cancelled(v3))
|
|
22489
|
+
return;
|
|
22490
|
+
working.syncIntervalMs = parseDuration(v3, working.syncIntervalMs);
|
|
22491
|
+
} else if (field === "auto") {
|
|
22492
|
+
const v3 = await le2({ message: "\u542F\u7528 auto-sync?", initialValue: working.autoSync });
|
|
22493
|
+
if (cancelled(v3))
|
|
22494
|
+
return;
|
|
22495
|
+
working.autoSync = v3;
|
|
22496
|
+
}
|
|
22497
|
+
}
|
|
22498
|
+
}
|
|
22499
|
+
async function peerWizard(db) {
|
|
22500
|
+
const node = getNodeId(db);
|
|
22501
|
+
for (;; ) {
|
|
22502
|
+
const action = await Ee({
|
|
22503
|
+
message: "\u540C\u6B65\u8BBE\u5907",
|
|
22504
|
+
options: [
|
|
22505
|
+
{ value: "add", label: "\u6DFB\u52A0\u8BBE\u5907", hint: "\u5BF9\u65B9\u5730\u5740 + \u914D\u5BF9\u7801" },
|
|
22506
|
+
{ value: "code", label: "\u751F\u6210\u672C\u673A\u914D\u5BF9\u7801" },
|
|
22507
|
+
{ value: "list", label: "\u5217\u51FA\u8BBE\u5907" },
|
|
22508
|
+
{ value: "rm", label: "\u79FB\u9664\u8BBE\u5907" },
|
|
22509
|
+
{ value: "toggle", label: "\u542F\u7528 / \u7981\u7528\u8BBE\u5907" },
|
|
22510
|
+
{ value: "sync", label: "\u7ACB\u5373\u540C\u6B65\u5168\u90E8" },
|
|
22511
|
+
{ value: "back", label: "\u8FD4\u56DE" }
|
|
22512
|
+
]
|
|
22513
|
+
});
|
|
22514
|
+
if (cancelled(action) || action === "back")
|
|
22515
|
+
return;
|
|
22516
|
+
try {
|
|
22517
|
+
if (action === "add") {
|
|
22518
|
+
const url2 = await Re({ message: "\u5BF9\u65B9\u670D\u52A1\u5668\u5730\u5740", placeholder: "http://192.168.1.10:7777", validate: required2 });
|
|
22519
|
+
if (cancelled(url2))
|
|
22520
|
+
continue;
|
|
22521
|
+
const code = await Re({ message: "\u914D\u5BF9\u7801", validate: required2 });
|
|
22522
|
+
if (cancelled(code))
|
|
22523
|
+
continue;
|
|
22524
|
+
const selfUrl = await Re({ message: "\u672C\u673A\u53EF\u8FBE\u5730\u5740 (\u53EF\u9009, \u7559\u7A7A\u5219\u5355\u5411)" });
|
|
22525
|
+
if (cancelled(selfUrl))
|
|
22526
|
+
continue;
|
|
22527
|
+
const s2 = vt();
|
|
22528
|
+
s2.start("\u914D\u5BF9\u4E2D\u2026");
|
|
22529
|
+
try {
|
|
22530
|
+
const r3 = await performPairing(db, node, url2, code, selfUrl || undefined);
|
|
22531
|
+
const sync = await syncPeer(db, r3.url);
|
|
22532
|
+
s2.stop(`\u2713 \u5DF2\u914D\u5BF9 ${r3.url} (node ${r3.node_id})`);
|
|
22533
|
+
Ce(syncLine(sync), "\u540C\u6B65\u7ED3\u679C");
|
|
22534
|
+
} catch (e3) {
|
|
22535
|
+
s2.stop(`\u2717 ${e3.message}`);
|
|
22536
|
+
}
|
|
22537
|
+
} else if (action === "code") {
|
|
22538
|
+
const code = generatePairingCode(db);
|
|
22539
|
+
Ce(`\u914D\u5BF9\u7801: ${code.code}
|
|
22540
|
+
\u6709\u6548\u81F3: ${iso2(code.exp)}`, "\u672C\u673A\u914D\u5BF9\u7801");
|
|
22541
|
+
} else if (action === "list") {
|
|
22542
|
+
const rows = listPeers(db).map(peerView);
|
|
22543
|
+
Ce(rows.length ? table(rows) : "(\u65E0\u8BBE\u5907)", "\u8BBE\u5907\u5217\u8868");
|
|
22544
|
+
} else if (action === "rm") {
|
|
22545
|
+
const peers = listPeers(db);
|
|
22546
|
+
if (!peers.length) {
|
|
22547
|
+
Ce("(\u65E0\u8BBE\u5907)");
|
|
22548
|
+
continue;
|
|
22549
|
+
}
|
|
22550
|
+
const url2 = await Ee({ message: "\u9009\u62E9\u8981\u79FB\u9664\u7684\u8BBE\u5907", options: peerChoices(peers) });
|
|
22551
|
+
if (cancelled(url2))
|
|
22552
|
+
continue;
|
|
22553
|
+
Ce(removePeer(db, url2) ? `\u2713 \u5DF2\u79FB\u9664 ${url2}` : "\u672A\u627E\u5230\u8BE5\u8BBE\u5907");
|
|
22554
|
+
} else if (action === "toggle") {
|
|
22555
|
+
const peers = listPeers(db);
|
|
22556
|
+
if (!peers.length) {
|
|
22557
|
+
Ce("(\u65E0\u8BBE\u5907)");
|
|
22558
|
+
continue;
|
|
22559
|
+
}
|
|
22560
|
+
const url2 = await Ee({ message: "\u9009\u62E9\u8BBE\u5907", options: peerChoices(peers) });
|
|
22561
|
+
if (cancelled(url2))
|
|
22562
|
+
continue;
|
|
22563
|
+
const cur = peers.find((x4) => x4.url === url2);
|
|
22564
|
+
const en = await le2({ message: "\u542F\u7528\u8BE5\u8BBE\u5907?", initialValue: cur ? !!cur.enabled : true });
|
|
22565
|
+
if (cancelled(en))
|
|
22566
|
+
continue;
|
|
22567
|
+
setPeerEnabled(db, url2, en);
|
|
22568
|
+
Ce(`\u2713 ${en ? "\u5DF2\u542F\u7528" : "\u5DF2\u7981\u7528"} ${url2}`);
|
|
22569
|
+
} else if (action === "sync") {
|
|
22570
|
+
const s2 = vt();
|
|
22571
|
+
s2.start("\u540C\u6B65\u4E2D\u2026");
|
|
22572
|
+
const r3 = await syncAllPeers(db);
|
|
22573
|
+
s2.stop("\u540C\u6B65\u5B8C\u6210");
|
|
22574
|
+
Ce(r3.length ? r3.map(syncLine).join(`
|
|
22575
|
+
`) : "(\u65E0\u542F\u7528\u7684\u8BBE\u5907)", "\u540C\u6B65\u7ED3\u679C");
|
|
22576
|
+
}
|
|
22577
|
+
} catch (e3) {
|
|
22578
|
+
Ce(`\u2717 ${e3.message}`, "\u9519\u8BEF");
|
|
22579
|
+
}
|
|
22580
|
+
}
|
|
22581
|
+
}
|
|
22582
|
+
async function grantWizard(db) {
|
|
22583
|
+
for (;; ) {
|
|
22584
|
+
const grants = listGrants(db);
|
|
22585
|
+
Ce(grants.length ? table(grants.map(grantView)) : "(\u65E0\u5DF2\u7B7E\u53D1\u51ED\u636E)", "\u5DF2\u7B7E\u53D1\u51ED\u636E");
|
|
22586
|
+
if (!grants.length)
|
|
22587
|
+
return;
|
|
22588
|
+
const token = await Ee({
|
|
22589
|
+
message: "\u9009\u62E9\u8981\u540A\u9500\u7684\u51ED\u636E",
|
|
22590
|
+
options: [...grantChoices(grants), { value: "__back__", label: "\u8FD4\u56DE" }]
|
|
22591
|
+
});
|
|
22592
|
+
if (cancelled(token) || token === "__back__")
|
|
22593
|
+
return;
|
|
22594
|
+
const ok = await le2({ message: `\u786E\u8BA4\u540A\u9500 ${maskToken(token)}?`, initialValue: false });
|
|
22595
|
+
if (cancelled(ok) || !ok)
|
|
22596
|
+
continue;
|
|
22597
|
+
const n2 = revokeGrant(db, token);
|
|
22598
|
+
Ce(n2 > 0 ? `\u2713 \u5DF2\u540A\u9500 ${n2} \u6761` : "\u65E0\u5339\u914D\u51ED\u636E");
|
|
22599
|
+
}
|
|
22600
|
+
}
|
|
22601
|
+
async function wizard(db) {
|
|
22602
|
+
ye2("Metahub \u914D\u7F6E");
|
|
22603
|
+
for (;; ) {
|
|
22604
|
+
const choice = await Ee({
|
|
22605
|
+
message: "\u9009\u62E9\u64CD\u4F5C",
|
|
22606
|
+
options: [
|
|
22607
|
+
{ value: "server", label: "\u670D\u52A1\u5668\u8BBE\u7F6E", hint: "host / port / \u540C\u6B65\u95F4\u9694 / auto-sync" },
|
|
22608
|
+
{ value: "peers", label: "\u540C\u6B65\u8BBE\u5907", hint: "\u914D\u5BF9 / \u5217\u51FA / \u79FB\u9664 / \u540C\u6B65" },
|
|
22609
|
+
{ value: "grants", label: "\u5DF2\u7B7E\u53D1\u51ED\u636E", hint: "\u5217\u51FA / \u540A\u9500" },
|
|
22610
|
+
{ value: "exit", label: "\u9000\u51FA" }
|
|
22611
|
+
]
|
|
22612
|
+
});
|
|
22613
|
+
if (cancelled(choice) || choice === "exit") {
|
|
22614
|
+
fe2("\u518D\u89C1");
|
|
22615
|
+
return;
|
|
22616
|
+
}
|
|
22617
|
+
if (choice === "server")
|
|
22618
|
+
await serverWizard(db);
|
|
22619
|
+
else if (choice === "peers")
|
|
22620
|
+
await peerWizard(db);
|
|
22621
|
+
else if (choice === "grants")
|
|
22622
|
+
await grantWizard(db);
|
|
22623
|
+
}
|
|
22624
|
+
}
|
|
22625
|
+
var config_default = defineCommand({
|
|
22626
|
+
meta: {
|
|
22627
|
+
name: "config",
|
|
22628
|
+
description: "Configure server + sync devices. Run with no args for an interactive wizard, or use flags directly (e.g. `config --port 7777`, `config peer add --url <url> --code <code>`)."
|
|
22629
|
+
},
|
|
22630
|
+
args: {
|
|
22631
|
+
section: { type: "positional", required: false, description: "show | set | peer | grant (omit for wizard)" },
|
|
22632
|
+
action: { type: "positional", required: false, description: "peer: add|code|list|rm|enable|disable|sync \xB7 grant: list|revoke" },
|
|
22633
|
+
host: { type: "string", description: "Bind address" },
|
|
22634
|
+
port: { type: "string", description: "Server port" },
|
|
22635
|
+
"sync-interval": { type: "string", description: "Auto-sync interval (e.g. 30s, 5m)" },
|
|
22636
|
+
"auto-sync": { type: "string", description: "Enable auto-sync timer (true/false)" },
|
|
22637
|
+
url: { type: "string", description: "Peer URL (peer add/rm/enable/disable/sync)" },
|
|
22638
|
+
code: { type: "string", description: "One-time pairing code (peer add)" },
|
|
22639
|
+
"self-url": { type: "string", description: "This device's reachable URL (peer add, optional)" },
|
|
22640
|
+
token: { type: "string", description: "Issued credential token or prefix (grant revoke)" }
|
|
22641
|
+
},
|
|
22642
|
+
run: guard(async (args) => {
|
|
22643
|
+
const db = openMetahub();
|
|
22644
|
+
const section = typeof args.section === "string" ? args.section : undefined;
|
|
22645
|
+
const hasSettingFlag = args.host != null || args.port != null || args["sync-interval"] != null || args["auto-sync"] != null;
|
|
22646
|
+
if (!section) {
|
|
22647
|
+
if (hasSettingFlag)
|
|
22648
|
+
return applySet(db, args);
|
|
22649
|
+
if (isTTY2())
|
|
22650
|
+
return wizard(db);
|
|
22651
|
+
return showConfig(db);
|
|
22652
|
+
}
|
|
22653
|
+
if (section === "show")
|
|
22654
|
+
return showConfig(db);
|
|
22655
|
+
if (section === "set")
|
|
22656
|
+
return applySet(db, args);
|
|
22657
|
+
if (section === "peer")
|
|
22658
|
+
return peerDispatch(db, args.action ?? "list", args);
|
|
22659
|
+
if (section === "grant")
|
|
22660
|
+
return grantDispatch(db, args.action ?? "list", args);
|
|
22661
|
+
throw new Error(`unknown config section '${section}' (show | set | peer | grant)`);
|
|
22662
|
+
})
|
|
22663
|
+
});
|
|
22664
|
+
|
|
22665
|
+
// src/core/sync/server.ts
|
|
22666
|
+
init_node();
|
|
22667
|
+
|
|
22668
|
+
// src/core/sync/routes.ts
|
|
22669
|
+
init_crdt();
|
|
22670
|
+
|
|
22671
|
+
// src/core/sync/webui-routes.ts
|
|
22672
|
+
var DatabaseSchema = exports_external.object({
|
|
22673
|
+
id: exports_external.string(),
|
|
22674
|
+
name: exports_external.string(),
|
|
22675
|
+
icon: exports_external.string().nullable(),
|
|
22676
|
+
created_hlc: exports_external.string()
|
|
22677
|
+
});
|
|
22678
|
+
var PropertySchema = exports_external.object({
|
|
22679
|
+
id: exports_external.string(),
|
|
22680
|
+
database_id: exports_external.string(),
|
|
22681
|
+
name: exports_external.string(),
|
|
22682
|
+
type: exports_external.string(),
|
|
22683
|
+
config: exports_external.any().nullable(),
|
|
22684
|
+
position: exports_external.number()
|
|
22685
|
+
});
|
|
22686
|
+
var RecordSchema = exports_external.object({
|
|
22687
|
+
id: exports_external.string(),
|
|
22688
|
+
database_id: exports_external.string(),
|
|
22689
|
+
values: exports_external.record(exports_external.string(), exports_external.any())
|
|
22690
|
+
});
|
|
22691
|
+
var MoveRecordReq = exports_external.object({
|
|
22692
|
+
target: exports_external.string(),
|
|
22693
|
+
where: exports_external.enum(["before", "after"])
|
|
22694
|
+
});
|
|
22695
|
+
var DocumentSummarySchema = exports_external.object({
|
|
22696
|
+
id: exports_external.string(),
|
|
22697
|
+
title: exports_external.string(),
|
|
22698
|
+
database_id: exports_external.string().nullable(),
|
|
22699
|
+
parent_id: exports_external.string().nullable(),
|
|
22700
|
+
created_hlc: exports_external.string()
|
|
22701
|
+
});
|
|
22702
|
+
var DocumentSchema = DocumentSummarySchema.extend({ body: exports_external.string().nullable() });
|
|
22703
|
+
var SearchHitSchema = exports_external.object({
|
|
22704
|
+
type: exports_external.string(),
|
|
22705
|
+
id: exports_external.string(),
|
|
22706
|
+
database_id: exports_external.string().nullable(),
|
|
22707
|
+
title: exports_external.string().optional(),
|
|
22708
|
+
snippet: exports_external.string()
|
|
22709
|
+
});
|
|
22710
|
+
var OkSchema = exports_external.object({ ok: exports_external.boolean() });
|
|
22711
|
+
var CreateDatabaseReq = exports_external.object({ name: exports_external.string(), icon: exports_external.string().optional() });
|
|
22712
|
+
var UpdateDatabaseReq = exports_external.object({
|
|
22713
|
+
name: exports_external.string().optional(),
|
|
22714
|
+
icon: exports_external.string().nullable().optional()
|
|
22715
|
+
});
|
|
22716
|
+
var CreatePropertyReq = exports_external.object({
|
|
22717
|
+
db: exports_external.string(),
|
|
22718
|
+
name: exports_external.string(),
|
|
22719
|
+
type: exports_external.string(),
|
|
22720
|
+
config: exports_external.any().optional()
|
|
22721
|
+
});
|
|
22722
|
+
var UpdatePropertyReq = exports_external.object({
|
|
22723
|
+
name: exports_external.string().optional(),
|
|
22724
|
+
type: exports_external.string().optional(),
|
|
22725
|
+
config: exports_external.any().optional(),
|
|
22726
|
+
position: exports_external.number().optional()
|
|
22727
|
+
});
|
|
22728
|
+
var SetWidthReq = exports_external.object({ width: exports_external.number() });
|
|
22729
|
+
var RecordValuesReq = exports_external.record(exports_external.string(), exports_external.any());
|
|
22730
|
+
var CreateDocumentReq = exports_external.object({
|
|
22731
|
+
title: exports_external.string(),
|
|
22732
|
+
body: exports_external.string().optional(),
|
|
22733
|
+
database_id: exports_external.string().optional(),
|
|
22734
|
+
parent_id: exports_external.string().optional()
|
|
22735
|
+
});
|
|
22736
|
+
var UpdateDocumentReq = exports_external.object({
|
|
22737
|
+
title: exports_external.string().optional(),
|
|
22738
|
+
body: exports_external.string().optional(),
|
|
22739
|
+
parent_id: exports_external.string().nullable().optional()
|
|
22740
|
+
});
|
|
22741
|
+
function need(req, key) {
|
|
22742
|
+
const v3 = new URL(req.url).searchParams.get(key);
|
|
22743
|
+
if (!v3)
|
|
22744
|
+
throw new Error(`missing query param: ${key}`);
|
|
22745
|
+
return v3;
|
|
22746
|
+
}
|
|
22747
|
+
function opt(req, key) {
|
|
22748
|
+
return new URL(req.url).searchParams.get(key) ?? undefined;
|
|
20144
22749
|
}
|
|
20145
22750
|
function handle(fn) {
|
|
20146
22751
|
return async (req, ctx) => {
|
|
@@ -20149,12 +22754,20 @@ function handle(fn) {
|
|
|
20149
22754
|
if (out instanceof Response)
|
|
20150
22755
|
return out;
|
|
20151
22756
|
return Response.json(out ?? null);
|
|
20152
|
-
} catch (
|
|
20153
|
-
return Response.json({ error:
|
|
22757
|
+
} catch (e3) {
|
|
22758
|
+
return Response.json({ error: e3.message }, { status: 400 });
|
|
20154
22759
|
}
|
|
20155
22760
|
};
|
|
20156
22761
|
}
|
|
22762
|
+
var VersionSchema = exports_external.object({ version: exports_external.string() });
|
|
20157
22763
|
var webuiRoutes = [
|
|
22764
|
+
{
|
|
22765
|
+
method: "GET",
|
|
22766
|
+
path: "/api/version",
|
|
22767
|
+
summary: "Version of the running core (sidecar). Used by the desktop app's settings footer.",
|
|
22768
|
+
response: VersionSchema,
|
|
22769
|
+
handler: () => Response.json({ version: package_default.version })
|
|
22770
|
+
},
|
|
20158
22771
|
{
|
|
20159
22772
|
method: "GET",
|
|
20160
22773
|
path: "/api/databases",
|
|
@@ -20304,9 +22917,9 @@ var webuiRoutes = [
|
|
|
20304
22917
|
{
|
|
20305
22918
|
method: "GET",
|
|
20306
22919
|
path: "/api/documents",
|
|
20307
|
-
summary: "List documents. Optional query: ?db=<id>",
|
|
22920
|
+
summary: "List documents. Optional query: ?db=<id> or ?parent=<id>",
|
|
20308
22921
|
response: exports_external.array(DocumentSummarySchema),
|
|
20309
|
-
handler: handle((req, { db }) => listDocuments(db, { database_id: opt(req, "db") }))
|
|
22922
|
+
handler: handle((req, { db }) => listDocuments(db, { database_id: opt(req, "db"), parent_id: opt(req, "parent") }))
|
|
20310
22923
|
},
|
|
20311
22924
|
{
|
|
20312
22925
|
method: "POST",
|
|
@@ -20375,17 +22988,17 @@ var SiteFileSchema = exports_external.object({
|
|
|
20375
22988
|
encoding: exports_external.string()
|
|
20376
22989
|
});
|
|
20377
22990
|
function need2(req, key) {
|
|
20378
|
-
const
|
|
20379
|
-
if (!
|
|
22991
|
+
const v3 = new URL(req.url).searchParams.get(key);
|
|
22992
|
+
if (!v3)
|
|
20380
22993
|
throw new Error(`missing query param: ${key}`);
|
|
20381
|
-
return
|
|
22994
|
+
return v3;
|
|
20382
22995
|
}
|
|
20383
22996
|
function handle2(fn) {
|
|
20384
22997
|
return async (req, ctx) => {
|
|
20385
22998
|
try {
|
|
20386
22999
|
return Response.json(await fn(req, ctx) ?? null);
|
|
20387
|
-
} catch (
|
|
20388
|
-
return Response.json({ error:
|
|
23000
|
+
} catch (e3) {
|
|
23001
|
+
return Response.json({ error: e3.message }, { status: 400 });
|
|
20389
23002
|
}
|
|
20390
23003
|
};
|
|
20391
23004
|
}
|
|
@@ -20406,6 +23019,146 @@ var sitesRoutes = [
|
|
|
20406
23019
|
}
|
|
20407
23020
|
];
|
|
20408
23021
|
|
|
23022
|
+
// src/core/sync/peers-routes.ts
|
|
23023
|
+
var PeerSchema = exports_external.object({
|
|
23024
|
+
url: exports_external.string(),
|
|
23025
|
+
pull_cursor: exports_external.number(),
|
|
23026
|
+
push_cursor: exports_external.number(),
|
|
23027
|
+
token: exports_external.string().nullable(),
|
|
23028
|
+
label: exports_external.string().nullable(),
|
|
23029
|
+
node_id: exports_external.string().nullable(),
|
|
23030
|
+
enabled: exports_external.number(),
|
|
23031
|
+
last_sync_at: exports_external.number().nullable(),
|
|
23032
|
+
last_status: exports_external.string().nullable(),
|
|
23033
|
+
last_error: exports_external.string().nullable()
|
|
23034
|
+
});
|
|
23035
|
+
var PairingCodeSchema = exports_external.object({ code: exports_external.string(), exp: exports_external.number() });
|
|
23036
|
+
var AddPeerReq = exports_external.object({
|
|
23037
|
+
url: exports_external.string(),
|
|
23038
|
+
code: exports_external.string(),
|
|
23039
|
+
self_url: exports_external.string().optional()
|
|
23040
|
+
});
|
|
23041
|
+
var AddPeerRes = exports_external.object({ node_id: exports_external.string(), url: exports_external.string() });
|
|
23042
|
+
var UpdatePeerReq = exports_external.object({
|
|
23043
|
+
enabled: exports_external.boolean().optional(),
|
|
23044
|
+
label: exports_external.string().optional()
|
|
23045
|
+
});
|
|
23046
|
+
var PeerSyncRes = exports_external.object({
|
|
23047
|
+
url: exports_external.string(),
|
|
23048
|
+
ok: exports_external.boolean(),
|
|
23049
|
+
pushed: exports_external.number().optional(),
|
|
23050
|
+
pulled: exports_external.number().optional(),
|
|
23051
|
+
error: exports_external.string().optional()
|
|
23052
|
+
});
|
|
23053
|
+
var GrantSchema = exports_external.object({
|
|
23054
|
+
token: exports_external.string(),
|
|
23055
|
+
peer_url: exports_external.string().nullable(),
|
|
23056
|
+
node_id: exports_external.string().nullable(),
|
|
23057
|
+
created_at: exports_external.number().nullable()
|
|
23058
|
+
});
|
|
23059
|
+
var RevokeRes = exports_external.object({ revoked: exports_external.number() });
|
|
23060
|
+
var OkSchema2 = exports_external.object({ ok: exports_external.boolean() });
|
|
23061
|
+
function need3(req, key) {
|
|
23062
|
+
const v3 = new URL(req.url).searchParams.get(key);
|
|
23063
|
+
if (!v3)
|
|
23064
|
+
throw new Error(`missing query param: ${key}`);
|
|
23065
|
+
return v3;
|
|
23066
|
+
}
|
|
23067
|
+
function handle3(fn) {
|
|
23068
|
+
return async (req, ctx) => {
|
|
23069
|
+
try {
|
|
23070
|
+
const out = await fn(req, ctx);
|
|
23071
|
+
if (out instanceof Response)
|
|
23072
|
+
return out;
|
|
23073
|
+
return Response.json(out ?? null);
|
|
23074
|
+
} catch (e3) {
|
|
23075
|
+
return Response.json({ error: e3.message }, { status: 400 });
|
|
23076
|
+
}
|
|
23077
|
+
};
|
|
23078
|
+
}
|
|
23079
|
+
var peersRoutes = [
|
|
23080
|
+
{
|
|
23081
|
+
method: "POST",
|
|
23082
|
+
path: PAIR_PATH,
|
|
23083
|
+
summary: "Pairing handshake: redeem a one-time code and exchange credentials",
|
|
23084
|
+
request: PairRequestSchema,
|
|
23085
|
+
response: PairResponseSchema,
|
|
23086
|
+
handler: handle3(async (req, { db, node }) => {
|
|
23087
|
+
const body = await req.json();
|
|
23088
|
+
return handlePairRequest(db, node, body);
|
|
23089
|
+
})
|
|
23090
|
+
},
|
|
23091
|
+
{
|
|
23092
|
+
method: "POST",
|
|
23093
|
+
path: "/api/pair/new",
|
|
23094
|
+
summary: "Mint a one-time pairing code for another device to redeem",
|
|
23095
|
+
response: PairingCodeSchema,
|
|
23096
|
+
handler: handle3((_req, { db }) => generatePairingCode(db))
|
|
23097
|
+
},
|
|
23098
|
+
{
|
|
23099
|
+
method: "POST",
|
|
23100
|
+
path: "/api/peers/pair",
|
|
23101
|
+
summary: "Pair this server with a remote one using its one-time code",
|
|
23102
|
+
request: AddPeerReq,
|
|
23103
|
+
response: AddPeerRes,
|
|
23104
|
+
handler: handle3(async (req, { db, node }) => {
|
|
23105
|
+
const body = await req.json();
|
|
23106
|
+
return performPairing(db, node, body.url, body.code, body.self_url);
|
|
23107
|
+
})
|
|
23108
|
+
},
|
|
23109
|
+
{
|
|
23110
|
+
method: "GET",
|
|
23111
|
+
path: "/api/peers",
|
|
23112
|
+
summary: "List configured sync peers with status",
|
|
23113
|
+
response: exports_external.array(PeerSchema),
|
|
23114
|
+
handler: handle3((_req, { db }) => listPeers(db))
|
|
23115
|
+
},
|
|
23116
|
+
{
|
|
23117
|
+
method: "PATCH",
|
|
23118
|
+
path: "/api/peer",
|
|
23119
|
+
summary: "Update a peer (enable/disable or label). Query: ?url=<url>",
|
|
23120
|
+
request: UpdatePeerReq,
|
|
23121
|
+
response: OkSchema2,
|
|
23122
|
+
handler: handle3(async (req, { db }) => {
|
|
23123
|
+
const url2 = need3(req, "url");
|
|
23124
|
+
const body = await req.json();
|
|
23125
|
+
if (body.enabled != null)
|
|
23126
|
+
setPeerEnabled(db, url2, body.enabled);
|
|
23127
|
+
if (body.label != null)
|
|
23128
|
+
setPeerLabel(db, url2, body.label);
|
|
23129
|
+
return { ok: true };
|
|
23130
|
+
})
|
|
23131
|
+
},
|
|
23132
|
+
{
|
|
23133
|
+
method: "DELETE",
|
|
23134
|
+
path: "/api/peer",
|
|
23135
|
+
summary: "Remove a sync peer. Query: ?url=<url>",
|
|
23136
|
+
response: OkSchema2,
|
|
23137
|
+
handler: handle3((req, { db }) => ({ ok: removePeer(db, need3(req, "url")) }))
|
|
23138
|
+
},
|
|
23139
|
+
{
|
|
23140
|
+
method: "POST",
|
|
23141
|
+
path: "/api/peer/sync",
|
|
23142
|
+
summary: "Manually sync once with a peer. Query: ?url=<url>",
|
|
23143
|
+
response: PeerSyncRes,
|
|
23144
|
+
handler: handle3((req, { db }) => syncPeer(db, need3(req, "url")))
|
|
23145
|
+
},
|
|
23146
|
+
{
|
|
23147
|
+
method: "GET",
|
|
23148
|
+
path: "/api/grants",
|
|
23149
|
+
summary: "List credentials this server issued and accepts on /sync (inbound)",
|
|
23150
|
+
response: exports_external.array(GrantSchema),
|
|
23151
|
+
handler: handle3((_req, { db }) => listGrants(db))
|
|
23152
|
+
},
|
|
23153
|
+
{
|
|
23154
|
+
method: "DELETE",
|
|
23155
|
+
path: "/api/grant",
|
|
23156
|
+
summary: "Revoke an issued credential by token or prefix. Query: ?token=<token>",
|
|
23157
|
+
response: RevokeRes,
|
|
23158
|
+
handler: handle3((req, { db }) => ({ revoked: revokeGrant(db, need3(req, "token")) }))
|
|
23159
|
+
}
|
|
23160
|
+
];
|
|
23161
|
+
|
|
20409
23162
|
// src/core/sync/routes.ts
|
|
20410
23163
|
var syncRoutes = [
|
|
20411
23164
|
{
|
|
@@ -20431,7 +23184,7 @@ var syncRoutes = [
|
|
|
20431
23184
|
}
|
|
20432
23185
|
}
|
|
20433
23186
|
];
|
|
20434
|
-
var routes = [...syncRoutes, ...webuiRoutes, ...sitesRoutes];
|
|
23187
|
+
var routes = [...syncRoutes, ...webuiRoutes, ...sitesRoutes, ...peersRoutes];
|
|
20435
23188
|
|
|
20436
23189
|
// src/core/sync/openapi.ts
|
|
20437
23190
|
function buildOpenApi(version2) {
|
|
@@ -20468,9 +23221,9 @@ function cookieToken(req) {
|
|
|
20468
23221
|
if (!header)
|
|
20469
23222
|
return null;
|
|
20470
23223
|
for (const part of header.split(";")) {
|
|
20471
|
-
const [
|
|
20472
|
-
if (
|
|
20473
|
-
return decodeURIComponent(
|
|
23224
|
+
const [k3, ...v3] = part.trim().split("=");
|
|
23225
|
+
if (k3 === COOKIE)
|
|
23226
|
+
return decodeURIComponent(v3.join("="));
|
|
20474
23227
|
}
|
|
20475
23228
|
return null;
|
|
20476
23229
|
}
|
|
@@ -20500,6 +23253,17 @@ function hasValidToken(req, url2, cfg) {
|
|
|
20500
23253
|
return true;
|
|
20501
23254
|
return extractToken(req, url2) === cur.token;
|
|
20502
23255
|
}
|
|
23256
|
+
function acceptsSyncToken(req, url2, cfg, db) {
|
|
23257
|
+
if (!authActive(cfg))
|
|
23258
|
+
return true;
|
|
23259
|
+
const presented = extractToken(req, url2);
|
|
23260
|
+
if (!presented)
|
|
23261
|
+
return false;
|
|
23262
|
+
const cur = activeToken(cfg);
|
|
23263
|
+
if (cur && presented === cur.token)
|
|
23264
|
+
return true;
|
|
23265
|
+
return isAcceptedGrant(db, presented);
|
|
23266
|
+
}
|
|
20503
23267
|
function renewToken(req, url2, cfg) {
|
|
20504
23268
|
if (cfg.debug || cfg.staticToken != null || !cfg.db)
|
|
20505
23269
|
return null;
|
|
@@ -20615,8 +23379,8 @@ function injectShim(html) {
|
|
|
20615
23379
|
async function withShim(res, cfg) {
|
|
20616
23380
|
if (!authActive(cfg))
|
|
20617
23381
|
return res;
|
|
20618
|
-
const
|
|
20619
|
-
if (!
|
|
23382
|
+
const ct3 = res.headers.get("content-type") ?? "";
|
|
23383
|
+
if (!ct3.includes("text/html"))
|
|
20620
23384
|
return res;
|
|
20621
23385
|
const html = injectShim(await res.text());
|
|
20622
23386
|
const headers = new Headers(res.headers);
|
|
@@ -20627,6 +23391,16 @@ function unauthorized() {
|
|
|
20627
23391
|
}
|
|
20628
23392
|
|
|
20629
23393
|
// src/core/sync/server.ts
|
|
23394
|
+
class PortInUseError extends Error {
|
|
23395
|
+
port;
|
|
23396
|
+
host;
|
|
23397
|
+
constructor(port, host) {
|
|
23398
|
+
super(`port ${port} is already in use \u2014 another mh server may be running. ` + `Stop it, or start on a free port with --port <n>.`);
|
|
23399
|
+
this.port = port;
|
|
23400
|
+
this.host = host;
|
|
23401
|
+
this.name = "PortInUseError";
|
|
23402
|
+
}
|
|
23403
|
+
}
|
|
20630
23404
|
function scalarHtml(specUrl) {
|
|
20631
23405
|
return `<!doctype html><html><head>
|
|
20632
23406
|
<meta charset="utf-8"><title>Metahub API</title>
|
|
@@ -20639,6 +23413,11 @@ function startServer(opts = {}) {
|
|
|
20639
23413
|
const db = openMetahub();
|
|
20640
23414
|
const node = getNodeId(db);
|
|
20641
23415
|
const ctx = { db, node };
|
|
23416
|
+
const cfg = getServerConfig(db);
|
|
23417
|
+
const port = opts.port ?? cfg.port;
|
|
23418
|
+
const host = opts.host ?? cfg.host;
|
|
23419
|
+
const syncIntervalMs = opts.syncIntervalMs ?? cfg.syncIntervalMs;
|
|
23420
|
+
const autoSync = opts.autoSync ?? cfg.autoSync;
|
|
20642
23421
|
const auth = opts.debug ? { debug: true, staticToken: null, db: null, ttlMs: 0, graceMs: 0 } : opts.token ? { debug: false, staticToken: opts.token, db: null, ttlMs: 0, graceMs: 0 } : {
|
|
20643
23422
|
debug: false,
|
|
20644
23423
|
staticToken: null,
|
|
@@ -20646,53 +23425,210 @@ function startServer(opts = {}) {
|
|
|
20646
23425
|
ttlMs: DEFAULT_TTL_MS,
|
|
20647
23426
|
graceMs: DEFAULT_GRACE_MS
|
|
20648
23427
|
};
|
|
20649
|
-
|
|
20650
|
-
|
|
20651
|
-
|
|
20652
|
-
|
|
20653
|
-
|
|
20654
|
-
|
|
20655
|
-
|
|
20656
|
-
|
|
20657
|
-
|
|
20658
|
-
|
|
20659
|
-
|
|
20660
|
-
|
|
20661
|
-
|
|
20662
|
-
|
|
20663
|
-
|
|
20664
|
-
|
|
20665
|
-
|
|
20666
|
-
|
|
20667
|
-
|
|
20668
|
-
|
|
20669
|
-
|
|
20670
|
-
|
|
20671
|
-
|
|
20672
|
-
|
|
20673
|
-
|
|
20674
|
-
|
|
20675
|
-
|
|
20676
|
-
|
|
20677
|
-
|
|
20678
|
-
|
|
20679
|
-
|
|
20680
|
-
|
|
20681
|
-
|
|
20682
|
-
|
|
20683
|
-
|
|
23428
|
+
let server;
|
|
23429
|
+
try {
|
|
23430
|
+
server = Bun.serve({
|
|
23431
|
+
port,
|
|
23432
|
+
hostname: host,
|
|
23433
|
+
async fetch(req) {
|
|
23434
|
+
const url2 = new URL(req.url);
|
|
23435
|
+
if (url2.pathname === SYNC_PATH) {
|
|
23436
|
+
if (!acceptsSyncToken(req, url2, auth, db))
|
|
23437
|
+
return unauthorized();
|
|
23438
|
+
} else {
|
|
23439
|
+
const exempt = url2.pathname === HEALTH_PATH || url2.pathname === RENEW_PATH || url2.pathname === PAIR_PATH;
|
|
23440
|
+
if (!exempt && !hasValidToken(req, url2, auth)) {
|
|
23441
|
+
return wantsHtml(req) ? new Response(unlockPage(), { headers: HTML_HEADERS }) : unauthorized();
|
|
23442
|
+
}
|
|
23443
|
+
}
|
|
23444
|
+
if (url2.pathname === RENEW_PATH) {
|
|
23445
|
+
const r3 = renewToken(req, url2, auth);
|
|
23446
|
+
return r3 ? Response.json(r3) : unauthorized();
|
|
23447
|
+
}
|
|
23448
|
+
if (req.method === "GET" && url2.pathname === "/docs.json") {
|
|
23449
|
+
return Response.json(buildOpenApi(package_default.version));
|
|
23450
|
+
}
|
|
23451
|
+
if (req.method === "GET" && url2.pathname === "/docs") {
|
|
23452
|
+
return withShim(new Response(scalarHtml("/docs.json"), { headers: HTML_HEADERS }), auth);
|
|
23453
|
+
}
|
|
23454
|
+
if (req.method === "GET" && (url2.pathname === "/" || url2.pathname === "/webui.js")) {
|
|
23455
|
+
const { serveWebui: serveWebui2 } = await Promise.resolve().then(() => (init_webui(), exports_webui));
|
|
23456
|
+
const res = await serveWebui2(req);
|
|
23457
|
+
if (res)
|
|
23458
|
+
return withShim(res, auth);
|
|
23459
|
+
}
|
|
23460
|
+
if (req.method === "GET" && url2.pathname.startsWith("/sites/")) {
|
|
23461
|
+
const { serveSite: serveSite2 } = await Promise.resolve().then(() => (init_sites_serve(), exports_sites_serve));
|
|
23462
|
+
const res = await serveSite2(req, ctx);
|
|
23463
|
+
if (res)
|
|
23464
|
+
return withShim(res, auth);
|
|
23465
|
+
}
|
|
23466
|
+
const route = routes.find((r3) => r3.method === req.method && r3.path === url2.pathname);
|
|
23467
|
+
if (route)
|
|
23468
|
+
return route.handler(req, ctx);
|
|
23469
|
+
return new Response("not found", { status: 404 });
|
|
23470
|
+
}
|
|
23471
|
+
});
|
|
23472
|
+
} catch (e3) {
|
|
23473
|
+
const code = e3?.code;
|
|
23474
|
+
if (code === "EADDRINUSE") {
|
|
23475
|
+
throw new PortInUseError(port, host);
|
|
20684
23476
|
}
|
|
20685
|
-
|
|
23477
|
+
if (code === "EACCES") {
|
|
23478
|
+
throw new Error(`permission denied binding ${host}:${port} \u2014 ports below 1024 need elevated privileges; pick a higher port with --port`);
|
|
23479
|
+
}
|
|
23480
|
+
throw e3;
|
|
23481
|
+
}
|
|
23482
|
+
let timer = null;
|
|
23483
|
+
if (autoSync && syncIntervalMs > 0) {
|
|
23484
|
+
timer = setInterval(() => {
|
|
23485
|
+
syncAllPeers(db).catch(() => {});
|
|
23486
|
+
}, syncIntervalMs);
|
|
23487
|
+
timer.unref?.();
|
|
23488
|
+
}
|
|
20686
23489
|
const active = activeToken(auth);
|
|
23490
|
+
const authMode = opts.debug ? "disabled" : opts.token ? "static" : "managed";
|
|
20687
23491
|
return {
|
|
20688
23492
|
server,
|
|
20689
23493
|
node,
|
|
20690
|
-
port: server.port ??
|
|
23494
|
+
port: server.port ?? port,
|
|
23495
|
+
host,
|
|
20691
23496
|
token: active?.token ?? null,
|
|
20692
|
-
exp: active?.exp ?? null
|
|
23497
|
+
exp: active?.exp ?? null,
|
|
23498
|
+
authMode,
|
|
23499
|
+
autoSync: Boolean(autoSync && syncIntervalMs > 0),
|
|
23500
|
+
syncIntervalMs,
|
|
23501
|
+
stop() {
|
|
23502
|
+
if (timer)
|
|
23503
|
+
clearInterval(timer);
|
|
23504
|
+
server.stop();
|
|
23505
|
+
}
|
|
20693
23506
|
};
|
|
20694
23507
|
}
|
|
20695
23508
|
|
|
23509
|
+
// src/cli/banner.ts
|
|
23510
|
+
var ANSI = /\x1b\[[0-9;]*m/g;
|
|
23511
|
+
function supportsColor() {
|
|
23512
|
+
return Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
|
|
23513
|
+
}
|
|
23514
|
+
function visibleWidth(s2) {
|
|
23515
|
+
return s2.replace(ANSI, "").length;
|
|
23516
|
+
}
|
|
23517
|
+
var CODES = {
|
|
23518
|
+
reset: "\x1B[0m",
|
|
23519
|
+
dim: "\x1B[2m",
|
|
23520
|
+
bold: "\x1B[1m",
|
|
23521
|
+
green: "\x1B[32m",
|
|
23522
|
+
cyan: "\x1B[36m",
|
|
23523
|
+
yellow: "\x1B[33m",
|
|
23524
|
+
magenta: "\x1B[35m",
|
|
23525
|
+
red: "\x1B[31m"
|
|
23526
|
+
};
|
|
23527
|
+
function makePalette(on) {
|
|
23528
|
+
const wrap = (code) => (s2) => on ? `${code}${s2}${CODES.reset}` : s2;
|
|
23529
|
+
return {
|
|
23530
|
+
reset: (s2) => s2,
|
|
23531
|
+
dim: wrap(CODES.dim),
|
|
23532
|
+
bold: wrap(CODES.bold),
|
|
23533
|
+
green: wrap(CODES.green),
|
|
23534
|
+
cyan: wrap(CODES.cyan),
|
|
23535
|
+
yellow: wrap(CODES.yellow),
|
|
23536
|
+
magenta: wrap(CODES.magenta),
|
|
23537
|
+
red: wrap(CODES.red)
|
|
23538
|
+
};
|
|
23539
|
+
}
|
|
23540
|
+
var MONTHS = [
|
|
23541
|
+
"Jan",
|
|
23542
|
+
"Feb",
|
|
23543
|
+
"Mar",
|
|
23544
|
+
"Apr",
|
|
23545
|
+
"May",
|
|
23546
|
+
"Jun",
|
|
23547
|
+
"Jul",
|
|
23548
|
+
"Aug",
|
|
23549
|
+
"Sep",
|
|
23550
|
+
"Oct",
|
|
23551
|
+
"Nov",
|
|
23552
|
+
"Dec"
|
|
23553
|
+
];
|
|
23554
|
+
function humanizeExpiry(exp, now = Date.now()) {
|
|
23555
|
+
if (exp == null)
|
|
23556
|
+
return "";
|
|
23557
|
+
if (!Number.isFinite(exp))
|
|
23558
|
+
return "never expires";
|
|
23559
|
+
const ms = exp - now;
|
|
23560
|
+
if (ms <= 0)
|
|
23561
|
+
return "expired";
|
|
23562
|
+
const d2 = new Date(exp);
|
|
23563
|
+
const date5 = `${MONTHS[d2.getMonth()]} ${d2.getDate()}`;
|
|
23564
|
+
const mins = Math.round(ms / 60000);
|
|
23565
|
+
let rel;
|
|
23566
|
+
if (mins < 60)
|
|
23567
|
+
rel = `${mins}m`;
|
|
23568
|
+
else if (mins < 60 * 24)
|
|
23569
|
+
rel = `${Math.round(mins / 60)}h`;
|
|
23570
|
+
else
|
|
23571
|
+
rel = `${Math.round(mins / (60 * 24))} days`;
|
|
23572
|
+
return `expires in ${rel} \xB7 ${date5}`;
|
|
23573
|
+
}
|
|
23574
|
+
function formatSync(autoSync, ms) {
|
|
23575
|
+
if (!autoSync || ms <= 0)
|
|
23576
|
+
return "auto-sync off";
|
|
23577
|
+
const secs2 = Math.round(ms / 1000);
|
|
23578
|
+
const human = secs2 % 3600 === 0 ? `${secs2 / 3600}h` : secs2 % 60 === 0 ? `${secs2 / 60}m` : `${secs2}s`;
|
|
23579
|
+
return `every ${human}`;
|
|
23580
|
+
}
|
|
23581
|
+
function isLoopback(host) {
|
|
23582
|
+
return host === "127.0.0.1" || host === "::1" || host === "localhost" || host === "::";
|
|
23583
|
+
}
|
|
23584
|
+
function renderStartupBanner(info) {
|
|
23585
|
+
const on = info.color ?? supportsColor();
|
|
23586
|
+
const c3 = makePalette(on);
|
|
23587
|
+
const LABEL_W = 7;
|
|
23588
|
+
const label = (t2) => c3.dim(t2.padEnd(LABEL_W));
|
|
23589
|
+
const rows = [];
|
|
23590
|
+
const push = (plain, painted) => rows.push([plain, painted]);
|
|
23591
|
+
push(`\u25CF listening ${info.host}:${info.port}`, `${c3.green("\u25CF")} ${c3.dim("listening")} ${c3.bold(`${info.host}:${info.port}`)}`);
|
|
23592
|
+
if (!isLoopback(info.host)) {
|
|
23593
|
+
push("\u26A0 reachable on your network", c3.yellow("\u26A0 reachable on your network"));
|
|
23594
|
+
}
|
|
23595
|
+
push("", "");
|
|
23596
|
+
push(`${"WebUI".padEnd(LABEL_W)}${info.webuiUrl}`, `${label("WebUI")}${c3.cyan(info.webuiUrl)}`);
|
|
23597
|
+
push(`${"Docs".padEnd(LABEL_W)}${info.docsUrl}`, `${label("Docs")}${c3.cyan(info.docsUrl)}`);
|
|
23598
|
+
if (info.authMode === "disabled" || !info.token) {
|
|
23599
|
+
push(`${"Auth".padEnd(LABEL_W)}disabled (--debug)`, `${label("Auth")}${c3.red("disabled")} ${c3.dim("(--debug)")}`);
|
|
23600
|
+
} else {
|
|
23601
|
+
push(`${"Token".padEnd(LABEL_W)}${info.token}`, `${label("Token")}${c3.yellow(info.token)}`);
|
|
23602
|
+
const exp = humanizeExpiry(info.exp);
|
|
23603
|
+
if (exp)
|
|
23604
|
+
push(`${" ".repeat(LABEL_W)}${exp}`, `${" ".repeat(LABEL_W)}${c3.dim(exp)}`);
|
|
23605
|
+
}
|
|
23606
|
+
const sync = formatSync(info.autoSync, info.syncIntervalMs);
|
|
23607
|
+
const syncLabel = info.autoSync && info.syncIntervalMs > 0 ? `sync ${sync}` : sync;
|
|
23608
|
+
push(`${"Node".padEnd(LABEL_W)}${info.node} \xB7 ${syncLabel}`, `${label("Node")}${info.node} ${c3.dim("\xB7")} ${c3.dim(syncLabel)}`);
|
|
23609
|
+
const PAD = 2;
|
|
23610
|
+
const title = "metahub";
|
|
23611
|
+
const ver = `v${info.version}`;
|
|
23612
|
+
const footer = "Ctrl-C to stop";
|
|
23613
|
+
const contentW = Math.max(...rows.map(([plain]) => plain.length), title.length + ver.length + 6, footer.length + 4);
|
|
23614
|
+
const innerW = contentW + PAD * 2;
|
|
23615
|
+
const totalW = innerW + 2;
|
|
23616
|
+
const border = (lc, left, right, rc) => {
|
|
23617
|
+
const leftSeg = left ? `\u2500 ${left} ` : "\u2500";
|
|
23618
|
+
const rightSeg = right ? ` ${right} \u2500` : "\u2500";
|
|
23619
|
+
const dashes = totalW - 2 - visibleWidth(leftSeg) - visibleWidth(rightSeg);
|
|
23620
|
+
return `${lc}${leftSeg}${"\u2500".repeat(Math.max(0, dashes))}${rightSeg}${rc}`;
|
|
23621
|
+
};
|
|
23622
|
+
const top = border("\u256D", c3.bold(c3.magenta(title)), c3.dim(ver), "\u256E");
|
|
23623
|
+
const bottom = border("\u2570", c3.dim(footer), "", "\u256F");
|
|
23624
|
+
const body = rows.map(([plain, painted]) => {
|
|
23625
|
+
const fill = innerW - PAD * 2 - plain.length;
|
|
23626
|
+
return `\u2502${" ".repeat(PAD)}${painted}${" ".repeat(Math.max(0, fill) + PAD)}\u2502`;
|
|
23627
|
+
});
|
|
23628
|
+
return [top, ...body, bottom].join(`
|
|
23629
|
+
`);
|
|
23630
|
+
}
|
|
23631
|
+
|
|
20696
23632
|
// src/cli/help.ts
|
|
20697
23633
|
var ROOT_GUIDE = `metahub \u2014 local knowledge base for AI agents
|
|
20698
23634
|
Notion-like typed databases (tables + rows) and markdown documents, stored in
|
|
@@ -20886,7 +23822,7 @@ async function showUsage2(cmd, parent) {
|
|
|
20886
23822
|
|
|
20887
23823
|
EXAMPLES
|
|
20888
23824
|
|
|
20889
|
-
` + examples.map((
|
|
23825
|
+
` + examples.map((e3) => " " + e3).join(`
|
|
20890
23826
|
`);
|
|
20891
23827
|
}
|
|
20892
23828
|
console.log(usage + `
|
|
@@ -20905,7 +23841,9 @@ var main = defineCommand({
|
|
|
20905
23841
|
port: { type: "string", description: "Server port (with --server)", default: "7777" },
|
|
20906
23842
|
host: { type: "string", description: "Bind address (with --server)", default: "127.0.0.1" },
|
|
20907
23843
|
debug: { type: "boolean", description: "Disable auth on the server (with --server)" },
|
|
20908
|
-
token: { type: "string", description: "Server auth token; generated if omitted (with --server)" }
|
|
23844
|
+
token: { type: "string", description: "Server auth token; generated if omitted (with --server)" },
|
|
23845
|
+
"sync-interval": { type: "string", description: "Auto-sync interval, e.g. 30s/5m (with --server)" },
|
|
23846
|
+
"no-auto-sync": { type: "boolean", description: "Disable the auto-sync timer (with --server)" }
|
|
20909
23847
|
},
|
|
20910
23848
|
subCommands: {
|
|
20911
23849
|
init: init_default,
|
|
@@ -20923,7 +23861,10 @@ var main = defineCommand({
|
|
|
20923
23861
|
__complete: complete,
|
|
20924
23862
|
sync: sync_default,
|
|
20925
23863
|
snapshot: snapshot_default,
|
|
20926
|
-
restore: restore_default
|
|
23864
|
+
restore: restore_default,
|
|
23865
|
+
doctor: doctor_default,
|
|
23866
|
+
repair: repair_default,
|
|
23867
|
+
config: config_default
|
|
20927
23868
|
}
|
|
20928
23869
|
});
|
|
20929
23870
|
function flagValue(argv2, name) {
|
|
@@ -20933,29 +23874,52 @@ function flagValue(argv2, name) {
|
|
|
20933
23874
|
const a2 = argv2[i2];
|
|
20934
23875
|
return a2.includes("=") ? a2.slice(a2.indexOf("=") + 1) : argv2[i2 + 1];
|
|
20935
23876
|
}
|
|
20936
|
-
function parsePort(argv2) {
|
|
20937
|
-
return Number(flagValue(argv2, "port")) || 7777;
|
|
20938
|
-
}
|
|
20939
23877
|
var argv2 = process.argv.slice(2);
|
|
20940
23878
|
if (argv2.includes("--server")) {
|
|
20941
|
-
const
|
|
20942
|
-
|
|
20943
|
-
|
|
20944
|
-
|
|
20945
|
-
|
|
20946
|
-
|
|
20947
|
-
|
|
23879
|
+
const portFlag = flagValue(argv2, "port");
|
|
23880
|
+
const intervalFlag = flagValue(argv2, "sync-interval");
|
|
23881
|
+
let s2;
|
|
23882
|
+
try {
|
|
23883
|
+
s2 = startServer({
|
|
23884
|
+
port: portFlag != null ? Number(portFlag) : undefined,
|
|
23885
|
+
host: flagValue(argv2, "host"),
|
|
23886
|
+
debug: argv2.includes("--debug"),
|
|
23887
|
+
token: flagValue(argv2, "token") ?? process.env.METAHUB_TOKEN,
|
|
23888
|
+
syncIntervalMs: intervalFlag != null ? parseDuration(intervalFlag, 30000) : undefined,
|
|
23889
|
+
autoSync: argv2.includes("--no-auto-sync") ? false : undefined
|
|
23890
|
+
});
|
|
23891
|
+
} catch (e3) {
|
|
23892
|
+
fail(e3 instanceof Error ? e3.message : String(e3), e3 instanceof PortInUseError ? 98 : 1);
|
|
23893
|
+
}
|
|
23894
|
+
const webui = `http://localhost:${s2.port}`;
|
|
23895
|
+
const docs = `${webui}/docs`;
|
|
20948
23896
|
print({
|
|
20949
23897
|
server: "listening",
|
|
20950
23898
|
port: s2.port,
|
|
23899
|
+
host: s2.host,
|
|
20951
23900
|
nodeId: s2.node,
|
|
20952
23901
|
docs: `/docs`,
|
|
23902
|
+
webui,
|
|
23903
|
+
authMode: s2.authMode,
|
|
20953
23904
|
token: s2.token,
|
|
20954
|
-
exp: s2.exp != null && Number.isFinite(s2.exp) ? s2.exp : null
|
|
20955
|
-
|
|
20956
|
-
|
|
23905
|
+
exp: s2.exp != null && Number.isFinite(s2.exp) ? s2.exp : null,
|
|
23906
|
+
autoSync: s2.autoSync,
|
|
23907
|
+
syncIntervalMs: s2.syncIntervalMs
|
|
23908
|
+
}, () => renderStartupBanner({
|
|
23909
|
+
version: package_default.version,
|
|
23910
|
+
host: s2.host,
|
|
23911
|
+
port: s2.port,
|
|
23912
|
+
webuiUrl: webui,
|
|
23913
|
+
docsUrl: docs,
|
|
23914
|
+
authMode: s2.authMode,
|
|
23915
|
+
token: s2.token,
|
|
23916
|
+
exp: s2.exp,
|
|
23917
|
+
node: s2.node,
|
|
23918
|
+
autoSync: s2.autoSync,
|
|
23919
|
+
syncIntervalMs: s2.syncIntervalMs
|
|
23920
|
+
}));
|
|
20957
23921
|
} else {
|
|
20958
23922
|
runMain(main, { showUsage: showUsage2 });
|
|
20959
23923
|
}
|
|
20960
23924
|
|
|
20961
|
-
//# debugId=
|
|
23925
|
+
//# debugId=74D4BFCB8CE1AB2064756E2164756E21
|