remosidian 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/remosidian.mjs +396 -40
  2. package/package.json +1 -1
@@ -14380,13 +14380,20 @@ var __webpack_exports__ = {};
14380
14380
 
14381
14381
  // EXPORTS
14382
14382
  __nccwpck_require__.d(__webpack_exports__, {
14383
+ gu: () => (/* binding */ buildPairCode),
14384
+ ti: () => (/* binding */ buildPairDeepLink),
14383
14385
  lz: () => (/* binding */ buildPairUri),
14386
+ jO: () => (/* binding */ buildServeArgs),
14387
+ YP: () => (/* binding */ createCompactPairingPayload),
14384
14388
  wX: () => (/* binding */ expandVaultSpec),
14385
14389
  iW: () => (/* binding */ main),
14386
14390
  Zy: () => (/* binding */ parseArgs),
14391
+ Mf: () => (/* binding */ resolveEntrypointHref),
14387
14392
  t0: () => (/* binding */ resolveInvocation)
14388
14393
  });
14389
14394
 
14395
+ ;// CONCATENATED MODULE: external "node:child_process"
14396
+ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:child_process");
14390
14397
  ;// CONCATENATED MODULE: external "node:fs"
14391
14398
  const external_node_fs_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:fs");
14392
14399
  ;// CONCATENATED MODULE: external "node:os"
@@ -14712,8 +14719,6 @@ function loadOrCreateDaemonIdentity(port, identityPath) {
14712
14719
  return identity;
14713
14720
  }
14714
14721
  //# sourceMappingURL=identity.js.map
14715
- ;// CONCATENATED MODULE: external "node:child_process"
14716
- const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:child_process");
14717
14722
  ;// CONCATENATED MODULE: ../../packages/daemon-core/dist/advertised-hosts.js
14718
14723
 
14719
14724
 
@@ -19580,7 +19585,7 @@ function runFileBackedPluginTurn(input, prompt, options, onDelta) {
19580
19585
  }
19581
19586
  //# sourceMappingURL=chat-plugin-runtime.js.map
19582
19587
  ;// CONCATENATED MODULE: ../remosidian-server/dist/version.js
19583
- const VERSION = '0.1.0';
19588
+ const VERSION = '0.1.2';
19584
19589
  //# sourceMappingURL=version.js.map
19585
19590
  ;// CONCATENATED MODULE: ../remosidian-server/dist/chat-runtime.js
19586
19591
 
