autho 3.0.4 → 3.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/autho.js +327 -37
- package/package.json +1 -1
package/dist/autho.js
CHANGED
|
@@ -590,7 +590,7 @@ var package_default;
|
|
|
590
590
|
var init_package = __esm(() => {
|
|
591
591
|
package_default = {
|
|
592
592
|
name: "autho",
|
|
593
|
-
version: "3.0.
|
|
593
|
+
version: "3.0.5",
|
|
594
594
|
description: "Local-first secret manager for humans and coding agents, rebuilt on Bun",
|
|
595
595
|
type: "module",
|
|
596
596
|
bin: {
|
|
@@ -4105,7 +4105,7 @@ var init_palette = __esm(() => {
|
|
|
4105
4105
|
// apps/cli/src/tui/settings.tsx
|
|
4106
4106
|
import { existsSync as existsSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
4107
4107
|
import { homedir as homedir2 } from "os";
|
|
4108
|
-
import { dirname as dirname3, join as join3, resolve as
|
|
4108
|
+
import { dirname as dirname3, join as join3, resolve as resolve5 } from "path";
|
|
4109
4109
|
import { useKeyboard as useKeyboard4, useTerminalDimensions as useTerminalDimensions3 } from "@opentui/react";
|
|
4110
4110
|
import { useCallback, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef5, useState as useState4 } from "react";
|
|
4111
4111
|
import { jsx as jsx4, jsxs as jsxs4, Fragment } from "@opentui/react/jsx-runtime";
|
|
@@ -4372,7 +4372,7 @@ function SettingsScreen({ vaultPath, setHints, onBack, passkeyRunner = runPasske
|
|
|
4372
4372
|
return;
|
|
4373
4373
|
case "vaultDir": {
|
|
4374
4374
|
const config = { ...loadConfig() };
|
|
4375
|
-
const target = first.trim() ?
|
|
4375
|
+
const target = first.trim() ? resolve5(first.trim()).replace(/\\/g, "/") : null;
|
|
4376
4376
|
if (target)
|
|
4377
4377
|
config.vaultDir = target;
|
|
4378
4378
|
else
|
|
@@ -4440,7 +4440,7 @@ function SettingsScreen({ vaultPath, setHints, onBack, passkeyRunner = runPasske
|
|
|
4440
4440
|
registerPasskey(first.trim());
|
|
4441
4441
|
return;
|
|
4442
4442
|
case "recoveryPath": {
|
|
4443
|
-
const target =
|
|
4443
|
+
const target = resolve5(first.trim() || join3(homedir2(), "autho-recovery.txt"));
|
|
4444
4444
|
if (existsSync5(target)) {
|
|
4445
4445
|
setError("That file already exists \u2014 pick another name");
|
|
4446
4446
|
return;
|
|
@@ -6238,10 +6238,9 @@ var init_tui = __esm(() => {
|
|
|
6238
6238
|
init_clipboard();
|
|
6239
6239
|
init_passkey();
|
|
6240
6240
|
init_package();
|
|
6241
|
-
import { spawn as spawn3 } from "child_process";
|
|
6242
6241
|
import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
6243
6242
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
6244
|
-
import { basename as basename4, isAbsolute, resolve as
|
|
6243
|
+
import { basename as basename4, isAbsolute, resolve as resolve6 } from "path";
|
|
6245
6244
|
|
|
6246
6245
|
// apps/cli/src/password.ts
|
|
6247
6246
|
import { createInterface } from "readline/promises";
|
|
@@ -6683,6 +6682,321 @@ async function daemonStop(options) {
|
|
|
6683
6682
|
// apps/cli/src/index.ts
|
|
6684
6683
|
init_src3();
|
|
6685
6684
|
init_os_secrets();
|
|
6685
|
+
|
|
6686
|
+
// apps/web/src/server.ts
|
|
6687
|
+
init_src();
|
|
6688
|
+
init_src3();
|
|
6689
|
+
init_os_secrets();
|
|
6690
|
+
import { randomBytes as randomBytes6 } from "crypto";
|
|
6691
|
+
import { resolve as resolve4 } from "path";
|
|
6692
|
+
function json2(data, status = 200, headers) {
|
|
6693
|
+
return new Response(JSON.stringify(data, null, 2), {
|
|
6694
|
+
headers: {
|
|
6695
|
+
"content-type": "application/json; charset=utf-8",
|
|
6696
|
+
...headers ?? {}
|
|
6697
|
+
},
|
|
6698
|
+
status
|
|
6699
|
+
});
|
|
6700
|
+
}
|
|
6701
|
+
function html(body) {
|
|
6702
|
+
return new Response(body, {
|
|
6703
|
+
headers: {
|
|
6704
|
+
"content-type": "text/html; charset=utf-8"
|
|
6705
|
+
}
|
|
6706
|
+
});
|
|
6707
|
+
}
|
|
6708
|
+
function parseCookies(request) {
|
|
6709
|
+
const cookieHeader = request.headers.get("cookie") ?? "";
|
|
6710
|
+
return Object.fromEntries(cookieHeader.split(";").map((part) => part.trim()).filter(Boolean).map((part) => {
|
|
6711
|
+
const index = part.indexOf("=");
|
|
6712
|
+
return [part.slice(0, index), decodeURIComponent(part.slice(index + 1))];
|
|
6713
|
+
}));
|
|
6714
|
+
}
|
|
6715
|
+
async function openSession(vaultPath, state) {
|
|
6716
|
+
return new VaultSession(new AuthoDatabase(vaultPath), state.rootKey, {
|
|
6717
|
+
sealBound: state.sealBound,
|
|
6718
|
+
sealRecord: await loadSealRecord(vaultPath)
|
|
6719
|
+
});
|
|
6720
|
+
}
|
|
6721
|
+
function requireSession(request, sessions) {
|
|
6722
|
+
const cookies = parseCookies(request);
|
|
6723
|
+
const sessionId = cookies.autho_session;
|
|
6724
|
+
if (!sessionId) {
|
|
6725
|
+
throw new Error("Missing web session");
|
|
6726
|
+
}
|
|
6727
|
+
const session = sessions.get(sessionId);
|
|
6728
|
+
if (!session) {
|
|
6729
|
+
throw new Error("Unknown web session");
|
|
6730
|
+
}
|
|
6731
|
+
if (Date.parse(session.expiresAt) <= Date.now()) {
|
|
6732
|
+
sessions.delete(sessionId);
|
|
6733
|
+
throw new Error("Expired web session");
|
|
6734
|
+
}
|
|
6735
|
+
return { ...session, id: sessionId };
|
|
6736
|
+
}
|
|
6737
|
+
async function readJson2(request) {
|
|
6738
|
+
return await request.json();
|
|
6739
|
+
}
|
|
6740
|
+
function page2() {
|
|
6741
|
+
return `<!doctype html>
|
|
6742
|
+
<html lang="en">
|
|
6743
|
+
<head>
|
|
6744
|
+
<meta charset="utf-8" />
|
|
6745
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6746
|
+
<title>Autho</title>
|
|
6747
|
+
<style>
|
|
6748
|
+
:root { color-scheme: light; font-family: ui-sans-serif, system-ui, sans-serif; }
|
|
6749
|
+
body { margin: 0; background: #f5f1e8; color: #1f2937; }
|
|
6750
|
+
main { max-width: 760px; margin: 0 auto; padding: 32px 20px 64px; }
|
|
6751
|
+
section { background: #fffdf8; border: 1px solid #d6cbb8; border-radius: 16px; padding: 20px; margin-bottom: 16px; box-shadow: 0 8px 30px rgba(83,58,18,0.08); }
|
|
6752
|
+
h1 { margin-top: 0; font-size: 2rem; }
|
|
6753
|
+
input, select, button { font: inherit; padding: 10px 12px; border-radius: 10px; border: 1px solid #bca889; }
|
|
6754
|
+
button { background: #1f4d3a; color: white; border: none; cursor: pointer; }
|
|
6755
|
+
form { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
6756
|
+
pre { background: #1f2937; color: #f9fafb; padding: 12px; border-radius: 12px; overflow: auto; }
|
|
6757
|
+
ul { padding-left: 18px; }
|
|
6758
|
+
</style>
|
|
6759
|
+
</head>
|
|
6760
|
+
<body>
|
|
6761
|
+
<main>
|
|
6762
|
+
<section>
|
|
6763
|
+
<h1>Autho Local Web</h1>
|
|
6764
|
+
<p>Local-only Bun web UI for vault unlock and secret browsing.</p>
|
|
6765
|
+
<form id="unlock-form">
|
|
6766
|
+
<input id="password" type="password" placeholder="Master password" required />
|
|
6767
|
+
<input id="totp" inputmode="numeric" autocomplete="one-time-code" placeholder="Authenticator code (if enabled)" />
|
|
6768
|
+
<button type="submit">Unlock</button>
|
|
6769
|
+
</form>
|
|
6770
|
+
</section>
|
|
6771
|
+
<section>
|
|
6772
|
+
<h2>Secrets</h2>
|
|
6773
|
+
<button id="refresh">Refresh</button>
|
|
6774
|
+
<ul id="secret-list"></ul>
|
|
6775
|
+
<pre id="output"></pre>
|
|
6776
|
+
</section>
|
|
6777
|
+
<section id="linked" hidden>
|
|
6778
|
+
<h2>Linked to <span id="linked-name"></span></h2>
|
|
6779
|
+
<ul id="linked-list"></ul>
|
|
6780
|
+
<p id="linked-from"></p>
|
|
6781
|
+
<form id="link-form">
|
|
6782
|
+
<input id="link-ref" placeholder="Secret name or id to link" required />
|
|
6783
|
+
<button type="submit">Link</button>
|
|
6784
|
+
</form>
|
|
6785
|
+
</section>
|
|
6786
|
+
</main>
|
|
6787
|
+
<script>
|
|
6788
|
+
const output = document.getElementById('output');
|
|
6789
|
+
const secretList = document.getElementById('secret-list');
|
|
6790
|
+
async function refresh() {
|
|
6791
|
+
const response = await fetch('/api/secrets');
|
|
6792
|
+
if (!response.ok) {
|
|
6793
|
+
output.textContent = await response.text();
|
|
6794
|
+
return;
|
|
6795
|
+
}
|
|
6796
|
+
const data = await response.json();
|
|
6797
|
+
secretList.innerHTML = '';
|
|
6798
|
+
data.data.forEach((secret) => {
|
|
6799
|
+
const item = document.createElement('li');
|
|
6800
|
+
const open = document.createElement('a');
|
|
6801
|
+
open.href = '#';
|
|
6802
|
+
open.textContent = secret.name + " (" + secret.type + ")" + (secret.linkCount ? " \xB7 " + secret.linkCount + " linked" : "");
|
|
6803
|
+
open.addEventListener('click', (event) => { event.preventDefault(); showSecret(secret.id); });
|
|
6804
|
+
item.appendChild(open);
|
|
6805
|
+
secretList.appendChild(item);
|
|
6806
|
+
});
|
|
6807
|
+
output.textContent = JSON.stringify(data, null, 2);
|
|
6808
|
+
}
|
|
6809
|
+
let current = null;
|
|
6810
|
+
async function showSecret(ref) {
|
|
6811
|
+
const response = await fetch('/api/secrets/' + encodeURIComponent(ref));
|
|
6812
|
+
if (!response.ok) {
|
|
6813
|
+
output.textContent = await response.text();
|
|
6814
|
+
return;
|
|
6815
|
+
}
|
|
6816
|
+
const secret = (await response.json()).data;
|
|
6817
|
+
current = secret;
|
|
6818
|
+
const { links, linkedFrom, ...rest } = secret;
|
|
6819
|
+
output.textContent = JSON.stringify(rest, null, 2);
|
|
6820
|
+
document.getElementById('linked').hidden = false;
|
|
6821
|
+
document.getElementById('linked-name').textContent = secret.name;
|
|
6822
|
+
const list = document.getElementById('linked-list');
|
|
6823
|
+
list.innerHTML = '';
|
|
6824
|
+
if (!links.length) {
|
|
6825
|
+
list.innerHTML = '<li>No linked secrets</li>';
|
|
6826
|
+
}
|
|
6827
|
+
links.forEach((linked) => {
|
|
6828
|
+
const item = document.createElement('li');
|
|
6829
|
+
const open = document.createElement('a');
|
|
6830
|
+
open.href = '#';
|
|
6831
|
+
open.textContent = linked.name + " (" + linked.type + ") ";
|
|
6832
|
+
open.addEventListener('click', (event) => { event.preventDefault(); showSecret(linked.id); });
|
|
6833
|
+
const unlink = document.createElement('button');
|
|
6834
|
+
unlink.textContent = 'Unlink';
|
|
6835
|
+
unlink.addEventListener('click', () => changeLink('DELETE', linked.id));
|
|
6836
|
+
item.append(open, unlink);
|
|
6837
|
+
list.appendChild(item);
|
|
6838
|
+
});
|
|
6839
|
+
document.getElementById('linked-from').textContent = linkedFrom.length
|
|
6840
|
+
? 'Linked from: ' + linkedFrom.map((primary) => primary.name).join(', ')
|
|
6841
|
+
: '';
|
|
6842
|
+
}
|
|
6843
|
+
async function changeLink(method, secret) {
|
|
6844
|
+
const response = await fetch('/api/links', {
|
|
6845
|
+
method,
|
|
6846
|
+
headers: { 'content-type': 'application/json' },
|
|
6847
|
+
body: JSON.stringify({ primary: current.id, secret })
|
|
6848
|
+
});
|
|
6849
|
+
if (!response.ok) {
|
|
6850
|
+
output.textContent = await response.text();
|
|
6851
|
+
return;
|
|
6852
|
+
}
|
|
6853
|
+
await showSecret(current.id);
|
|
6854
|
+
await refresh();
|
|
6855
|
+
}
|
|
6856
|
+
document.getElementById('link-form').addEventListener('submit', async (event) => {
|
|
6857
|
+
event.preventDefault();
|
|
6858
|
+
const input = document.getElementById('link-ref');
|
|
6859
|
+
await changeLink('POST', input.value);
|
|
6860
|
+
input.value = '';
|
|
6861
|
+
});
|
|
6862
|
+
document.getElementById('unlock-form').addEventListener('submit', async (event) => {
|
|
6863
|
+
event.preventDefault();
|
|
6864
|
+
const response = await fetch('/api/session/unlock', {
|
|
6865
|
+
method: 'POST',
|
|
6866
|
+
headers: { 'content-type': 'application/json' },
|
|
6867
|
+
body: JSON.stringify({
|
|
6868
|
+
password: document.getElementById('password').value,
|
|
6869
|
+
totp: document.getElementById('totp').value || undefined
|
|
6870
|
+
})
|
|
6871
|
+
});
|
|
6872
|
+
output.textContent = await response.text();
|
|
6873
|
+
if (response.ok) {
|
|
6874
|
+
await refresh();
|
|
6875
|
+
}
|
|
6876
|
+
});
|
|
6877
|
+
document.getElementById('refresh').addEventListener('click', refresh);
|
|
6878
|
+
</script>
|
|
6879
|
+
</body>
|
|
6880
|
+
</html>`;
|
|
6881
|
+
}
|
|
6882
|
+
async function startWebServer(options = {}) {
|
|
6883
|
+
const vaultPath = resolve4(options.vaultPath ?? defaultVaultPath());
|
|
6884
|
+
const host = options.host ?? "127.0.0.1";
|
|
6885
|
+
const port = options.port ?? 8061;
|
|
6886
|
+
const sessions = new Map;
|
|
6887
|
+
const cleanup = () => {
|
|
6888
|
+
for (const [id, session] of sessions.entries()) {
|
|
6889
|
+
if (Date.parse(session.expiresAt) <= Date.now()) {
|
|
6890
|
+
sessions.delete(id);
|
|
6891
|
+
}
|
|
6892
|
+
}
|
|
6893
|
+
};
|
|
6894
|
+
const server = Bun.serve({
|
|
6895
|
+
fetch: async (request) => {
|
|
6896
|
+
cleanup();
|
|
6897
|
+
const url = new URL(request.url);
|
|
6898
|
+
try {
|
|
6899
|
+
if (request.method === "GET" && url.pathname === "/health") {
|
|
6900
|
+
return json2({ ok: true, running: true, vaultPath });
|
|
6901
|
+
}
|
|
6902
|
+
if (request.method === "GET" && url.pathname === "/") {
|
|
6903
|
+
return html(page2());
|
|
6904
|
+
}
|
|
6905
|
+
if (request.method === "POST" && url.pathname === "/api/session/unlock") {
|
|
6906
|
+
const body = await readJson2(request);
|
|
6907
|
+
const ttlSeconds = body.ttlSeconds ?? 3600;
|
|
6908
|
+
if (ttlSeconds <= 0 || ttlSeconds > 86400) {
|
|
6909
|
+
return json2({ error: "Session TTL must be between 1 and 86400 seconds" }, 400);
|
|
6910
|
+
}
|
|
6911
|
+
const { rootKey, sealBound } = VaultService.unlockRootKey(vaultPath, {
|
|
6912
|
+
password: body.password,
|
|
6913
|
+
sealRecord: await loadSealRecord(vaultPath),
|
|
6914
|
+
totp: body.totp
|
|
6915
|
+
});
|
|
6916
|
+
const sessionId = randomBytes6(16).toString("hex");
|
|
6917
|
+
const expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString();
|
|
6918
|
+
sessions.set(sessionId, { expiresAt, rootKey, sealBound });
|
|
6919
|
+
return json2({ expiresAt, sessionId }, 200, { "set-cookie": `autho_session=${sessionId}; HttpOnly; SameSite=Strict; Path=/; Secure` });
|
|
6920
|
+
}
|
|
6921
|
+
if (request.method === "POST" && url.pathname === "/api/session/lock") {
|
|
6922
|
+
const cookies = parseCookies(request);
|
|
6923
|
+
if (cookies.autho_session) {
|
|
6924
|
+
sessions.delete(cookies.autho_session);
|
|
6925
|
+
}
|
|
6926
|
+
return json2({ locked: true }, 200, { "set-cookie": "autho_session=; Max-Age=0; Path=/; Secure" });
|
|
6927
|
+
}
|
|
6928
|
+
if (url.pathname.startsWith("/api/")) {
|
|
6929
|
+
const webSession = requireSession(request, sessions);
|
|
6930
|
+
const session = await openSession(vaultPath, webSession);
|
|
6931
|
+
try {
|
|
6932
|
+
if (request.method === "GET" && url.pathname === "/api/status") {
|
|
6933
|
+
return json2({
|
|
6934
|
+
initialized: true,
|
|
6935
|
+
secretCount: session.listSecrets().length,
|
|
6936
|
+
unlocked: true,
|
|
6937
|
+
vaultPath
|
|
6938
|
+
});
|
|
6939
|
+
}
|
|
6940
|
+
if (request.method === "GET" && url.pathname === "/api/secrets") {
|
|
6941
|
+
return json2({ data: session.listSecretIndex() });
|
|
6942
|
+
}
|
|
6943
|
+
if (request.method === "GET" && url.pathname.startsWith("/api/secrets/")) {
|
|
6944
|
+
const ref = decodeURIComponent(url.pathname.slice("/api/secrets/".length));
|
|
6945
|
+
const { linkedFrom, links } = session.getLinks(ref);
|
|
6946
|
+
return json2({ data: { ...session.getSecret(ref), linkedFrom, links } });
|
|
6947
|
+
}
|
|
6948
|
+
if ((request.method === "POST" || request.method === "DELETE") && url.pathname === "/api/links") {
|
|
6949
|
+
const body = await readJson2(request);
|
|
6950
|
+
return json2({
|
|
6951
|
+
data: request.method === "POST" ? session.linkSecrets(body.primary, body.secret) : session.unlinkSecrets(body.primary, body.secret)
|
|
6952
|
+
});
|
|
6953
|
+
}
|
|
6954
|
+
if (request.method === "POST" && url.pathname === "/api/secrets") {
|
|
6955
|
+
const body = await readJson2(request);
|
|
6956
|
+
return json2({
|
|
6957
|
+
data: session.addSecret({
|
|
6958
|
+
metadata: Object.fromEntries(Object.entries({
|
|
6959
|
+
algorithm: body.algorithm,
|
|
6960
|
+
description: body.description,
|
|
6961
|
+
digits: body.digits,
|
|
6962
|
+
url: body.url
|
|
6963
|
+
}).filter(([, value]) => value !== undefined)),
|
|
6964
|
+
name: body.name,
|
|
6965
|
+
type: body.type,
|
|
6966
|
+
username: body.username,
|
|
6967
|
+
value: body.value
|
|
6968
|
+
})
|
|
6969
|
+
}, 201);
|
|
6970
|
+
}
|
|
6971
|
+
if (request.method === "DELETE" && url.pathname.startsWith("/api/secrets/")) {
|
|
6972
|
+
const ref = decodeURIComponent(url.pathname.slice("/api/secrets/".length));
|
|
6973
|
+
return json2({ data: session.removeSecret(ref, { withLinked: url.searchParams.get("withLinked") === "1" }) });
|
|
6974
|
+
}
|
|
6975
|
+
if (request.method === "GET" && url.pathname.startsWith("/api/otp/")) {
|
|
6976
|
+
const ref = decodeURIComponent(url.pathname.slice("/api/otp/".length));
|
|
6977
|
+
return json2({ data: session.generateOtp(ref) });
|
|
6978
|
+
}
|
|
6979
|
+
} finally {
|
|
6980
|
+
await closeSessionAndRecordSeal(vaultPath, session);
|
|
6981
|
+
}
|
|
6982
|
+
}
|
|
6983
|
+
} catch (error) {
|
|
6984
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6985
|
+
const status = /session/i.test(message) ? 401 : 400;
|
|
6986
|
+
return json2({ error: message }, status);
|
|
6987
|
+
}
|
|
6988
|
+
return json2({ error: "Not found" }, 404);
|
|
6989
|
+
},
|
|
6990
|
+
hostname: host,
|
|
6991
|
+
port
|
|
6992
|
+
});
|
|
6993
|
+
process.on("SIGINT", () => process.exit(0));
|
|
6994
|
+
process.on("SIGTERM", () => process.exit(0));
|
|
6995
|
+
console.log(`Autho web server listening on http://${host}:${server.port}`);
|
|
6996
|
+
await new Promise(() => {});
|
|
6997
|
+
}
|
|
6998
|
+
|
|
6999
|
+
// apps/cli/src/index.ts
|
|
6686
7000
|
function parseArgs(argv) {
|
|
6687
7001
|
const dashDashIndex = argv.indexOf("--");
|
|
6688
7002
|
const main = dashDashIndex === -1 ? argv : argv.slice(0, dashDashIndex);
|
|
@@ -6776,7 +7090,7 @@ function printSealChanges(changes, diffTrusted) {
|
|
|
6776
7090
|
}
|
|
6777
7091
|
}
|
|
6778
7092
|
function absolutePath(path) {
|
|
6779
|
-
return
|
|
7093
|
+
return resolve6(path);
|
|
6780
7094
|
}
|
|
6781
7095
|
function buildSecretMetadata(args) {
|
|
6782
7096
|
return Object.fromEntries(Object.entries({
|
|
@@ -7031,34 +7345,10 @@ async function runInitWizard(vaultPath, password, existingCreds) {
|
|
|
7031
7345
|
console.log("Setup complete. Run \x1B[1mautho init\x1B[0m anytime to change these settings.");
|
|
7032
7346
|
}
|
|
7033
7347
|
async function runWebServer(vaultPath, args) {
|
|
7034
|
-
|
|
7035
|
-
"
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
"--vault",
|
|
7039
|
-
absolutePath(vaultPath)
|
|
7040
|
-
];
|
|
7041
|
-
const host = getString(args, "host");
|
|
7042
|
-
const port = getString(args, "port") ?? String(numberPreference("webPort"));
|
|
7043
|
-
if (host) {
|
|
7044
|
-
commandArgs.push("--host", host);
|
|
7045
|
-
}
|
|
7046
|
-
if (port) {
|
|
7047
|
-
commandArgs.push("--port", port);
|
|
7048
|
-
}
|
|
7049
|
-
await new Promise((resolvePromise, rejectPromise) => {
|
|
7050
|
-
const child = spawn3(process.execPath, commandArgs, {
|
|
7051
|
-
cwd: process.cwd(),
|
|
7052
|
-
stdio: "inherit"
|
|
7053
|
-
});
|
|
7054
|
-
child.on("exit", (code) => {
|
|
7055
|
-
if (code === 0) {
|
|
7056
|
-
resolvePromise();
|
|
7057
|
-
return;
|
|
7058
|
-
}
|
|
7059
|
-
rejectPromise(new Error(`Web server exited with code ${code ?? 1}`));
|
|
7060
|
-
});
|
|
7061
|
-
child.on("error", rejectPromise);
|
|
7348
|
+
await startWebServer({
|
|
7349
|
+
host: getString(args, "host"),
|
|
7350
|
+
port: Number(getString(args, "port") ?? numberPreference("webPort")),
|
|
7351
|
+
vaultPath: absolutePath(vaultPath)
|
|
7062
7352
|
});
|
|
7063
7353
|
}
|
|
7064
7354
|
function help() {
|
|
@@ -7235,7 +7525,7 @@ async function main() {
|
|
|
7235
7525
|
console.log(" Tip: Use a cloud-synced folder (e.g. Google Drive) to share the vault across machines.");
|
|
7236
7526
|
const customDir = (await readLine(` Path (Enter to keep default): `)).trim();
|
|
7237
7527
|
if (customDir && customDir !== currentDir) {
|
|
7238
|
-
const resolved =
|
|
7528
|
+
const resolved = resolve6(customDir).replace(/\\/g, "/");
|
|
7239
7529
|
saveConfig({ ...config, vaultDir: resolved });
|
|
7240
7530
|
effectiveVaultPath = resolved + "/vault.db";
|
|
7241
7531
|
console.log(` \x1B[32m\u2713\x1B[0m Vault directory set to ${resolved}`);
|
|
@@ -7303,7 +7593,7 @@ async function main() {
|
|
|
7303
7593
|
if (key === "autoLock") {
|
|
7304
7594
|
updated.autoLock = value === "true";
|
|
7305
7595
|
} else if (key === "vaultDir") {
|
|
7306
|
-
updated.vaultDir = isAbsolute(value) ? value :
|
|
7596
|
+
updated.vaultDir = isAbsolute(value) ? value : resolve6(value);
|
|
7307
7597
|
} else if (key === "editor") {
|
|
7308
7598
|
updated.editor = value;
|
|
7309
7599
|
} else if (key === "autoLockTimeout") {
|