@@ -28062,6 +28067,7 @@ function startTunnel({ port, logDir, tunnelStatePath, resolveLaunchSpec = resolv
28062
28067
 
28063
28068
 
28064
28069
 
28070
+
28065
28071
  const PROG = 'remosidian';
28066
28072
  const PRIMARY_NPM_PACKAGE = '@remosidian/cli';
28067
28073
  const ALIAS_NPM_PACKAGE = 'remosidian';
@@ -28080,9 +28086,11 @@ function helpText() {
28080
28086
  ` ${PROG} <command> [options]`,
28081
28087
  '',
28082
28088
  'Commands:',
28083
- ' start Start the Remosidian server in the foreground',
28089
+ ' start Start or reuse the Remosidian background daemon',
28090
+ ' serve Internal foreground daemon entrypoint',
28084
28091
  ' pair Open a one-time pairing window and print a QR code',
28085
28092
  ' status Print the running daemon health snapshot',
28093
+ ' stop Stop the background daemon',
28086
28094
  ' reset-pairings Revoke every paired device (local only)',
28087
28095
  ' init Create ~/.remosidian/config.json interactively',
28088
28096
  ' help Show this help',
@@ -28104,6 +28112,7 @@ function helpText() {
28104
28112
  ' --bind <address> Bind to this interface (default 0.0.0.0)',
28105
28113
  ' --agent <name> AI agent for /Ask: claude or codex (auto-detects)',
28106
28114
  ' --agent-path <path> Override the agent binary path',
28115
+ ' --foreground Run the server in the foreground',
28107
28116
  ' --json Emit a single JSON line instead of human output',
28108
28117
  ' --no-qr Skip the pairing QR on startup',
28109
28118
  ].join('\n');
@@ -28156,6 +28165,15 @@ function resolveInvocation(argv) {
28156
28165
  return { command, rest, useCurrentDirectoryVault: false };
28157
28166
  }
28158
28167
 
28168
+ function resolveEntrypointHref(path) {
28169
+ if (!path) return '';
28170
+ try {
28171
+ return (0,external_node_url_namespaceObject.pathToFileURL)((0,external_node_fs_namespaceObject.realpathSync)((0,external_node_path_namespaceObject.resolve)(path))).href;
28172
+ } catch {
28173
+ return (0,external_node_url_namespaceObject.pathToFileURL)((0,external_node_path_namespaceObject.resolve)(path)).href;
28174
+ }
28175
+ }
28176
+
28159
28177
  function expandVaultSpec(spec) {
28160
28178
  const trimmed = spec.trim();
28161
28179
  if (!trimmed) return null;
@@ -28201,6 +28219,69 @@ function resolvePaths(flags) {
28201
28219
  return createRemosidianPaths(dataDir ?? process.env.REMOSIDIAN_HOME ?? '~/.remosidian');
28202
28220
  }
28203
28221
 
28222
+ function createRuntimePaths(paths) {
28223
+ const runtimeDir = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'runtime', 'current');
28224
+ const logDir = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'logs');
28225
+ return {
28226
+ runtimeDir,
28227
+ runtimeEntryPath: (0,external_node_path_namespaceObject.join)(runtimeDir, 'dist', 'remosidian.mjs'),
28228
+ pidPath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'daemon.pid'),
28229
+ logDir,
28230
+ stdoutLogPath: (0,external_node_path_namespaceObject.join)(logDir, 'daemon.stdout.log'),
28231
+ stderrLogPath: (0,external_node_path_namespaceObject.join)(logDir, 'daemon.stderr.log'),
28232
+ pairingQrPath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-qr.png'),
28233
+ pairingCodePath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-code.txt'),
28234
+ };
28235
+ }
28236
+
28237
+ function readPidFile(pidPath) {
28238
+ try {
28239
+ const pid = Number((0,external_node_fs_namespaceObject.readFileSync)(pidPath, 'utf8').trim());
28240
+ return Number.isFinite(pid) && pid > 0 ? pid : null;
28241
+ } catch {
28242
+ return null;
28243
+ }
28244
+ }
28245
+
28246
+ function writePidFile(pidPath, pid) {
28247
+ (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(pidPath), { recursive: true });
28248
+ (0,external_node_fs_namespaceObject.writeFileSync)(pidPath, `${pid}\n`, 'utf8');
28249
+ }
28250
+
28251
+ function removePidFile(pidPath) {
28252
+ (0,external_node_fs_namespaceObject.rmSync)(pidPath, { force: true });
28253
+ }
28254
+
28255
+ function remosidian_isProcessAlive(pid) {
28256
+ try {
28257
+ process.kill(pid, 0);
28258
+ return true;
28259
+ } catch {
28260
+ return false;
28261
+ }
28262
+ }
28263
+
28264
+ function currentEntrypointPath() {
28265
+ const entryPath = (0,external_node_url_namespaceObject.fileURLToPath)(import.meta.url);
28266
+ try {
28267
+ return (0,external_node_fs_namespaceObject.realpathSync)(entryPath);
28268
+ } catch {
28269
+ return entryPath;
28270
+ }
28271
+ }
28272
+
28273
+ function installRuntime(paths) {
28274
+ const runtime = createRuntimePaths(paths);
28275
+ (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(runtime.runtimeEntryPath), { recursive: true });
28276
+ (0,external_node_fs_namespaceObject.copyFileSync)(currentEntrypointPath(), runtime.runtimeEntryPath);
28277
+ (0,external_node_fs_namespaceObject.chmodSync)(runtime.runtimeEntryPath, 0o755);
28278
+ return runtime;
28279
+ }
28280
+
28281
+ function resolveRequestedPort(flags, paths) {
28282
+ return Number(flags.port) || (loadConfigOrNull(paths)?.server?.port ?? 41234);
28283
+ }
28284
+
28204
28285
  function readCsvList(value) {
28205
28286
  if (typeof value !== 'string') return [];
28206
28287
  return value
@@ -28331,7 +28412,7 @@ async function resolveRelayForStart(flags, paths, port) {
28331
28412
 
28332
28413
  function renderQrTerminal(payload) {
28333
28414
  return new Promise((resolveTerm, rejectTerm) => {
28334
- lib.toString(payload, { type: 'terminal', small: true }, (err, str) => {
28415
+ lib.toString(payload, { type: 'terminal', small: true, errorCorrectionLevel: 'M' }, (err, str) => {
28335
28416
  if (err) {
28336
28417
  rejectTerm(err);
28337
28418
  return;
@@ -28343,7 +28424,7 @@ function renderQrTerminal(payload) {
28343
28424
 
28344
28425
  async function writeQrPng(payload, filePath) {
28345
28426
  (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(filePath), { recursive: true });
28346
- await lib.toFile(filePath, payload, { type: 'png', margin: 1, width: 512 });
28427
+ await lib.toFile(filePath, payload, { type: 'png', errorCorrectionLevel: 'M', margin: 1, scale: 8 });
28347
28428
  }
28348
28429
 
28349
28430
  function printHumanHealth(health) {
@@ -28405,7 +28486,214 @@ function buildPairUri(payload) {
28405
28486
  return `remosidian://pair?v=1&data=${encoded}`;
28406
28487
  }
28407
28488
 
28408
- async function cmdStart(flags, paths) {
28489
+ function normalizePairingConnections(payload) {
28490
+ const explicitConnections = Array.isArray(payload.connections)
28491
+ ? payload.connections
28492
+ .filter(
28493
+ (target) =>
28494
+ target &&
28495
+ typeof target.host === 'string' &&
28496
+ Number.isFinite(target.port) &&
28497
+ typeof target.source === 'string' &&
28498
+ typeof target.stability === 'string',
28499
+ )
28500
+ .map((target) => ({
28501
+ kind: target.kind === 'relay' ? 'relay' : 'direct',
28502
+ host: target.host.trim(),
28503
+ port: Math.floor(Number(target.port)),
28504
+ ...(target.secure === true ? { secure: true } : {}),
28505
+ source: target.source,
28506
+ stability: target.stability,
28507
+ }))
28508
+ .filter((target) => target.host && target.port > 0)
28509
+ : [];
28510
+ if (explicitConnections.length > 0) {
28511
+ return explicitConnections;
28512
+ }
28513
+ const fallbackHosts = Array.isArray(payload.fallbackHosts)
28514
+ ? payload.fallbackHosts.filter((host) => typeof host === 'string' && host.trim()).map((host) => host.trim())
28515
+ : [];
28516
+ const port = Number.isFinite(payload.port) && payload.port > 0 ? Math.floor(payload.port) : 41234;
28517
+ return [
28518
+ {
28519
+ kind: 'direct',
28520
+ host: payload.canonicalHost,
28521
+ port,
28522
+ source: 'configured_host',
28523
+ stability: 'stable',
28524
+ },
28525
+ ...fallbackHosts.map((host) => ({
28526
+ kind: 'direct',
28527
+ host,
28528
+ port,
28529
+ source: 'fallback',
28530
+ stability: 'stable',
28531
+ })),
28532
+ ];
28533
+ }
28534
+
28535
+ function createCompactPairingPayload(payload) {
28536
+ const compact = {
28537
+ t: 'rp',
28538
+ d: payload.daemonId,
28539
+ n: payload.displayName,
28540
+ h: payload.canonicalHost,
28541
+ p: payload.port,
28542
+ c: payload.pairNonce,
28543
+ e: payload.expiresAt,
28544
+ };
28545
+ if (Array.isArray(payload.fallbackHosts) && payload.fallbackHosts.length > 0) {
28546
+ compact.f = payload.fallbackHosts;
28547
+ }
28548
+ const connections = normalizePairingConnections(payload);
28549
+ if (connections.length > 0) {
28550
+ compact.o = connections.map((target) => [
28551
+ target.kind,
28552
+ target.host,
28553
+ target.port,
28554
+ target.source,
28555
+ target.stability,
28556
+ target.secure === true,
28557
+ ]);
28558
+ }
28559
+ if (payload.runtime) {
28560
+ compact.r = payload.runtime;
28561
+ }
28562
+ if (Array.isArray(payload.vaults) && payload.vaults.length > 0) {
28563
+ compact.v = payload.vaults;
28564
+ }
28565
+ return compact;
28566
+ }
28567
+
28568
+ function buildPairCode(payload) {
28569
+ return JSON.stringify(createCompactPairingPayload(payload));
28570
+ }
28571
+
28572
+ function buildPairDeepLink(pairingCode) {
28573
+ return `remosidian://pair?data=${encodeURIComponent(pairingCode)}`;
28574
+ }
28575
+
28576
+ function formatConnectionSummary(target) {
28577
+ const scheme = target.secure === true || target.port === 443 ? 'https' : 'http';
28578
+ return `${scheme}://${target.host}:${target.port} (${target.source}, ${target.stability})`;
28579
+ }
28580
+
28581
+ async function printPairingQr(pairingPayload, paths) {
28582
+ const runtime = createRuntimePaths(paths);
28583
+ const pairingCode = buildPairCode(pairingPayload);
28584
+ const deepLink = buildPairDeepLink(pairingCode);
28585
+ const qr = await renderQrTerminal(pairingCode);
28586
+ (0,external_node_fs_namespaceObject.writeFileSync)(runtime.pairingCodePath, `${pairingCode}\n`, 'utf8');
28587
+ await writeQrPng(pairingCode, runtime.pairingQrPath).catch(() => undefined);
28588
+ process.stdout.write(`\nScan this QR code with the Remosidian app:\n\n${qr}\n`);
28589
+ process.stdout.write(`If the terminal QR is hard to scan, open this PNG instead:\n${runtime.pairingQrPath}\n`);
28590
+ process.stdout.write(`If this device cannot scan, paste this pairing code in Remosidian:\n${pairingCode}\n`);
28591
+ process.stdout.write(`Pairing code saved to:\n${runtime.pairingCodePath}\n`);
28592
+ process.stdout.write(`Or open this deep link on a device with Remosidian installed:\n${deepLink}\n`);
28593
+ }
28594
+
28595
+ async function printPairingSummary(port, paths, health = null, flags = {}) {
28596
+ const healthPayload = health ?? (await waitForHealth(port));
28597
+ if (flags.json) {
28598
+ process.stdout.write(`${JSON.stringify({ ok: true, ...healthPayload })}\n`);
28599
+ return;
28600
+ }
28601
+ process.stdout.write(`Remosidian daemon is ready.\n\n`);
28602
+ process.stdout.write(`${printHumanHealth(healthPayload)}\n`);
28603
+ if (flags['no-qr']) {
28604
+ return;
28605
+ }
28606
+ const pairingPayload = await postJson(port, '/pair/open');
28607
+ const connections = normalizePairingConnections(pairingPayload);
28608
+ if (connections.length > 0) {
28609
+ process.stdout.write('\nPairing paths:\n');
28610
+ for (const target of connections) {
28611
+ process.stdout.write(` - ${formatConnectionSummary(target)}\n`);
28612
+ }
28613
+ }
28614
+ await printPairingQr(pairingPayload, paths);
28615
+ }
28616
+
28617
+ async function probeHealth(port, timeoutMs = 0) {
28618
+ const deadline = Date.now() + timeoutMs;
28619
+ do {
28620
+ try {
28621
+ const resp = await fetch(`http://127.0.0.1:${port}/health`, { signal: AbortSignal.timeout(2000) });
28622
+ if (resp.ok) {
28623
+ return await resp.json();
28624
+ }
28625
+ } catch {
28626
+ // Retry below until timeout.
28627
+ }
28628
+ if (timeoutMs <= 0) break;
28629
+ await new Promise((resolveProbe) => setTimeout(resolveProbe, 250));
28630
+ } while (Date.now() < deadline);
28631
+ return null;
28632
+ }
28633
+
28634
+ async function waitForHealth(port, timeoutMs = 30_000) {
28635
+ const health = await probeHealth(port, timeoutMs);
28636
+ if (health) {
28637
+ return health;
28638
+ }
28639
+ throw new Error(`Timed out waiting for Remosidian daemon on 127.0.0.1:${port}`);
28640
+ }
28641
+
28642
+ async function postJson(port, pathname, body = undefined) {
28643
+ const response = await fetch(`http://127.0.0.1:${port}${pathname}`, {
28644
+ method: 'POST',
28645
+ headers: body ? { 'content-type': 'application/json' } : undefined,
28646
+ body: body ? JSON.stringify(body) : undefined,
28647
+ });
28648
+ const payload = await response.json().catch(() => ({}));
28649
+ if (!response.ok) {
28650
+ throw new Error(`${pathname} failed: ${response.status} ${JSON.stringify(payload)}`);
28651
+ }
28652
+ return payload;
28653
+ }
28654
+
28655
+ function buildServeArgs(flags) {
28656
+ const args = ['serve'];
28657
+ for (const vault of flags.__vaults ?? []) {
28658
+ args.push('--vault', vault);
28659
+ }
28660
+ const skipFlags = new Set(['__vaults', 'foreground', 'json']);
28661
+ for (const [key, value] of Object.entries(flags)) {
28662
+ if (skipFlags.has(key)) continue;
28663
+ if (key === 'no-qr') continue;
28664
+ if (value === true) {
28665
+ args.push(`--${key}`);
28666
+ } else if (typeof value === 'string') {
28667
+ args.push(`--${key}`, value);
28668
+ }
28669
+ }
28670
+ args.push('--no-qr');
28671
+ return args;
28672
+ }
28673
+
28674
+ function startDetachedDaemon(paths, flags) {
28675
+ const runtime = installRuntime(paths);
28676
+ (0,external_node_fs_namespaceObject.mkdirSync)(runtime.logDir, { recursive: true });
28677
+ const stdoutFd = (0,external_node_fs_namespaceObject.openSync)(runtime.stdoutLogPath, 'a');
28678
+ const stderrFd = (0,external_node_fs_namespaceObject.openSync)(runtime.stderrLogPath, 'a');
28679
+ try {
28680
+ const child = (0,external_node_child_process_namespaceObject.spawn)(process.execPath, [runtime.runtimeEntryPath, ...buildServeArgs(flags)], {
28681
+ detached: true,
28682
+ stdio: ['ignore', stdoutFd, stderrFd],
28683
+ cwd: process.cwd(),
28684
+ env: { ...process.env },
28685
+ windowsHide: true,
28686
+ });
28687
+ child.unref();
28688
+ writePidFile(runtime.pidPath, child.pid);
28689
+ return child.pid;
28690
+ } finally {
28691
+ (0,external_node_fs_namespaceObject.closeSync)(stdoutFd);
28692
+ (0,external_node_fs_namespaceObject.closeSync)(stderrFd);
28693
+ }
28694
+ }
28695
+
28696
+ async function cmdServe(flags, paths) {
28409
28697
  const config = loadConfigOrNull(paths);
28410
28698
  const inlineVaults = flags.__vaults.map((spec) => expandVaultSpec(spec)).filter((vault) => Boolean(vault));
28411
28699
  let finalConfig;
@@ -28487,16 +28775,7 @@ async function cmdStart(flags, paths) {
28487
28775
  const openResp = await fetch(`http://127.0.0.1:${server.port}/pair/open`, { method: 'POST' });
28488
28776
  if (openResp.ok) {
28489
28777
  const payload = await openResp.json();
28490
- const uri = buildPairUri(payload);
28491
- const qr = await renderQrTerminal(uri);
28492
- process.stdout.write('\nScan to pair (one-time, expires in 5 min):\n');
28493
- process.stdout.write(`${qr}\n`);
28494
- process.stdout.write(`${uri}\n\n`);
28495
- const qrPath = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-qr.png');
28496
- await writeQrPng(uri, qrPath).catch(() => {
28497
- // PNG export is best-effort.
28498
- });
28499
- process.stdout.write(`Saved QR image: ${qrPath}\n`);
28778
+ await printPairingQr(payload, paths);
28500
28779
  }
28501
28780
  } catch (error) {
28502
28781
  process.stderr.write(`[${PROG}] pair payload not available: ${error?.message ?? error}\n`);
@@ -28519,31 +28798,60 @@ async function cmdStart(flags, paths) {
28519
28798
  process.on('SIGHUP', () => void shutdown('SIGHUP'));
28520
28799
  }
28521
28800
 
28522
- async function cmdPair(flags, paths) {
28523
- const port = Number(flags.port) || (loadConfigOrNull(paths)?.server?.port ?? 41234);
28524
- const openResp = await fetch(`http://127.0.0.1:${port}/pair/open`, { method: 'POST' });
28525
- if (!openResp.ok) {
28526
- process.stderr.write(`[${PROG}] /pair/open failed: ${openResp.status} ${openResp.statusText}\n`);
28527
- process.exit(1);
28801
+ async function cmdStart(flags, paths) {
28802
+ if (flags.foreground) {
28803
+ await cmdServe(flags, paths);
28804
+ return;
28805
+ }
28806
+ const port = resolveRequestedPort(flags, paths);
28807
+ const existing = await probeHealth(port, 1_000);
28808
+ if (existing) {
28809
+ if (!flags.json) {
28810
+ process.stdout.write('Remosidian daemon is already running. Reusing it.\n\n');
28811
+ }
28812
+ await printPairingSummary(port, paths, existing, flags);
28813
+ return;
28528
28814
  }
28529
- const payload = await openResp.json();
28530
- const uri = buildPairUri(payload);
28815
+
28816
+ const runtime = createRuntimePaths(paths);
28817
+ const stalePid = readPidFile(runtime.pidPath);
28818
+ if (stalePid && !remosidian_isProcessAlive(stalePid)) {
28819
+ removePidFile(runtime.pidPath);
28820
+ }
28821
+
28822
+ const pid = startDetachedDaemon(paths, flags);
28823
+ const health = await waitForHealth(port);
28824
+ if (!flags.json) {
28825
+ process.stdout.write(`Started Remosidian daemon in the background (pid ${pid}).\n`);
28826
+ process.stdout.write(`Logs: ${runtime.stdoutLogPath} / ${runtime.stderrLogPath}\n\n`);
28827
+ }
28828
+ await printPairingSummary(port, paths, health, flags);
28829
+ }
28830
+
28831
+ async function cmdPair(flags, paths) {
28832
+ const port = resolveRequestedPort(flags, paths);
28833
+ const payload = await postJson(port, '/pair/open');
28834
+ const pairingCode = buildPairCode(payload);
28835
+ const deepLink = buildPairDeepLink(pairingCode);
28531
28836
  if (flags.json) {
28532
- process.stdout.write(`${JSON.stringify({ ok: true, payload, uri })}\n`);
28837
+ process.stdout.write(`${JSON.stringify({ ok: true, payload, pairingCode, deepLink })}\n`);
28533
28838
  return;
28534
28839
  }
28535
- const qr = await renderQrTerminal(uri);
28840
+ const qr = await renderQrTerminal(pairingCode);
28536
28841
  process.stdout.write(`${qr}\n`);
28537
- process.stdout.write(`${uri}\n`);
28842
+ process.stdout.write(`${pairingCode}\n`);
28843
+ process.stdout.write(`${deepLink}\n`);
28538
28844
  process.stdout.write(`Daemon ${payload.daemonId} (${payload.displayName})\n`);
28539
28845
  process.stdout.write(`Expires ${payload.expiresAt}\n`);
28540
- const qrPath = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-qr.png');
28541
- await writeQrPng(uri, qrPath).catch(() => undefined);
28542
- process.stdout.write(`Saved QR ${qrPath}\n`);
28846
+ const runtime = createRuntimePaths(paths);
28847
+ (0,external_node_fs_namespaceObject.writeFileSync)(runtime.pairingCodePath, `${pairingCode}\n`, 'utf8');
28848
+ await writeQrPng(pairingCode, runtime.pairingQrPath).catch(() => undefined);
28849
+ process.stdout.write(`Saved QR ${runtime.pairingQrPath}\n`);
28850
+ process.stdout.write(`Saved code ${runtime.pairingCodePath}\n`);
28543
28851
  }
28544
28852
 
28545
28853
  async function cmdStatus(flags, paths) {
28546
- const port = Number(flags.port) || (loadConfigOrNull(paths)?.server?.port ?? 41234);
28854
+ const port = resolveRequestedPort(flags, paths);
28547
28855
  try {
28548
28856
  const resp = await fetch(`http://127.0.0.1:${port}/health`);
28549
28857
  if (!resp.ok) {
@@ -28562,13 +28870,49 @@ async function cmdStatus(flags, paths) {
28562
28870
  }
28563
28871
  }
28564
28872
 
28565
- async function cmdResetPairings(flags, paths) {
28566
- const port = Number(flags.port) || (loadConfigOrNull(paths)?.server?.port ?? 41234);
28567
- const resp = await fetch(`http://127.0.0.1:${port}/pair/reset`, { method: 'POST' });
28568
- if (!resp.ok) {
28569
- process.stderr.write(`[${PROG}] /pair/reset failed: ${resp.status} ${resp.statusText}\n`);
28873
+ async function waitForDaemonExit(port, timeoutMs = 5_000) {
28874
+ const deadline = Date.now() + timeoutMs;
28875
+ while (Date.now() < deadline) {
28876
+ const health = await probeHealth(port, 0);
28877
+ if (!health) {
28878
+ return true;
28879
+ }
28880
+ await new Promise((resolveWait) => setTimeout(resolveWait, 200));
28881
+ }
28882
+ return false;
28883
+ }
28884
+
28885
+ async function cmdStop(flags, paths) {
28886
+ const port = resolveRequestedPort(flags, paths);
28887
+ const runtime = createRuntimePaths(paths);
28888
+ const pid = readPidFile(runtime.pidPath);
28889
+ if (pid && remosidian_isProcessAlive(pid)) {
28890
+ try {
28891
+ process.kill(pid, 'SIGTERM');
28892
+ } catch {
28893
+ // Process may have exited between the liveness check and kill.
28894
+ }
28895
+ await waitForDaemonExit(port);
28896
+ removePidFile(runtime.pidPath);
28897
+ stopTunnel({ tunnelStatePath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'tunnel.json') });
28898
+ process.stdout.write('Remosidian daemon stopped.\n');
28899
+ return;
28900
+ }
28901
+ removePidFile(runtime.pidPath);
28902
+ const health = await probeHealth(port, 500);
28903
+ if (health) {
28904
+ process.stderr.write(
28905
+ `[${PROG}] daemon is responding on 127.0.0.1:${port}, but no background PID file exists. Stop the foreground terminal process that started it.\n`,
28906
+ );
28570
28907
  process.exit(1);
28571
28908
  }
28909
+ stopTunnel({ tunnelStatePath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'tunnel.json') });
28910
+ process.stdout.write('Remosidian daemon is not running.\n');
28911
+ }
28912
+
28913
+ async function cmdResetPairings(flags, paths) {
28914
+ const port = resolveRequestedPort(flags, paths);
28915
+ await postJson(port, '/pair/reset');
28572
28916
  process.stdout.write('All pairings revoked.\n');
28573
28917
  }
28574
28918
 
@@ -28642,12 +28986,18 @@ async function main() {
28642
28986
  case 'start':
28643
28987
  await cmdStart(parsed.flags, paths);
28644
28988
  return;
28989
+ case 'serve':
28990
+ await cmdServe(parsed.flags, paths);
28991
+ return;
28645
28992
  case 'pair':
28646
28993
  await cmdPair(parsed.flags, paths);
28647
28994
  return;
28648
28995
  case 'status':
28649
28996
  await cmdStatus(parsed.flags, paths);
28650
28997
  return;
28998
+ case 'stop':
28999
+ await cmdStop(parsed.flags, paths);
29000
+ return;
28651
29001
  case 'reset-pairings':
28652
29002
  await cmdResetPairings(parsed.flags, paths);
28653
29003
  return;
@@ -28660,8 +29010,9 @@ async function main() {
28660
29010
  }
28661
29011
  }
28662
29012
 
28663
- const entryHref = process.argv[1] ? new URL(`file://${(0,external_node_path_namespaceObject.resolve)(process.argv[1])}`).href : '';
28664
- if (import.meta.url === entryHref || import.meta.url === `file://${process.argv[1]}`) {
29013
+ const entryHref = resolveEntrypointHref(process.argv[1]);
29014
+ const moduleHref = resolveEntrypointHref((0,external_node_url_namespaceObject.fileURLToPath)(import.meta.url));
29015
+ if (entryHref && moduleHref === entryHref) {
28665
29016
  main().catch((error) => {
28666
29017
  process.stderr.write(`[${PROG}] ${error?.stack ?? error}\n`);
28667
29018
  process.exit(1);
@@ -28670,9 +29021,14 @@ if (import.meta.url === entryHref || import.meta.url === `file://${process.argv[
28670
29021
 
28671
29022
 
28672
29023
 
29024
+ var __webpack_exports__buildPairCode = __webpack_exports__.gu;
29025
+ var __webpack_exports__buildPairDeepLink = __webpack_exports__.ti;
28673
29026
  var __webpack_exports__buildPairUri = __webpack_exports__.lz;
29027
+ var __webpack_exports__buildServeArgs = __webpack_exports__.jO;
29028
+ var __webpack_exports__createCompactPairingPayload = __webpack_exports__.YP;
28674
29029
  var __webpack_exports__expandVaultSpec = __webpack_exports__.wX;
28675
29030
  var __webpack_exports__main = __webpack_exports__.iW;
28676
29031
  var __webpack_exports__parseArgs = __webpack_exports__.Zy;
29032
+ var __webpack_exports__resolveEntrypointHref = __webpack_exports__.Mf;
28677
29033
  var __webpack_exports__resolveInvocation = __webpack_exports__.t0;
28678
- export { __webpack_exports__buildPairUri as buildPairUri, __webpack_exports__expandVaultSpec as expandVaultSpec, __webpack_exports__main as main, __webpack_exports__parseArgs as parseArgs, __webpack_exports__resolveInvocation as resolveInvocation };
29034
+ export { __webpack_exports__buildPairCode as buildPairCode, __webpack_exports__buildPairDeepLink as buildPairDeepLink, __webpack_exports__buildPairUri as buildPairUri, __webpack_exports__buildServeArgs as buildServeArgs, __webpack_exports__createCompactPairingPayload as createCompactPairingPayload, __webpack_exports__expandVaultSpec as expandVaultSpec, __webpack_exports__main as main, __webpack_exports__parseArgs as parseArgs, __webpack_exports__resolveEntrypointHref as resolveEntrypointHref, __webpack_exports__resolveInvocation as resolveInvocation };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remosidian",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Run Remosidian — remote, read/write access to your Obsidian vault from any device",
5
5
  "license": "MIT",
6
6
  "homepage": "https://lifeos.vip",