remosidian 0.1.12 → 0.1.13

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.
@@ -15244,21 +15244,22 @@ var __webpack_exports__ = {};
15244
15244
 
15245
15245
  // EXPORTS
15246
15246
  __nccwpck_require__.d(__webpack_exports__, {
15247
- gu: () => (/* binding */ buildPairCode),
15248
- ti: () => (/* binding */ buildPairDeepLink),
15249
- lz: () => (/* binding */ buildPairUri),
15250
- jO: () => (/* binding */ buildServeArgs),
15251
- YP: () => (/* binding */ createCompactPairingPayload),
15247
+ gu: () => (/* reexport */ buildPairCode),
15248
+ ti: () => (/* reexport */ buildPairDeepLink),
15249
+ lz: () => (/* reexport */ buildPairUri),
15250
+ jO: () => (/* reexport */ buildServeArgs),
15251
+ YP: () => (/* reexport */ createCompactPairingPayload),
15252
15252
  wX: () => (/* binding */ expandVaultSpec),
15253
- NT: () => (/* binding */ hasLegacyAccessArgs),
15254
- BK: () => (/* binding */ helpText),
15253
+ NT: () => (/* reexport */ hasLegacyAccessArgs),
15254
+ BK: () => (/* reexport */ helpText),
15255
15255
  iW: () => (/* binding */ main),
15256
- RG: () => (/* binding */ normalizeCliFlags),
15257
- QN: () => (/* binding */ normalizePortValue),
15258
- bo: () => (/* binding */ parseAccessValue),
15259
- Zy: () => (/* binding */ parseArgs),
15256
+ RG: () => (/* reexport */ normalizeCliFlags),
15257
+ QN: () => (/* reexport */ normalizePortValue),
15258
+ bo: () => (/* reexport */ parseAccessValue),
15259
+ Zy: () => (/* reexport */ parseArgs),
15260
15260
  Mf: () => (/* binding */ resolveEntrypointHref),
15261
- t0: () => (/* binding */ resolveInvocation)
15261
+ t0: () => (/* reexport */ resolveInvocation),
15262
+ kr: () => (/* reexport */ resolvePersonalAgentOverride)
15262
15263
  });
15263
15264
 
15264
15265
  ;// CONCATENATED MODULE: external "node:child_process"
@@ -15275,6 +15276,203 @@ const promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.ur
15275
15276
  const external_node_url_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:url");
15276
15277
  // EXTERNAL MODULE: ../../node_modules/qrcode/lib/index.js
15277
15278
  var lib = __nccwpck_require__(7514);
15279
+ ;// CONCATENATED MODULE: ./bin/cli-pairing.mjs
15280
+
15281
+
15282
+
15283
+
15284
+ function cli_pairing_renderQrTerminal(payload) {
15285
+ return new Promise((resolveTerm, rejectTerm) => {
15286
+ lib.toString(payload, { type: 'terminal', small: true, errorCorrectionLevel: 'M' }, (err, str) => {
15287
+ if (err) {
15288
+ rejectTerm(err);
15289
+ return;
15290
+ }
15291
+ resolveTerm(str);
15292
+ });
15293
+ });
15294
+ }
15295
+
15296
+ async function cli_pairing_writeQrPng(payload, filePath) {
15297
+ (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(filePath), { recursive: true });
15298
+ await lib.toFile(filePath, payload, { type: 'png', errorCorrectionLevel: 'M', margin: 1, scale: 8 });
15299
+ }
15300
+
15301
+ function printHumanHealth(health) {
15302
+ const lines = [];
15303
+ lines.push(`Daemon ${health.daemonId} (${health.displayName})`);
15304
+ lines.push(`Version ${health.version}`);
15305
+ if (health.runtime) {
15306
+ lines.push(`Runtime ${health.runtime === 'cli' ? 'CLI' : 'Obsidian plugin'}`);
15307
+ }
15308
+ lines.push(`Listening http://${health.canonicalHost}:${health.port}`);
15309
+ if (health.relayUrl) {
15310
+ lines.push(`Relay ${health.relayUrl}`);
15311
+ }
15312
+ if (Array.isArray(health.fallbackHosts) && health.fallbackHosts.length > 0) {
15313
+ lines.push(`Fallbacks ${health.fallbackHosts.join(', ')}`);
15314
+ }
15315
+ if (Array.isArray(health.connections) && health.connections.length > 0) {
15316
+ lines.push('Connections:');
15317
+ for (const conn of health.connections) {
15318
+ const proto = conn.secure ? 'wss' : 'ws';
15319
+ lines.push(
15320
+ conn.source === 'remoteDirect'
15321
+ ? ' - Remote Direct (stable)'
15322
+ : ` - ${proto}://${conn.host}:${conn.port} (${conn.source}, ${conn.stability})`,
15323
+ );
15324
+ }
15325
+ }
15326
+ if (Array.isArray(health.vaults) && health.vaults.length > 0) {
15327
+ lines.push('Vaults:');
15328
+ for (const vault of health.vaults) {
15329
+ lines.push(` - ${vault.id}: ${vault.root ?? '(unknown root)'}`);
15330
+ }
15331
+ } else {
15332
+ lines.push('Vaults: (none configured — pass --vault or run `remosidian init`)');
15333
+ }
15334
+ lines.push(`Paired ${health.pairedDevices ?? 0} device(s)`);
15335
+ lines.push(`Personal Agent ${health.features?.personalAgent === true ? 'shown' : 'hidden'}`);
15336
+ if (health.ai?.configured) {
15337
+ lines.push(`AI ${health.ai.agent ?? 'agent'} (local CLI)`);
15338
+ } else {
15339
+ lines.push('AI (no claude / codex CLI on PATH — install one to enable Ask)');
15340
+ }
15341
+ return lines.join('\n');
15342
+ }
15343
+
15344
+ function buildPairUri(payload) {
15345
+ // Emit the full schema so @lifeos/client-core's default parser picks it
15346
+ // up. We chose JSON-over-base64url because it survives clipboard paste,
15347
+ // deep links, and QR data URIs without escaping headaches.
15348
+ const full = {
15349
+ type: payload.type,
15350
+ daemonId: payload.daemonId,
15351
+ displayName: payload.displayName,
15352
+ canonicalHost: payload.canonicalHost,
15353
+ port: payload.port,
15354
+ ...(payload.fallbackHosts && payload.fallbackHosts.length > 0 ? { fallbackHosts: payload.fallbackHosts } : {}),
15355
+ ...(payload.connections && payload.connections.length > 0 ? { connections: payload.connections } : {}),
15356
+ pairNonce: payload.pairNonce,
15357
+ expiresAt: payload.expiresAt,
15358
+ ...(payload.runtime ? { runtime: payload.runtime } : {}),
15359
+ ...(payload.vaults && payload.vaults.length > 0 ? { vaults: payload.vaults } : {}),
15360
+ };
15361
+ const encoded = Buffer.from(JSON.stringify(full), 'utf8').toString('base64url');
15362
+ return `remosidian://pair?v=1&data=${encoded}`;
15363
+ }
15364
+
15365
+ function normalizePairingConnections(payload) {
15366
+ const explicitConnections = Array.isArray(payload.connections)
15367
+ ? payload.connections
15368
+ .filter(
15369
+ (target) =>
15370
+ target &&
15371
+ typeof target.host === 'string' &&
15372
+ Number.isFinite(target.port) &&
15373
+ typeof target.source === 'string' &&
15374
+ typeof target.stability === 'string',
15375
+ )
15376
+ .map((target) => ({
15377
+ kind: target.kind === 'relay' ? 'relay' : 'direct',
15378
+ host: target.host.trim(),
15379
+ port: Math.floor(Number(target.port)),
15380
+ ...(target.secure === true ? { secure: true } : {}),
15381
+ ...(target.remoteDirect ? { remoteDirect: target.remoteDirect } : {}),
15382
+ source: target.source,
15383
+ stability: target.stability,
15384
+ }))
15385
+ .filter((target) => target.host && target.port > 0)
15386
+ : [];
15387
+ if (explicitConnections.length > 0) {
15388
+ return explicitConnections;
15389
+ }
15390
+ const fallbackHosts = Array.isArray(payload.fallbackHosts)
15391
+ ? payload.fallbackHosts.filter((host) => typeof host === 'string' && host.trim()).map((host) => host.trim())
15392
+ : [];
15393
+ const port = Number.isFinite(payload.port) && payload.port > 0 ? Math.floor(payload.port) : 41234;
15394
+ return [
15395
+ {
15396
+ kind: 'direct',
15397
+ host: payload.canonicalHost,
15398
+ port,
15399
+ source: 'configured_host',
15400
+ stability: 'stable',
15401
+ },
15402
+ ...fallbackHosts.map((host) => ({
15403
+ kind: 'direct',
15404
+ host,
15405
+ port,
15406
+ source: 'fallback',
15407
+ stability: 'stable',
15408
+ })),
15409
+ ];
15410
+ }
15411
+
15412
+ function createCompactPairingPayload(payload) {
15413
+ const compact = {
15414
+ t: 'rp',
15415
+ d: payload.daemonId,
15416
+ n: payload.displayName,
15417
+ h: payload.canonicalHost,
15418
+ p: payload.port,
15419
+ c: payload.pairNonce,
15420
+ e: payload.expiresAt,
15421
+ };
15422
+ if (Array.isArray(payload.fallbackHosts) && payload.fallbackHosts.length > 0) {
15423
+ compact.f = payload.fallbackHosts;
15424
+ }
15425
+ const connections = normalizePairingConnections(payload);
15426
+ if (connections.length > 0) {
15427
+ compact.o = connections.map((target) => [
15428
+ target.kind,
15429
+ target.host,
15430
+ target.port,
15431
+ target.source,
15432
+ target.stability,
15433
+ target.secure === true,
15434
+ ...(target.remoteDirect ? [target.remoteDirect.serverAddress, target.remoteDirect.remotePort] : []),
15435
+ ]);
15436
+ }
15437
+ if (payload.runtime) {
15438
+ compact.r = payload.runtime;
15439
+ }
15440
+ if (Array.isArray(payload.vaults) && payload.vaults.length > 0) {
15441
+ compact.v = payload.vaults;
15442
+ }
15443
+ return compact;
15444
+ }
15445
+
15446
+ function buildPairCode(payload) {
15447
+ return JSON.stringify(createCompactPairingPayload(payload));
15448
+ }
15449
+
15450
+ function buildPairDeepLink(pairingCode) {
15451
+ return `remosidian://pair?data=${encodeURIComponent(pairingCode)}`;
15452
+ }
15453
+
15454
+ function formatConnectionSummary(target) {
15455
+ if (target.source === 'remoteDirect') return 'Remote Direct (stable)';
15456
+ const scheme = target.secure === true || target.port === 443 ? 'https' : 'http';
15457
+ return `${scheme}://${target.host}:${target.port} (${target.source}, ${target.stability})`;
15458
+ }
15459
+
15460
+ async function printPairingQr(pairingPayload, paths) {
15461
+ const runtime = paths;
15462
+ const pairingCode = buildPairCode(pairingPayload);
15463
+ const deepLink = buildPairDeepLink(pairingCode);
15464
+ const qr = await cli_pairing_renderQrTerminal(pairingCode);
15465
+ (0,external_node_fs_namespaceObject.writeFileSync)(runtime.pairingCodePath, `${pairingCode}\n`, 'utf8');
15466
+ await cli_pairing_writeQrPng(pairingCode, runtime.pairingQrPath).catch(() => undefined);
15467
+ process.stdout.write(`\nScan this QR code with the Remosidian app:\n\n${qr}\n`);
15468
+ process.stdout.write(`If the terminal QR is hard to scan, open this PNG instead:\n${runtime.pairingQrPath}\n`);
15469
+ process.stdout.write(`If this device cannot scan, paste this pairing code in Remosidian:\n${pairingCode}\n`);
15470
+ process.stdout.write(`Pairing code saved to:\n${runtime.pairingCodePath}\n`);
15471
+ process.stdout.write(`Or open this deep link on a device with Remosidian installed:\n${deepLink}\n`);
15472
+ }
15473
+
15474
+
15475
+
15278
15476
  ;// CONCATENATED MODULE: ../../packages/remosidian-server/dist/paths.js
15279
15477
 
15280
15478
 
@@ -15338,6 +15536,18 @@ function isTruthyEnv(value) {
15338
15536
  const normalized = value.trim().toLowerCase();
15339
15537
  return normalized === '' || normalized === '1' || normalized === 'true' || normalized === 'yes';
15340
15538
  }
15539
+ function readBoolean(value, fallback = false) {
15540
+ if (typeof value === 'boolean')
15541
+ return value;
15542
+ if (typeof value !== 'string')
15543
+ return fallback;
15544
+ const normalized = value.trim().toLowerCase();
15545
+ if (['1', 'true', 'yes', 'on'].includes(normalized))
15546
+ return true;
15547
+ if (['0', 'false', 'no', 'off'].includes(normalized))
15548
+ return false;
15549
+ return fallback;
15550
+ }
15341
15551
  function getNonBlankPath(value) {
15342
15552
  return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
15343
15553
  }
@@ -15426,6 +15636,8 @@ function normalizeRemosidianConfig(value) {
15426
15636
  : typeof server.relayUrl === 'string'
15427
15637
  ? server.relayUrl.trim()
15428
15638
  : '';
15639
+ const rawFeatures = server.features && typeof server.features === 'object' ? server.features : {};
15640
+ const personalAgent = readBoolean(process.env.REMOSIDIAN_PERSONAL_AGENT, readBoolean(rawFeatures.personalAgent));
15429
15641
  const vaults = Array.isArray(raw.vaults)
15430
15642
  ? raw.vaults.map(normalizeVault).filter((vault) => Boolean(vault))
15431
15643
  : [];
@@ -15436,6 +15648,7 @@ function normalizeRemosidianConfig(value) {
15436
15648
  port: readPort(envPort ?? server.port, readPort(server.port, DEFAULT_REMOSIDIAN_PORT)),
15437
15649
  advertisedHosts,
15438
15650
  ...(relayUrl ? { relayUrl } : {}),
15651
+ features: { personalAgent },
15439
15652
  },
15440
15653
  ...(ai ? { ai } : {}),
15441
15654
  };
@@ -15452,225 +15665,32 @@ function loadRemosidianConfig(configPath = createRemosidianPaths().configPath) {
15452
15665
  //# sourceMappingURL=config.js.map
15453
15666
  ;// CONCATENATED MODULE: external "node:http"
15454
15667
  const external_node_http_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:http");
15455
- ;// CONCATENATED MODULE: ../../packages/daemon-core/dist/auth.js
15456
- function firstHeaderValue(value) {
15457
- if (Array.isArray(value))
15458
- return value[0];
15459
- return value;
15460
- }
15461
- function extractClientFingerprint(req) {
15462
- const forwardedFor = firstHeaderValue(req.headers["x-forwarded-for"]);
15463
- const cfConnectingIp = firstHeaderValue(req.headers["cf-connecting-ip"]);
15464
- const realIp = firstHeaderValue(req.headers["x-real-ip"]);
15465
- const ip = forwardedFor?.split(",")[0]?.trim() ||
15466
- cfConnectingIp ||
15467
- realIp ||
15468
- req.socket.remoteAddress ||
15469
- undefined;
15470
- const userAgent = firstHeaderValue(req.headers["user-agent"]);
15471
- return {
15472
- ...(ip ? { ip } : {}),
15473
- ...(userAgent ? { userAgent } : {}),
15474
- };
15668
+ ;// CONCATENATED MODULE: ../../packages/daemon-core/dist/advertised-hosts.js
15669
+
15670
+
15671
+ function isIpv4(address) {
15672
+ const parts = address.split('.');
15673
+ if (parts.length !== 4)
15674
+ return false;
15675
+ return parts.every((part) => /^\d+$/.test(part) && Number(part) >= 0 && Number(part) <= 255);
15475
15676
  }
15476
- function isLoopbackAddress(address) {
15477
- if (!address)
15677
+ function isSharedIpv4(address) {
15678
+ if (!isIpv4(address))
15478
15679
  return false;
15479
- return (address === "127.0.0.1" ||
15480
- address === "::1" ||
15481
- address === "::ffff:127.0.0.1");
15680
+ const [first, second] = address.split('.').map(Number);
15681
+ return first === 100 && second >= 64 && second <= 127;
15482
15682
  }
15483
- const PROXY_HEADER_NAMES = [
15484
- "x-forwarded-for",
15485
- "x-forwarded-host",
15486
- "x-forwarded-proto",
15487
- "x-real-ip",
15488
- "forwarded",
15489
- "via",
15490
- "cf-connecting-ip",
15491
- "cf-ray",
15492
- ];
15493
- function isProxiedRequest(headers) {
15494
- for (const name of PROXY_HEADER_NAMES) {
15495
- if (headers[name] !== undefined)
15496
- return true;
15497
- }
15498
- return false;
15683
+ function isLinkLocalIpv4(address) {
15684
+ if (!isIpv4(address))
15685
+ return false;
15686
+ const [first, second] = address.split('.').map(Number);
15687
+ return first === 169 && second === 254;
15499
15688
  }
15500
- function isTrustedLocalRequest(req) {
15501
- return (isLoopbackAddress(req.socket.remoteAddress) &&
15502
- !isProxiedRequest(req.headers));
15503
- }
15504
- function readBearerToken(header) {
15505
- if (!header)
15506
- return null;
15507
- const match = header.match(/^Bearer\s+(.+)$/i);
15508
- return match?.[1]?.trim() || null;
15509
- }
15510
- function resolveRequestToken(authHeader, queryToken) {
15511
- return readBearerToken(authHeader) ?? queryToken;
15512
- }
15513
- function isLegacyToken(token, legacyToken) {
15514
- return Boolean(token && legacyToken && token === legacyToken);
15515
- }
15516
- function isAuthorizedToken(token, legacyToken, validatePairToken, touch = true) {
15517
- if (!token)
15518
- return false;
15519
- if (isLegacyToken(token, legacyToken))
15520
- return true;
15521
- return Boolean(validatePairToken(token, touch));
15522
- }
15523
- //# sourceMappingURL=auth.js.map
15524
- ;// CONCATENATED MODULE: external "node:crypto"
15525
- const external_node_crypto_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:crypto");
15526
- ;// CONCATENATED MODULE: ../../packages/daemon-core/dist/identity.js
15527
-
15528
-
15529
-
15530
-
15531
-
15532
- function base64Url(bytes) {
15533
- return (0,external_node_crypto_namespaceObject.randomBytes)(bytes).toString('base64url');
15534
- }
15535
- function buildDaemonId() {
15536
- return `d_${base64Url(12)}`;
15537
- }
15538
- function defaultDisplayName(options) {
15539
- return options?.systemHostname ?? (0,external_node_os_namespaceObject.hostname)();
15540
- }
15541
- function runHostnameCommand(command, args) {
15542
- return (0,external_node_child_process_namespaceObject.execFileSync)(command, args, {
15543
- encoding: 'utf8',
15544
- stdio: ['ignore', 'pipe', 'ignore'],
15545
- timeout: 1_000,
15546
- });
15547
- }
15548
- function normalizeMdnsHostname(value) {
15549
- const raw = value.trim().replace(/\.$/, '').toLowerCase();
15550
- if (!raw)
15551
- return null;
15552
- const firstLabel = raw.endsWith('.local') ? raw.slice(0, -6) : raw.split('.')[0];
15553
- const label = firstLabel
15554
- .normalize('NFKD')
15555
- .replace(/[^a-z0-9-]+/g, '-')
15556
- .replace(/^-+|-+$/g, '')
15557
- .slice(0, 63);
15558
- return label ? `${label}.local` : null;
15559
- }
15560
- /**
15561
- * Discover the platform's real local host label before projecting it into the
15562
- * mDNS `.local` namespace. macOS has a dedicated Bonjour LocalHostName that
15563
- * can differ from `os.hostname()` on managed machines. Linux prefers its
15564
- * configured static hostname; Windows' `os.hostname()` reflects COMPUTERNAME.
15565
- * Whether another device can resolve the resulting name is verified by the
15566
- * client after an IP pairing succeeds.
15567
- */
15568
- function discoverPlatformLocalHostname(options = {}) {
15569
- const platform = options.platform ?? process.platform;
15570
- const systemHostname = options.systemHostname ?? (0,external_node_os_namespaceObject.hostname)();
15571
- const runCommand = options.runCommand ?? runHostnameCommand;
15572
- const command = platform === 'darwin'
15573
- ? ['scutil', ['--get', 'LocalHostName']]
15574
- : platform === 'linux'
15575
- ? ['hostnamectl', ['--static']]
15576
- : null;
15577
- if (command) {
15578
- try {
15579
- const discovered = normalizeMdnsHostname(runCommand(command[0], [...command[1]]));
15580
- if (discovered)
15581
- return discovered;
15582
- }
15583
- catch {
15584
- // Minimal Linux containers and older macOS installations may not have
15585
- // the platform helper. `os.hostname()` remains the portable fallback.
15586
- }
15587
- }
15588
- return normalizeMdnsHostname(systemHostname) ?? 'localhost';
15589
- }
15590
- function defaultCanonicalHost(options) {
15591
- return discoverPlatformLocalHostname(options);
15592
- }
15593
- function createIdentity(port, hostnameOptions) {
15594
- return {
15595
- daemonId: buildDaemonId(),
15596
- daemonSecret: base64Url(32),
15597
- displayName: defaultDisplayName(hostnameOptions),
15598
- canonicalHost: defaultCanonicalHost(hostnameOptions),
15599
- port,
15600
- createdAt: new Date().toISOString(),
15601
- };
15602
- }
15603
- function writeIdentity(identity, identityPath) {
15604
- (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(identityPath), { recursive: true });
15605
- (0,external_node_fs_namespaceObject.writeFileSync)(identityPath, `${JSON.stringify(identity, null, 2)}\n`, 'utf8');
15606
- }
15607
- function loadOrCreateDaemonIdentity(port, identityPath, hostnameOptions) {
15608
- const discoveredCanonicalHost = defaultCanonicalHost(hostnameOptions);
15609
- try {
15610
- const raw = (0,external_node_fs_namespaceObject.readFileSync)(identityPath, 'utf8');
15611
- const parsed = JSON.parse(raw);
15612
- if (typeof parsed.daemonId === 'string' &&
15613
- typeof parsed.daemonSecret === 'string' &&
15614
- typeof parsed.displayName === 'string' &&
15615
- typeof parsed.canonicalHost === 'string' &&
15616
- typeof parsed.createdAt === 'string') {
15617
- const identity = {
15618
- daemonId: parsed.daemonId,
15619
- daemonSecret: parsed.daemonSecret,
15620
- displayName: parsed.displayName,
15621
- canonicalHost: parsed.canonicalHost,
15622
- port: typeof parsed.port === 'number' && Number.isFinite(parsed.port) ? parsed.port : port,
15623
- createdAt: parsed.createdAt,
15624
- };
15625
- const legacyGeneratedHost = normalizeMdnsHostname(parsed.displayName);
15626
- if (legacyGeneratedHost &&
15627
- identity.canonicalHost.toLowerCase() === legacyGeneratedHost &&
15628
- identity.canonicalHost !== discoveredCanonicalHost) {
15629
- identity.canonicalHost = discoveredCanonicalHost;
15630
- }
15631
- if (identity.port !== port) {
15632
- identity.port = port;
15633
- }
15634
- if (identity.port !== parsed.port || identity.canonicalHost !== parsed.canonicalHost) {
15635
- writeIdentity(identity, identityPath);
15636
- }
15637
- return identity;
15638
- }
15639
- }
15640
- catch {
15641
- // Fall through to create a new identity.
15642
- }
15643
- const identity = createIdentity(port, hostnameOptions);
15644
- writeIdentity(identity, identityPath);
15645
- return identity;
15646
- }
15647
- //# sourceMappingURL=identity.js.map
15648
- ;// CONCATENATED MODULE: ../../packages/daemon-core/dist/advertised-hosts.js
15649
-
15650
-
15651
- function isIpv4(address) {
15652
- const parts = address.split('.');
15653
- if (parts.length !== 4)
15654
- return false;
15655
- return parts.every((part) => /^\d+$/.test(part) && Number(part) >= 0 && Number(part) <= 255);
15656
- }
15657
- function isSharedIpv4(address) {
15658
- if (!isIpv4(address))
15659
- return false;
15660
- const [first, second] = address.split('.').map(Number);
15661
- return first === 100 && second >= 64 && second <= 127;
15662
- }
15663
- function isLinkLocalIpv4(address) {
15664
- if (!isIpv4(address))
15665
- return false;
15666
- const [first, second] = address.split('.').map(Number);
15667
- return first === 169 && second === 254;
15668
- }
15669
- function isBenchmarkIpv4(address) {
15670
- if (!isIpv4(address))
15671
- return false;
15672
- const [first, second] = address.split('.').map(Number);
15673
- return first === 198 && (second === 18 || second === 19);
15689
+ function isBenchmarkIpv4(address) {
15690
+ if (!isIpv4(address))
15691
+ return false;
15692
+ const [first, second] = address.split('.').map(Number);
15693
+ return first === 198 && (second === 18 || second === 19);
15674
15694
  }
15675
15695
  function isPrivateIpv4(address) {
15676
15696
  if (!isIpv4(address))
@@ -15959,139 +15979,199 @@ function computeAdvertisedHosts(options) {
15959
15979
  };
15960
15980
  }
15961
15981
  //# sourceMappingURL=advertised-hosts.js.map
15962
- ;// CONCATENATED MODULE: ../../packages/daemon-core/dist/pairing-store.js
15963
-
15964
-
15965
-
15966
- function pairing_store_base64Url(bytes) {
15967
- return (0,external_node_crypto_namespaceObject.randomBytes)(bytes).toString("base64url");
15982
+ ;// CONCATENATED MODULE: ../../packages/daemon-core/dist/auth.js
15983
+ function firstHeaderValue(value) {
15984
+ if (Array.isArray(value))
15985
+ return value[0];
15986
+ return value;
15968
15987
  }
15969
- function hashToken(token) {
15970
- return (0,external_node_crypto_namespaceObject.createHash)("sha256").update(token).digest("hex");
15988
+ function extractClientFingerprint(req) {
15989
+ const forwardedFor = firstHeaderValue(req.headers["x-forwarded-for"]);
15990
+ const cfConnectingIp = firstHeaderValue(req.headers["cf-connecting-ip"]);
15991
+ const realIp = firstHeaderValue(req.headers["x-real-ip"]);
15992
+ const ip = forwardedFor?.split(",")[0]?.trim() ||
15993
+ cfConnectingIp ||
15994
+ realIp ||
15995
+ req.socket.remoteAddress ||
15996
+ undefined;
15997
+ const userAgent = firstHeaderValue(req.headers["user-agent"]);
15998
+ return {
15999
+ ...(ip ? { ip } : {}),
16000
+ ...(userAgent ? { userAgent } : {}),
16001
+ };
15971
16002
  }
15972
- function sameHash(left, right) {
15973
- const leftBuffer = Buffer.from(left);
15974
- const rightBuffer = Buffer.from(right);
15975
- if (leftBuffer.length !== rightBuffer.length)
16003
+ function isLoopbackAddress(address) {
16004
+ if (!address)
15976
16005
  return false;
15977
- return (0,external_node_crypto_namespaceObject.timingSafeEqual)(leftBuffer, rightBuffer);
16006
+ return (address === "127.0.0.1" ||
16007
+ address === "::1" ||
16008
+ address === "::ffff:127.0.0.1");
15978
16009
  }
15979
- function readRecords(pairingsPath) {
15980
- try {
15981
- const raw = (0,external_node_fs_namespaceObject.readFileSync)(pairingsPath, "utf8");
15982
- const parsed = JSON.parse(raw);
15983
- if (!Array.isArray(parsed))
15984
- return [];
15985
- return parsed.filter((entry) => entry &&
15986
- typeof entry.deviceId === "string" &&
15987
- typeof entry.deviceName === "string" &&
15988
- typeof entry.tokenHash === "string" &&
15989
- typeof entry.createdAt === "string" &&
15990
- typeof entry.lastSeenAt === "string" &&
15991
- (typeof entry.revokedAt === "string" || entry.revokedAt === null));
15992
- }
15993
- catch {
15994
- return [];
16010
+ const PROXY_HEADER_NAMES = [
16011
+ "x-forwarded-for",
16012
+ "x-forwarded-host",
16013
+ "x-forwarded-proto",
16014
+ "x-real-ip",
16015
+ "forwarded",
16016
+ "via",
16017
+ "cf-connecting-ip",
16018
+ "cf-ray",
16019
+ ];
16020
+ function isProxiedRequest(headers) {
16021
+ for (const name of PROXY_HEADER_NAMES) {
16022
+ if (headers[name] !== undefined)
16023
+ return true;
15995
16024
  }
16025
+ return false;
15996
16026
  }
15997
- function writeRecords(pairingsPath, records) {
15998
- (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(pairingsPath), { recursive: true });
15999
- (0,external_node_fs_namespaceObject.writeFileSync)(pairingsPath, `${JSON.stringify(records, null, 2)}\n`, "utf8");
16027
+ function isTrustedLocalRequest(req) {
16028
+ return (isLoopbackAddress(req.socket.remoteAddress) &&
16029
+ !isProxiedRequest(req.headers));
16000
16030
  }
16001
- class PairingStore {
16002
- pairingsPath;
16003
- records;
16004
- windows = new Map();
16005
- constructor(pairingsPath) {
16006
- this.pairingsPath = pairingsPath;
16007
- this.records = readRecords(pairingsPath);
16008
- }
16009
- list() {
16010
- return this.records.slice();
16011
- }
16012
- pairedCount() {
16013
- return this.records.filter((record) => !record.revokedAt).length;
16014
- }
16015
- openWindow(windowMs = 5 * 60_000, reusable = false) {
16016
- const pairingId = `pair_${pairing_store_base64Url(8)}`;
16017
- const pairNonce = pairing_store_base64Url(24);
16018
- const expiresAt = new Date(Date.now() + windowMs).toISOString();
16019
- const window = { pairingId, pairNonce, expiresAt, reusable };
16020
- this.windows.set(pairNonce, window);
16021
- return window;
16022
- }
16023
- consumeWindow(pairNonce) {
16024
- const window = this.windows.get(pairNonce);
16025
- if (!window)
16026
- return null;
16027
- if (!window.reusable) {
16028
- this.windows.delete(pairNonce);
16029
- }
16030
- if (new Date(window.expiresAt).getTime() < Date.now()) {
16031
- return null;
16032
- }
16033
- return window;
16034
- }
16035
- issuePairToken(deviceId, deviceName) {
16036
- const now = new Date().toISOString();
16037
- const pairToken = pairing_store_base64Url(32);
16038
- const nextRecord = {
16039
- deviceId,
16040
- deviceName,
16041
- tokenHash: hashToken(pairToken),
16042
- createdAt: now,
16043
- lastSeenAt: now,
16044
- revokedAt: null,
16045
- };
16046
- const existingIndex = this.records.findIndex((record) => record.deviceId === deviceId);
16047
- if (existingIndex >= 0) {
16048
- this.records[existingIndex] = nextRecord;
16049
- }
16050
- else {
16051
- this.records.unshift(nextRecord);
16031
+ function readBearerToken(header) {
16032
+ if (!header)
16033
+ return null;
16034
+ const match = header.match(/^Bearer\s+(.+)$/i);
16035
+ return match?.[1]?.trim() || null;
16036
+ }
16037
+ function resolveRequestToken(authHeader, queryToken) {
16038
+ return readBearerToken(authHeader) ?? queryToken;
16039
+ }
16040
+ function isLegacyToken(token, legacyToken) {
16041
+ return Boolean(token && legacyToken && token === legacyToken);
16042
+ }
16043
+ function isAuthorizedToken(token, legacyToken, validatePairToken, touch = true) {
16044
+ if (!token)
16045
+ return false;
16046
+ if (isLegacyToken(token, legacyToken))
16047
+ return true;
16048
+ return Boolean(validatePairToken(token, touch));
16049
+ }
16050
+ //# sourceMappingURL=auth.js.map
16051
+ ;// CONCATENATED MODULE: external "node:crypto"
16052
+ const external_node_crypto_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:crypto");
16053
+ ;// CONCATENATED MODULE: ../../packages/daemon-core/dist/identity.js
16054
+
16055
+
16056
+
16057
+
16058
+
16059
+ function base64Url(bytes) {
16060
+ return (0,external_node_crypto_namespaceObject.randomBytes)(bytes).toString('base64url');
16061
+ }
16062
+ function buildDaemonId() {
16063
+ return `d_${base64Url(12)}`;
16064
+ }
16065
+ function defaultDisplayName(options) {
16066
+ return options?.systemHostname ?? (0,external_node_os_namespaceObject.hostname)();
16067
+ }
16068
+ function runHostnameCommand(command, args) {
16069
+ return (0,external_node_child_process_namespaceObject.execFileSync)(command, args, {
16070
+ encoding: 'utf8',
16071
+ stdio: ['ignore', 'pipe', 'ignore'],
16072
+ timeout: 1_000,
16073
+ });
16074
+ }
16075
+ function normalizeMdnsHostname(value) {
16076
+ const raw = value.trim().replace(/\.$/, '').toLowerCase();
16077
+ if (!raw)
16078
+ return null;
16079
+ const firstLabel = raw.endsWith('.local') ? raw.slice(0, -6) : raw.split('.')[0];
16080
+ const label = firstLabel
16081
+ .normalize('NFKD')
16082
+ .replace(/[^a-z0-9-]+/g, '-')
16083
+ .replace(/^-+|-+$/g, '')
16084
+ .slice(0, 63);
16085
+ return label ? `${label}.local` : null;
16086
+ }
16087
+ /**
16088
+ * Discover the platform's real local host label before projecting it into the
16089
+ * mDNS `.local` namespace. macOS has a dedicated Bonjour LocalHostName that
16090
+ * can differ from `os.hostname()` on managed machines. Linux prefers its
16091
+ * configured static hostname; Windows' `os.hostname()` reflects COMPUTERNAME.
16092
+ * Whether another device can resolve the resulting name is verified by the
16093
+ * client after an IP pairing succeeds.
16094
+ */
16095
+ function discoverPlatformLocalHostname(options = {}) {
16096
+ const platform = options.platform ?? process.platform;
16097
+ const systemHostname = options.systemHostname ?? (0,external_node_os_namespaceObject.hostname)();
16098
+ const runCommand = options.runCommand ?? runHostnameCommand;
16099
+ const command = platform === 'darwin'
16100
+ ? ['scutil', ['--get', 'LocalHostName']]
16101
+ : platform === 'linux'
16102
+ ? ['hostnamectl', ['--static']]
16103
+ : null;
16104
+ if (command) {
16105
+ try {
16106
+ const discovered = normalizeMdnsHostname(runCommand(command[0], [...command[1]]));
16107
+ if (discovered)
16108
+ return discovered;
16052
16109
  }
16053
- writeRecords(this.pairingsPath, this.records);
16054
- return pairToken;
16055
- }
16056
- validatePairToken(deviceId, token, touch = true) {
16057
- const record = this.records.find((entry) => entry.deviceId === deviceId && !entry.revokedAt);
16058
- if (!record)
16059
- return false;
16060
- const tokenDigest = hashToken(token);
16061
- if (!sameHash(record.tokenHash, tokenDigest))
16062
- return false;
16063
- if (touch) {
16064
- record.lastSeenAt = new Date().toISOString();
16065
- writeRecords(this.pairingsPath, this.records);
16110
+ catch {
16111
+ // Minimal Linux containers and older macOS installations may not have
16112
+ // the platform helper. `os.hostname()` remains the portable fallback.
16066
16113
  }
16067
- return true;
16068
16114
  }
16069
- validateAnyPairToken(token, touch = true) {
16070
- const tokenDigest = hashToken(token);
16071
- const record = this.records.find((entry) => !entry.revokedAt && sameHash(entry.tokenHash, tokenDigest));
16072
- if (!record)
16073
- return null;
16074
- if (touch) {
16075
- record.lastSeenAt = new Date().toISOString();
16076
- writeRecords(this.pairingsPath, this.records);
16115
+ return normalizeMdnsHostname(systemHostname) ?? 'localhost';
16116
+ }
16117
+ function defaultCanonicalHost(options) {
16118
+ return discoverPlatformLocalHostname(options);
16119
+ }
16120
+ function createIdentity(port, hostnameOptions) {
16121
+ return {
16122
+ daemonId: buildDaemonId(),
16123
+ daemonSecret: base64Url(32),
16124
+ displayName: defaultDisplayName(hostnameOptions),
16125
+ canonicalHost: defaultCanonicalHost(hostnameOptions),
16126
+ port,
16127
+ createdAt: new Date().toISOString(),
16128
+ };
16129
+ }
16130
+ function writeIdentity(identity, identityPath) {
16131
+ (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(identityPath), { recursive: true });
16132
+ (0,external_node_fs_namespaceObject.writeFileSync)(identityPath, `${JSON.stringify(identity, null, 2)}\n`, 'utf8');
16133
+ }
16134
+ function loadOrCreateDaemonIdentity(port, identityPath, hostnameOptions) {
16135
+ const discoveredCanonicalHost = defaultCanonicalHost(hostnameOptions);
16136
+ try {
16137
+ const raw = (0,external_node_fs_namespaceObject.readFileSync)(identityPath, 'utf8');
16138
+ const parsed = JSON.parse(raw);
16139
+ if (typeof parsed.daemonId === 'string' &&
16140
+ typeof parsed.daemonSecret === 'string' &&
16141
+ typeof parsed.displayName === 'string' &&
16142
+ typeof parsed.canonicalHost === 'string' &&
16143
+ typeof parsed.createdAt === 'string') {
16144
+ const identity = {
16145
+ daemonId: parsed.daemonId,
16146
+ daemonSecret: parsed.daemonSecret,
16147
+ displayName: parsed.displayName,
16148
+ canonicalHost: parsed.canonicalHost,
16149
+ port: typeof parsed.port === 'number' && Number.isFinite(parsed.port) ? parsed.port : port,
16150
+ createdAt: parsed.createdAt,
16151
+ };
16152
+ const legacyGeneratedHost = normalizeMdnsHostname(parsed.displayName);
16153
+ if (legacyGeneratedHost &&
16154
+ identity.canonicalHost.toLowerCase() === legacyGeneratedHost &&
16155
+ identity.canonicalHost !== discoveredCanonicalHost) {
16156
+ identity.canonicalHost = discoveredCanonicalHost;
16157
+ }
16158
+ if (identity.port !== port) {
16159
+ identity.port = port;
16160
+ }
16161
+ if (identity.port !== parsed.port || identity.canonicalHost !== parsed.canonicalHost) {
16162
+ writeIdentity(identity, identityPath);
16163
+ }
16164
+ return identity;
16077
16165
  }
16078
- return record;
16079
- }
16080
- revoke(deviceId) {
16081
- const record = this.records.find((entry) => entry.deviceId === deviceId && !entry.revokedAt);
16082
- if (!record)
16083
- return false;
16084
- record.revokedAt = new Date().toISOString();
16085
- writeRecords(this.pairingsPath, this.records);
16086
- return true;
16087
16166
  }
16088
- reset() {
16089
- this.records = [];
16090
- this.windows.clear();
16091
- writeRecords(this.pairingsPath, this.records);
16167
+ catch {
16168
+ // Fall through to create a new identity.
16092
16169
  }
16170
+ const identity = createIdentity(port, hostnameOptions);
16171
+ writeIdentity(identity, identityPath);
16172
+ return identity;
16093
16173
  }
16094
- //# sourceMappingURL=pairing-store.js.map
16174
+ //# sourceMappingURL=identity.js.map
16095
16175
  ;// CONCATENATED MODULE: ../../packages/daemon-core/dist/pairing-http.js
16096
16176
  // Reusable HTTP pairing handlers for daemon products (Vibelet, Remosidian, ...).
16097
16177
  //
@@ -16225,6 +16305,7 @@ function createPairingHttpHandler(deps) {
16225
16305
  return;
16226
16306
  }
16227
16307
  deps.pairings.reset();
16308
+ deps.onReset?.();
16228
16309
  sendJson(res, 200, { ok: true });
16229
16310
  };
16230
16311
  const tryHandle = async (req, res, pathname) => {
@@ -16247,47 +16328,352 @@ function createPairingHttpHandler(deps) {
16247
16328
  return { openPairing, handleOpen, handleCreate, handleReset, tryHandle };
16248
16329
  }
16249
16330
  //# sourceMappingURL=pairing-http.js.map
16250
- ;// CONCATENATED MODULE: ../../packages/daemon-core/dist/index.js
16251
-
16252
-
16331
+ ;// CONCATENATED MODULE: ../../packages/daemon-core/dist/pairing-store.js
16253
16332
 
16254
16333
 
16255
16334
 
16256
- //# sourceMappingURL=index.js.map
16257
- ;// CONCATENATED MODULE: ../../packages/remosidian-protocol/dist/assistant.js
16258
- function isAssistantStatus(value) {
16259
- if (!value || typeof value !== 'object')
16260
- return false;
16261
- const v = value;
16262
- return (typeof v.enabled === 'boolean' &&
16263
- typeof v.connected === 'boolean' &&
16264
- typeof v.paused === 'boolean' &&
16265
- Number.isFinite(v.checkedAt));
16335
+ function pairing_store_base64Url(bytes) {
16336
+ return (0,external_node_crypto_namespaceObject.randomBytes)(bytes).toString("base64url");
16266
16337
  }
16267
- function isAssistantReply(value) {
16268
- if (!value || typeof value !== 'object')
16269
- return false;
16270
- const v = value;
16271
- return (typeof v.text === 'string' &&
16272
- v.text.length <= 30000 &&
16273
- Number.isFinite(v.checkedAt) &&
16274
- (v.receiptId === undefined || typeof v.receiptId === 'string') &&
16275
- (v.proposal === undefined ||
16276
- (typeof v.proposal === 'object' &&
16277
- v.proposal !== null &&
16278
- typeof v.proposal.id === 'string' &&
16279
- typeof v.proposal.description === 'string')));
16338
+ function hashToken(token) {
16339
+ return (0,external_node_crypto_namespaceObject.createHash)("sha256").update(token).digest("hex");
16280
16340
  }
16281
- //# sourceMappingURL=assistant.js.map
16282
- ;// CONCATENATED MODULE: ../../packages/remosidian-protocol/dist/assistant-signals.js
16283
- function isAssistantSignals(value) {
16284
- if (!value || typeof value !== 'object')
16341
+ function sameHash(left, right) {
16342
+ const leftBuffer = Buffer.from(left);
16343
+ const rightBuffer = Buffer.from(right);
16344
+ if (leftBuffer.length !== rightBuffer.length)
16285
16345
  return false;
16286
- const v = value;
16287
- return (typeof v.inboxEnabled === 'boolean' &&
16288
- typeof v.inboxDirectory === 'string' &&
16289
- Array.isArray(v.inbox) &&
16290
- v.inbox.length <= 50 &&
16346
+ return (0,external_node_crypto_namespaceObject.timingSafeEqual)(leftBuffer, rightBuffer);
16347
+ }
16348
+ function readRecords(pairingsPath) {
16349
+ try {
16350
+ const raw = (0,external_node_fs_namespaceObject.readFileSync)(pairingsPath, "utf8");
16351
+ const parsed = JSON.parse(raw);
16352
+ if (!Array.isArray(parsed))
16353
+ return [];
16354
+ return parsed.filter((entry) => entry &&
16355
+ typeof entry.deviceId === "string" &&
16356
+ typeof entry.deviceName === "string" &&
16357
+ typeof entry.tokenHash === "string" &&
16358
+ typeof entry.createdAt === "string" &&
16359
+ typeof entry.lastSeenAt === "string" &&
16360
+ (typeof entry.revokedAt === "string" || entry.revokedAt === null));
16361
+ }
16362
+ catch {
16363
+ return [];
16364
+ }
16365
+ }
16366
+ function writeRecords(pairingsPath, records) {
16367
+ (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(pairingsPath), { recursive: true });
16368
+ (0,external_node_fs_namespaceObject.writeFileSync)(pairingsPath, `${JSON.stringify(records, null, 2)}\n`, "utf8");
16369
+ }
16370
+ class PairingStore {
16371
+ pairingsPath;
16372
+ records;
16373
+ windows = new Map();
16374
+ constructor(pairingsPath) {
16375
+ this.pairingsPath = pairingsPath;
16376
+ this.records = readRecords(pairingsPath);
16377
+ }
16378
+ list() {
16379
+ return this.records.slice();
16380
+ }
16381
+ pairedCount() {
16382
+ return this.records.filter((record) => !record.revokedAt).length;
16383
+ }
16384
+ openWindow(windowMs = 5 * 60_000, reusable = false) {
16385
+ const pairingId = `pair_${pairing_store_base64Url(8)}`;
16386
+ const pairNonce = pairing_store_base64Url(24);
16387
+ const expiresAt = new Date(Date.now() + windowMs).toISOString();
16388
+ const window = { pairingId, pairNonce, expiresAt, reusable };
16389
+ this.windows.set(pairNonce, window);
16390
+ return window;
16391
+ }
16392
+ consumeWindow(pairNonce) {
16393
+ const window = this.windows.get(pairNonce);
16394
+ if (!window)
16395
+ return null;
16396
+ if (!window.reusable) {
16397
+ this.windows.delete(pairNonce);
16398
+ }
16399
+ if (new Date(window.expiresAt).getTime() < Date.now()) {
16400
+ return null;
16401
+ }
16402
+ return window;
16403
+ }
16404
+ issuePairToken(deviceId, deviceName) {
16405
+ const now = new Date().toISOString();
16406
+ const pairToken = pairing_store_base64Url(32);
16407
+ const nextRecord = {
16408
+ deviceId,
16409
+ deviceName,
16410
+ tokenHash: hashToken(pairToken),
16411
+ createdAt: now,
16412
+ lastSeenAt: now,
16413
+ revokedAt: null,
16414
+ };
16415
+ const existingIndex = this.records.findIndex((record) => record.deviceId === deviceId);
16416
+ if (existingIndex >= 0) {
16417
+ this.records[existingIndex] = nextRecord;
16418
+ }
16419
+ else {
16420
+ this.records.unshift(nextRecord);
16421
+ }
16422
+ writeRecords(this.pairingsPath, this.records);
16423
+ return pairToken;
16424
+ }
16425
+ validatePairToken(deviceId, token, touch = true) {
16426
+ const record = this.records.find((entry) => entry.deviceId === deviceId && !entry.revokedAt);
16427
+ if (!record)
16428
+ return false;
16429
+ const tokenDigest = hashToken(token);
16430
+ if (!sameHash(record.tokenHash, tokenDigest))
16431
+ return false;
16432
+ if (touch) {
16433
+ record.lastSeenAt = new Date().toISOString();
16434
+ writeRecords(this.pairingsPath, this.records);
16435
+ }
16436
+ return true;
16437
+ }
16438
+ validateAnyPairToken(token, touch = true) {
16439
+ const tokenDigest = hashToken(token);
16440
+ const record = this.records.find((entry) => !entry.revokedAt && sameHash(entry.tokenHash, tokenDigest));
16441
+ if (!record)
16442
+ return null;
16443
+ if (touch) {
16444
+ record.lastSeenAt = new Date().toISOString();
16445
+ writeRecords(this.pairingsPath, this.records);
16446
+ }
16447
+ return record;
16448
+ }
16449
+ revoke(deviceId) {
16450
+ const record = this.records.find((entry) => entry.deviceId === deviceId && !entry.revokedAt);
16451
+ if (!record)
16452
+ return false;
16453
+ record.revokedAt = new Date().toISOString();
16454
+ writeRecords(this.pairingsPath, this.records);
16455
+ return true;
16456
+ }
16457
+ reset() {
16458
+ this.records = [];
16459
+ this.windows.clear();
16460
+ writeRecords(this.pairingsPath, this.records);
16461
+ }
16462
+ }
16463
+ //# sourceMappingURL=pairing-store.js.map
16464
+ ;// CONCATENATED MODULE: external "node:fs/promises"
16465
+ const external_node_fs_promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:fs/promises");
16466
+ ;// CONCATENATED MODULE: external "node:readline"
16467
+ const external_node_readline_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:readline");
16468
+ ;// CONCATENATED MODULE: ../../packages/daemon-core/dist/remote-direct-server.js
16469
+
16470
+
16471
+
16472
+
16473
+
16474
+ const READY_TIMEOUT_MS = 45_000;
16475
+ const PREFERRED_APP_LOOPBACK_PORT = 39_876;
16476
+ const MAX_EVENT_LINE_LENGTH = 8_192;
16477
+ function binaryFilename() {
16478
+ const suffix = process.platform === 'win32' ? '.exe' : '';
16479
+ return `vibelet-remote-direct-${process.platform}-${process.arch}${suffix}`;
16480
+ }
16481
+ function resolveRemoteDirectBinaryCandidates() {
16482
+ const configured = (process.env.REMOSIDIAN_REMOTE_DIRECT_BIN || process.env.VIBELET_REMOTE_DIRECT_BIN)?.trim();
16483
+ const entryDirectory = (0,external_node_path_namespaceObject.dirname)((0,external_node_path_namespaceObject.resolve)(process.argv[1] || process.cwd()));
16484
+ return [
16485
+ ...(configured ? [(0,external_node_path_namespaceObject.resolve)(configured)] : []),
16486
+ (0,external_node_path_namespaceObject.join)(entryDirectory, 'remoteDirect', binaryFilename()),
16487
+ (0,external_node_path_namespaceObject.resolve)(entryDirectory, '../dist/remoteDirect', binaryFilename()),
16488
+ ];
16489
+ }
16490
+ async function resolveRemoteDirectBinary() {
16491
+ for (const candidate of resolveRemoteDirectBinaryCandidates()) {
16492
+ try {
16493
+ await (0,external_node_fs_promises_namespaceObject.access)(candidate, process.platform === 'win32' ? external_node_fs_namespaceObject.constants.F_OK : external_node_fs_namespaceObject.constants.X_OK);
16494
+ return candidate;
16495
+ }
16496
+ catch {
16497
+ // Try the next deterministic packaged/development location.
16498
+ }
16499
+ }
16500
+ throw new Error(`RemoteDirect sidecar is unavailable for ${process.platform}/${process.arch}`);
16501
+ }
16502
+ function redactCapability(value) {
16503
+ return value.replace(/tc[A-Za-z0-9_-]{16,}/g, '[redacted-remoteDirect-address]').slice(0, 500);
16504
+ }
16505
+ function parseReadyEvent(line, expectedPort) {
16506
+ if (!line || line.length > MAX_EVENT_LINE_LENGTH)
16507
+ return null;
16508
+ try {
16509
+ const parsed = JSON.parse(line);
16510
+ if (parsed.event !== 'server_ready' ||
16511
+ typeof parsed.address !== 'string' ||
16512
+ !parsed.address.startsWith('tc') ||
16513
+ parsed.address.length > 4096 ||
16514
+ parsed.port !== expectedPort) {
16515
+ return null;
16516
+ }
16517
+ return parsed;
16518
+ }
16519
+ catch {
16520
+ return null;
16521
+ }
16522
+ }
16523
+ class RemoteDirectServerProcess {
16524
+ options;
16525
+ child = null;
16526
+ stopping = false;
16527
+ constructor(options) {
16528
+ this.options = options;
16529
+ }
16530
+ get isRunning() {
16531
+ return Boolean(this.child && this.child.exitCode === null && this.child.signalCode === null);
16532
+ }
16533
+ async start() {
16534
+ if (this.child)
16535
+ throw new Error('RemoteDirect sidecar is already started');
16536
+ const binary = await resolveRemoteDirectBinary();
16537
+ this.stopping = false;
16538
+ const child = (0,external_node_child_process_namespaceObject.spawn)(binary, [
16539
+ 'server',
16540
+ '--target',
16541
+ `127.0.0.1:${this.options.daemonPort}`,
16542
+ '--remote-port',
16543
+ String(this.options.daemonPort),
16544
+ '--state',
16545
+ this.options.statePath,
16546
+ ], { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
16547
+ this.child = child;
16548
+ let stderr = '';
16549
+ child.stderr.setEncoding('utf8');
16550
+ child.stderr.on('data', (chunk) => {
16551
+ stderr = `${stderr}${chunk}`.slice(-2_000);
16552
+ });
16553
+ child.once('exit', (code, signal) => {
16554
+ if (this.child === child)
16555
+ this.child = null;
16556
+ if (!this.stopping)
16557
+ this.options.onUnexpectedExit?.({ code, signal });
16558
+ });
16559
+ const ready = await new Promise((resolveReady, rejectReady) => {
16560
+ const lines = (0,external_node_readline_namespaceObject.createInterface)({ input: child.stdout });
16561
+ const timer = setTimeout(() => {
16562
+ lines.close();
16563
+ rejectReady(new Error('RemoteDirect sidecar startup timed out'));
16564
+ }, READY_TIMEOUT_MS);
16565
+ timer.unref?.();
16566
+ const finish = (error, event) => {
16567
+ clearTimeout(timer);
16568
+ lines.close();
16569
+ child.off('error', onError);
16570
+ child.off('exit', onExitBeforeReady);
16571
+ if (error)
16572
+ rejectReady(error);
16573
+ else if (event)
16574
+ resolveReady(event);
16575
+ };
16576
+ const onError = (error) => finish(new Error(`RemoteDirect sidecar failed to launch: ${redactCapability(error.message)}`));
16577
+ const onExitBeforeReady = (code, signal) => finish(new Error(`RemoteDirect sidecar exited before ready (${code ?? signal ?? 'unknown'}): ${redactCapability(stderr.trim())}`));
16578
+ child.once('error', onError);
16579
+ child.once('exit', onExitBeforeReady);
16580
+ lines.on('line', (line) => {
16581
+ const event = parseReadyEvent(line, this.options.daemonPort);
16582
+ if (event)
16583
+ finish(undefined, event);
16584
+ });
16585
+ }).catch((error) => {
16586
+ this.stop();
16587
+ throw error;
16588
+ });
16589
+ return {
16590
+ kind: 'direct',
16591
+ host: '127.0.0.1',
16592
+ port: PREFERRED_APP_LOOPBACK_PORT,
16593
+ source: 'remoteDirect',
16594
+ stability: 'stable',
16595
+ remoteDirect: {
16596
+ serverAddress: ready.address,
16597
+ remotePort: ready.port,
16598
+ },
16599
+ };
16600
+ }
16601
+ stop() {
16602
+ this.stopping = true;
16603
+ const child = this.child;
16604
+ this.child = null;
16605
+ if (!child || child.exitCode !== null || child.signalCode !== null)
16606
+ return;
16607
+ child.kill('SIGTERM');
16608
+ const forceTimer = setTimeout(() => {
16609
+ if (child.exitCode === null && child.signalCode === null)
16610
+ child.kill('SIGKILL');
16611
+ }, 2_000);
16612
+ forceTimer.unref?.();
16613
+ }
16614
+ }
16615
+ //# sourceMappingURL=remote-direct-server.js.map
16616
+ ;// CONCATENATED MODULE: ../../packages/daemon-core/dist/index.js
16617
+
16618
+
16619
+
16620
+
16621
+
16622
+
16623
+ //# sourceMappingURL=index.js.map
16624
+ ;// CONCATENATED MODULE: ../../packages/remosidian-protocol/dist/assistant.js
16625
+ function isAssistantStatus(value) {
16626
+ if (!value || typeof value !== 'object')
16627
+ return false;
16628
+ const v = value;
16629
+ return (typeof v.enabled === 'boolean' &&
16630
+ typeof v.connected === 'boolean' &&
16631
+ typeof v.paused === 'boolean' &&
16632
+ Number.isFinite(v.checkedAt) &&
16633
+ ['ready', 'disabled', 'paused', 'agent_offline'].includes(v.state) &&
16634
+ (v.summary === undefined || isAssistantSummary(v.summary)));
16635
+ }
16636
+ function isAssistantSummary(value) {
16637
+ if (!value || typeof value !== 'object')
16638
+ return false;
16639
+ const v = value;
16640
+ return (['total', 'active', 'paused', 'blocked', 'review', 'completed'].every((key) => Number.isSafeInteger(v[key]) && Number(v[key]) >= 0) &&
16641
+ (v.runningGoalId === null || typeof v.runningGoalId === 'string') &&
16642
+ Array.isArray(v.focus) &&
16643
+ v.focus.length <= 8 &&
16644
+ v.focus.every((goal) => goal &&
16645
+ typeof goal.id === 'string' &&
16646
+ typeof goal.title === 'string' &&
16647
+ ['active', 'paused', 'blocked', 'review', 'completed'].includes(goal.status) &&
16648
+ Number.isSafeInteger(goal.priority) &&
16649
+ typeof goal.summary === 'string' &&
16650
+ typeof goal.blocker === 'string' &&
16651
+ Number.isFinite(goal.updatedAt)));
16652
+ }
16653
+ function isAssistantReply(value) {
16654
+ if (!value || typeof value !== 'object')
16655
+ return false;
16656
+ const v = value;
16657
+ return (typeof v.text === 'string' &&
16658
+ v.text.length <= 30000 &&
16659
+ Number.isFinite(v.checkedAt) &&
16660
+ (v.receiptId === undefined || typeof v.receiptId === 'string') &&
16661
+ (v.proposal === undefined ||
16662
+ (typeof v.proposal === 'object' &&
16663
+ v.proposal !== null &&
16664
+ typeof v.proposal.id === 'string' &&
16665
+ typeof v.proposal.description === 'string')));
16666
+ }
16667
+ //# sourceMappingURL=assistant.js.map
16668
+ ;// CONCATENATED MODULE: ../../packages/remosidian-protocol/dist/assistant-signals.js
16669
+ function isAssistantSignals(value) {
16670
+ if (!value || typeof value !== 'object')
16671
+ return false;
16672
+ const v = value;
16673
+ return (typeof v.inboxEnabled === 'boolean' &&
16674
+ typeof v.inboxDirectory === 'string' &&
16675
+ Array.isArray(v.inbox) &&
16676
+ v.inbox.length <= 50 &&
16291
16677
  v.inbox.every((x) => x &&
16292
16678
  typeof x.id === 'string' &&
16293
16679
  typeof x.goalId === 'string' &&
@@ -16300,7 +16686,7 @@ function isAssistantSignals(value) {
16300
16686
  v.notifications.every((x) => x &&
16301
16687
  typeof x.id === 'string' &&
16302
16688
  typeof x.goalId === 'string' &&
16303
- ['decision', 'review', 'failure'].includes(x.kind) &&
16689
+ ['started', 'progress', 'decision', 'review', 'blocked', 'failure', 'completed', 'summary'].includes(x.kind) &&
16304
16690
  typeof x.title === 'string' &&
16305
16691
  typeof x.body === 'string' &&
16306
16692
  Number.isFinite(x.createdAt) &&
@@ -16747,8 +17133,6 @@ function parseRemosidianPush(input) {
16747
17133
  //# sourceMappingURL=index.js.map
16748
17134
  ;// CONCATENATED MODULE: external "node:events"
16749
17135
  const external_node_events_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:events");
16750
- ;// CONCATENATED MODULE: external "node:fs/promises"
16751
- const external_node_fs_promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:fs/promises");
16752
17136
  ;// CONCATENATED MODULE: external "node:stream"
16753
17137
  const external_node_stream_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:stream");
16754
17138
  ;// CONCATENATED MODULE: ../../packages/remosidian-server/node_modules/readdirp/index.js
@@ -18523,7 +18907,7 @@ function createAssistantProVerifier(env = process.env, fetcher = fetch) {
18523
18907
  const key = env.REVENUECAT_API_KEY;
18524
18908
  let validUntil = 0;
18525
18909
  return async (customerId) => {
18526
- if (developmentPro && customerId === 'remosidian-local-dev')
18910
+ if (developmentPro)
18527
18911
  return true;
18528
18912
  if (!customer || customer !== customerId || !project || !entitlement || !key)
18529
18913
  return false;
@@ -18675,7 +19059,9 @@ class AssistantPush {
18675
19059
  !feed.notifications.every((n) => typeof n.id === 'string' &&
18676
19060
  Number.isSafeInteger(n.sequence) &&
18677
19061
  n.sequence > device.cursor &&
18678
- ['decision', 'review', 'failure'].includes(n.kind) &&
19062
+ ['started', 'progress', 'decision', 'review', 'blocked', 'failure', 'completed', 'summary'].includes(n.kind) &&
19063
+ typeof n.title === 'string' &&
19064
+ typeof n.body === 'string' &&
18679
19065
  typeof n.read === 'boolean'))
18680
19066
  throw Error('Invalid notice feed');
18681
19067
  for (const notice of feed.notifications) {
@@ -18683,6 +19069,7 @@ class AssistantPush {
18683
19069
  device.queue.push({
18684
19070
  noticeId: notice.id,
18685
19071
  sequence: notice.sequence,
19072
+ title: notice.title.slice(0, 120),
18686
19073
  state: 'queued',
18687
19074
  attempts: 0,
18688
19075
  nextAt: this.now(),
@@ -18730,8 +19117,8 @@ class AssistantPush {
18730
19117
  try {
18731
19118
  const result = await this.request('send', {
18732
19119
  to: device.token,
18733
- title: 'Remosidian 个人助理',
18734
- body: '有新的任务进展需要你查看。',
19120
+ title: ticket.title || 'Remosidian Personal Agent',
19121
+ body: '打开置顶会话查看详情。',
18735
19122
  sound: 'default',
18736
19123
  channelId: 'personal-assistant',
18737
19124
  data: {
@@ -30506,12 +30893,12 @@ class RemosidianAssistant {
30506
30893
  (0,external_node_path_namespaceObject.join)((0,external_node_os_namespaceObject.homedir)(), 'Library', 'Application Support', 'LifeOS Agent');
30507
30894
  this.port = options.agentPort ?? Number(process.env.LIFEOS_AGENT_PORT || 4317);
30508
30895
  if (!Number.isInteger(this.port) || this.port < 1024 || this.port > 65535)
30509
- throw new Error('助理端口无效。');
30896
+ throw new Error('Personal Agent 端口无效。');
30510
30897
  this.ready = (0,external_node_fs_promises_namespaceObject.readFile)((0,external_node_path_namespaceObject.join)(options.dataDir, 'assistant-settings.json'), 'utf8')
30511
30898
  .then((raw) => {
30512
30899
  const ids = JSON.parse(raw);
30513
30900
  if (!Array.isArray(ids) || !ids.every((id) => typeof id === 'string'))
30514
- throw new Error('助理设置无效。');
30901
+ throw new Error('Personal Agent 设置无效。');
30515
30902
  this.enabled = new Set(ids);
30516
30903
  })
30517
30904
  .catch((error) => {
@@ -30538,7 +30925,7 @@ class RemosidianAssistant {
30538
30925
  async handle(req, signal) {
30539
30926
  await this.ready;
30540
30927
  if (this.loadError)
30541
- throw new Error('个人助理设置无法读取,请在电脑端检查。');
30928
+ throw new Error('Personal Agent 设置无法读取,请在电脑端检查。');
30542
30929
  const vault = this.options.config.vaults.find((v) => v.id === req.vaultId);
30543
30930
  if (!vault)
30544
30931
  throw new VaultRequestError('找不到这个知识库。', 'not_found');
@@ -30555,16 +30942,16 @@ class RemosidianAssistant {
30555
30942
  }
30556
30943
  // A lapse must not trap users into leaving automatic work running.
30557
30944
  if (!pro && !disabling)
30558
- throw new VaultRequestError('个人助理需要有效的 Remosidian Pro 权限;请检查电脑端的订阅验证配置。', 'forbidden');
30945
+ throw new VaultRequestError('Personal Agent 需要有效的 Remosidian Pro 权限;请检查电脑端的订阅验证配置。', 'forbidden');
30559
30946
  if (req.op === 'assistant.status')
30560
30947
  return this.status(req.vaultId, vault.root, signal);
30561
30948
  if (this.busy)
30562
- throw new Error('助理正在处理上一条请求,请稍后重试。');
30949
+ throw new Error('Personal Agent 正在处理上一条请求,请稍后重试。');
30563
30950
  this.busy = true;
30564
30951
  try {
30565
30952
  if (req.op === 'assistant.push.register') {
30566
30953
  if (req.enabled && !this.enabled.has(req.vaultId))
30567
- throw Error('请先开启个人助理。');
30954
+ throw Error('请先开启 Personal Agent。');
30568
30955
  const dashboard = req.enabled
30569
30956
  ? (await this.call(vault.root, '/api/dashboard', signal))
30570
30957
  : null;
@@ -30580,7 +30967,7 @@ class RemosidianAssistant {
30580
30967
  if (req.enabled) {
30581
30968
  await this.call(vault.root, '/api/dashboard', signal);
30582
30969
  if (req.pause)
30583
- throw new Error('开启助理时不能同时暂停后台。');
30970
+ throw new Error('开启 Personal Agent 时不能同时暂停后台。');
30584
30971
  }
30585
30972
  // Persist disabling first. If pause fails, control remains disabled and the error is explicit.
30586
30973
  const next = new Set(this.enabled);
@@ -30595,7 +30982,7 @@ class RemosidianAssistant {
30595
30982
  return this.status(req.vaultId, vault.root, signal);
30596
30983
  }
30597
30984
  if (!this.enabled.has(req.vaultId))
30598
- throw new VaultRequestError('请先在设置中开启个人助理。', 'forbidden');
30985
+ throw new VaultRequestError('请先在设置中开启 Personal Agent。', 'forbidden');
30599
30986
  if (req.op === 'assistant.signals' ||
30600
30987
  req.op === 'assistant.inbox.configure' ||
30601
30988
  req.op === 'assistant.notice.read') {
@@ -30626,7 +31013,7 @@ class RemosidianAssistant {
30626
31013
  : { vaultRoot: vault.root, proposalId: req.proposalId };
30627
31014
  const reply = await this.call(vault.root, path, signal, input);
30628
31015
  if (!isAssistantReply(reply))
30629
- throw new Error('电脑返回的助理结果格式无效。');
31016
+ throw new Error('电脑返回的 Personal Agent 结果格式无效。');
30630
31017
  return reply;
30631
31018
  }
30632
31019
  finally {
@@ -30636,15 +31023,50 @@ class RemosidianAssistant {
30636
31023
  async status(vaultId, root, signal) {
30637
31024
  try {
30638
31025
  const result = (await this.call(root, '/api/dashboard', signal));
31026
+ const goals = Array.isArray(result.goals) ? result.goals : [];
31027
+ const counts = (status) => goals.filter((goal) => goal.status === status).length;
31028
+ const enabled = this.enabled.has(vaultId);
31029
+ const paused = result.settings.paused;
30639
31030
  return {
30640
- enabled: this.enabled.has(vaultId),
31031
+ enabled,
30641
31032
  connected: true,
30642
- paused: result.settings.paused,
31033
+ paused,
30643
31034
  checkedAt: Date.now(),
31035
+ state: !enabled ? 'disabled' : paused ? 'paused' : 'ready',
31036
+ summary: {
31037
+ total: goals.length,
31038
+ active: counts('active'),
31039
+ paused: counts('paused'),
31040
+ blocked: counts('blocked'),
31041
+ review: counts('review'),
31042
+ completed: counts('completed'),
31043
+ runningGoalId: result.runningGoalId,
31044
+ focus: goals
31045
+ .filter((goal) => goal.status !== 'completed')
31046
+ .sort((a, b) => Number(b.id === result.runningGoalId) - Number(a.id === result.runningGoalId) ||
31047
+ a.priority - b.priority ||
31048
+ b.updatedAt - a.updatedAt)
31049
+ .slice(0, 4)
31050
+ .map((goal) => ({
31051
+ id: goal.id,
31052
+ title: goal.title.slice(0, 160),
31053
+ status: goal.status,
31054
+ priority: goal.priority,
31055
+ summary: goal.summary.slice(0, 500),
31056
+ blocker: goal.blocker.slice(0, 500),
31057
+ updatedAt: goal.updatedAt,
31058
+ })),
31059
+ },
30644
31060
  };
30645
31061
  }
30646
31062
  catch {
30647
- return { enabled: this.enabled.has(vaultId), connected: false, paused: false, checkedAt: Date.now() };
31063
+ return {
31064
+ enabled: this.enabled.has(vaultId),
31065
+ connected: false,
31066
+ paused: false,
31067
+ checkedAt: Date.now(),
31068
+ state: 'agent_offline',
31069
+ };
30648
31070
  }
30649
31071
  }
30650
31072
  async save(next) {
@@ -30657,7 +31079,7 @@ class RemosidianAssistant {
30657
31079
  async call(root, path, signal, body, method = 'POST') {
30658
31080
  const token = (await (0,external_node_fs_promises_namespaceObject.readFile)((0,external_node_path_namespaceObject.join)(this.agentDataDir, 'remosidian-bridge.token'), 'utf8')).trim();
30659
31081
  if (!/^[a-f0-9]{64}$/u.test(token))
30660
- throw new Error('本机助理凭据无效。');
31082
+ throw new Error('本机 Personal Agent 凭据无效。');
30661
31083
  const response = await fetch(`http://127.0.0.1:${this.port}${path}`, {
30662
31084
  method: body === undefined ? 'GET' : method,
30663
31085
  headers: {
@@ -30671,7 +31093,7 @@ class RemosidianAssistant {
30671
31093
  });
30672
31094
  const result = (await response.json());
30673
31095
  if (!response.ok)
30674
- throw new Error(result.error || '无法连接电脑上的个人助理。');
31096
+ throw new Error(result.error || '无法连接电脑上的 Personal Agent。');
30675
31097
  if (path === '/api/dashboard' &&
30676
31098
  (!result.settings?.vault || (await (0,external_node_fs_promises_namespaceObject.realpath)(result.settings.vault)) !== (await (0,external_node_fs_promises_namespaceObject.realpath)(root))))
30677
31099
  throw new Error('知识库连接不一致。');
@@ -33416,7 +33838,7 @@ function runFileBackedPluginTurn(input, prompt, options, onDelta) {
33416
33838
  }
33417
33839
  //# sourceMappingURL=chat-plugin-runtime.js.map
33418
33840
  ;// CONCATENATED MODULE: ../../packages/remosidian-server/dist/version.js
33419
- const VERSION = '0.1.12';
33841
+ const VERSION = '0.1.13';
33420
33842
  //# sourceMappingURL=version.js.map
33421
33843
  ;// CONCATENATED MODULE: ../../packages/remosidian-server/dist/chat-runtime.js
33422
33844
 
@@ -34263,6 +34685,30 @@ class RemosidianProtocolMetrics {
34263
34685
  }
34264
34686
  }
34265
34687
  //# sourceMappingURL=protocol-metrics.js.map
34688
+ ;// CONCATENATED MODULE: ../../packages/remosidian-server/dist/remote-direct-transport.js
34689
+
34690
+
34691
+
34692
+ async function startRemoteDirectTransport(instance, options, port) {
34693
+ const transport = options.remoteDirect
34694
+ ? new RemoteDirectServerProcess({
34695
+ daemonPort: port,
34696
+ statePath: (0,external_node_path_namespaceObject.join)(createRemosidianPaths(options.dataDir).dataDir, 'remote-direct-server.json'),
34697
+ onUnexpectedExit: () => instance.setRemoteDirectConnection(null),
34698
+ })
34699
+ : null;
34700
+ try {
34701
+ if (transport)
34702
+ instance.setRemoteDirectConnection(await transport.start());
34703
+ }
34704
+ catch (error) {
34705
+ await new Promise((resolve) => instance.webSocketServer.close(() => instance.httpServer.close(() => resolve())));
34706
+ await instance.dispose();
34707
+ throw error;
34708
+ }
34709
+ return transport;
34710
+ }
34711
+ //# sourceMappingURL=remote-direct-transport.js.map
34266
34712
  ;// CONCATENATED MODULE: ../../packages/remosidian-server/dist/request-handler.js
34267
34713
 
34268
34714
 
@@ -34310,7 +34756,7 @@ async function handleRemosidianRequest(req, vaultService, chatService, conversat
34310
34756
  case 'assistant.notice.read':
34311
34757
  case 'assistant.push.register':
34312
34758
  if (!context.assistant)
34313
- return invalidResponse(req.id, '此服务器尚未支持个人助理,请更新电脑端。');
34759
+ return invalidResponse(req.id, '此服务器尚未支持 Personal Agent,请更新电脑端。');
34314
34760
  return okResponse(req.id, await context.assistant.handle(req, context.signal));
34315
34761
  case 'vault.list':
34316
34762
  return okResponse(req.id, await vaultService.list(req.vaultId, req.dir));
@@ -34754,6 +35200,7 @@ const _parseCodexRateLimitsForTesting = (/* unused pure expression or super */ n
34754
35200
 
34755
35201
 
34756
35202
 
35203
+
34757
35204
  function server_sendJson(res, statusCode, data) {
34758
35205
  const body = `${JSON.stringify(data)}\n`;
34759
35206
  res.writeHead(statusCode, {
@@ -34923,13 +35370,19 @@ function createRemosidianServer(options = {}) {
34923
35370
  subscriptionWatchers.delete(key);
34924
35371
  void existing.watcher.close();
34925
35372
  };
35373
+ let remoteDirectConnection = null;
34926
35374
  const startedAt = Date.now();
34927
- const getAdvertisedConnectionTarget = () => computeAdvertisedConnectionTarget({
34928
- canonicalHost: advertisedHosts.canonicalHost,
34929
- fallbackHosts: advertisedHosts.fallbackHosts,
34930
- port: config.server.port,
34931
- relayUrl,
34932
- });
35375
+ const getAdvertisedConnectionTarget = () => {
35376
+ const target = computeAdvertisedConnectionTarget({
35377
+ canonicalHost: advertisedHosts.canonicalHost,
35378
+ fallbackHosts: advertisedHosts.fallbackHosts,
35379
+ port: config.server.port,
35380
+ relayUrl,
35381
+ });
35382
+ return remoteDirectConnection
35383
+ ? { ...target, connections: [remoteDirectConnection, ...target.connections] }
35384
+ : target;
35385
+ };
34933
35386
  const buildHealth = () => {
34934
35387
  const target = getAdvertisedConnectionTarget();
34935
35388
  return {
@@ -34940,6 +35393,7 @@ function createRemosidianServer(options = {}) {
34940
35393
  canonicalHost: target.canonicalHost,
34941
35394
  fallbackHosts: target.fallbackHosts,
34942
35395
  port: target.port,
35396
+ remoteDirect: { enabled: options.remoteDirect === true, running: Boolean(remoteDirectConnection) },
34943
35397
  runtime: hostRuntime,
34944
35398
  ...(relayUrl ? { relayUrl } : {}),
34945
35399
  connections: target.connections,
@@ -34952,6 +35406,9 @@ function createRemosidianServer(options = {}) {
34952
35406
  wsClients: webSocketServer.clients.size,
34953
35407
  protocol: protocolMetrics.snapshot(),
34954
35408
  uptimeSeconds: Math.round((Date.now() - startedAt) / 1000),
35409
+ features: {
35410
+ personalAgent: config.server.features?.personalAgent === true,
35411
+ },
34955
35412
  ai: chatService.configured
34956
35413
  ? {
34957
35414
  configured: true,
@@ -35144,6 +35601,9 @@ function createRemosidianServer(options = {}) {
35144
35601
  identity,
35145
35602
  pairings,
35146
35603
  advertisedHosts,
35604
+ setRemoteDirectConnection: (target) => {
35605
+ remoteDirectConnection = target;
35606
+ },
35147
35607
  dispose: async () => {
35148
35608
  await assistant.close();
35149
35609
  await Promise.all([...subscriptionWatchers.values()].map((entry) => entry.watcher.close()));
@@ -35170,10 +35630,14 @@ async function startRemosidianServer(options = {}) {
35170
35630
  });
35171
35631
  const address = instance.httpServer.address();
35172
35632
  const port = address?.port ?? listenPort;
35633
+ instance.config.server.port = port;
35634
+ const transport = await startRemoteDirectTransport(instance, options, port);
35173
35635
  return {
35174
35636
  ...instance,
35175
35637
  port,
35176
35638
  close: async () => {
35639
+ transport?.stop();
35640
+ instance.setRemoteDirectConnection(null);
35177
35641
  await new Promise((resolve, reject) => {
35178
35642
  // WebSocketServer.close waits for clients; mobile clients can otherwise
35179
35643
  // keep plugin stop/restart pending indefinitely.
@@ -36306,10 +36770,11 @@ function applyConnectionConfig(config, { relayUrl, advertisedHost, fallbackHosts
36306
36770
  ...(localOnly ? { localOnly: true } : {}),
36307
36771
  }
36308
36772
  : currentAdvertisedHosts;
36773
+ const { relayUrl: _savedRelay, ...server } = config.server ?? {};
36309
36774
  return {
36310
36775
  ...config,
36311
36776
  server: {
36312
- ...config.server,
36777
+ ...server,
36313
36778
  advertisedHosts,
36314
36779
  ...(relayUrl && !localOnly ? { relayUrl } : {}),
36315
36780
  },
@@ -36337,6 +36802,7 @@ async function resolveRelayForStart(
36337
36802
  const advertisedHost = isWildcardBindHost(flags.host) ? '' : normalizeOptionalString(flags.host);
36338
36803
  const fallbackHosts = readCsvList(flags['fallback-hosts']);
36339
36804
  const shouldManageTunnel =
36805
+ flags.cloudflare === true &&
36340
36806
  !localOnly &&
36341
36807
  explicitRelay === null &&
36342
36808
  !advertisedHost &&
@@ -36374,7 +36840,7 @@ async function resolveRelayForStart(
36374
36840
  clearRelayConfig(relayConfigPath);
36375
36841
  process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
36376
36842
  process.stderr.write(
36377
- `Continuing with LAN/local pairing. Use \`npx ${ALIAS_NPM_PACKAGE} start --force\` after fixing Cloudflare Tunnel, or pass --relay=<url> for a custom tunnel.\n`,
36843
+ `Continuing with Remote Direct and LAN pairing. Use \`npx ${ALIAS_NPM_PACKAGE} start --cloudflare --force\` after fixing Cloudflare Tunnel, or pass --relay=<url> for a custom tunnel.\n`,
36378
36844
  );
36379
36845
  }
36380
36846
  }
@@ -36389,7 +36855,8 @@ async function resolveRelayForStart(
36389
36855
  }
36390
36856
 
36391
36857
  const ignoreSavedRelay =
36392
- explicitRelay === null && (Boolean(advertisedHost) || fallbackHosts.length > 0 || managedTunnelFailed);
36858
+ explicitRelay === null &&
36859
+ (flags.cloudflare !== true || Boolean(advertisedHost) || fallbackHosts.length > 0 || managedTunnelFailed);
36393
36860
  const relayUrl =
36394
36861
  explicitRelay !== null
36395
36862
  ? explicitRelay
@@ -36399,75 +36866,17 @@ async function resolveRelayForStart(
36399
36866
 
36400
36867
 
36401
36868
 
36402
- ;// CONCATENATED MODULE: ./bin/remosidian.mjs
36403
-
36404
-
36405
-
36406
-
36407
-
36408
-
36409
-
36410
-
36411
-
36412
-
36413
-
36414
-
36869
+ function shouldRestartDaemon(health, flags, environment = process.env) {
36870
+ if (flags.force === true) return true;
36871
+ const remoteDirect = flags.local !== true;
36872
+ if (health.remoteDirect?.enabled !== remoteDirect || (remoteDirect && health.remoteDirect?.running !== true))
36873
+ return true;
36874
+ const wantsRelay = flags.cloudflare === true || Boolean(flags.relay) || Boolean(environment.REMOSIDIAN_RELAY_URL);
36875
+ return Boolean(health.relayUrl) !== wantsRelay;
36876
+ }
36415
36877
 
36416
-
36417
- const remosidian_PROG = 'remosidian';
36418
- const PRIMARY_NPM_PACKAGE = '@remosidian/cli';
36419
- const remosidian_ALIAS_NPM_PACKAGE = 'remosidian';
36420
-
36421
- function helpText() {
36422
- return [
36423
- `${remosidian_PROG} v${VERSION} — remote vault server for Obsidian / LifeOS`,
36424
- '',
36425
- 'Package names:',
36426
- ` ${PRIMARY_NPM_PACKAGE}`,
36427
- ` ${remosidian_ALIAS_NPM_PACKAGE}`,
36428
- '',
36429
- 'Usage:',
36430
- ` npx ${remosidian_ALIAS_NPM_PACKAGE} Start the current directory as a vault`,
36431
- ` npx ${remosidian_ALIAS_NPM_PACKAGE} <command> [options]`,
36432
- ` ${remosidian_PROG} <command> [options]`,
36433
- '',
36434
- 'Commands:',
36435
- ' start Start or reuse the Remosidian background daemon',
36436
- ' pair Open a one-time pairing window and print a QR code',
36437
- ' status Print the running daemon health snapshot',
36438
- ' stop Stop the background daemon',
36439
- ' reset-pairings Revoke every paired device (local only)',
36440
- ' init Create ~/.remosidian/config.json interactively',
36441
- ' help Show this help',
36442
- '',
36443
- 'Global options:',
36444
- ' --data-dir <path> Override $REMOSIDIAN_HOME (default: ~/.remosidian)',
36445
- ' --config <path> Override $REMOSIDIAN_CONFIG (default: <data-dir>/config.json)',
36446
- ' --version Print version and exit',
36447
- '',
36448
- 'start options:',
36449
- ' --vault <id>:<path> Use an ad-hoc vault instead of the config file',
36450
- ' (repeatable; id defaults to folder name)',
36451
- ' --port <number> Listen on this TCP port (default 41234)',
36452
- ' --access=local LAN/local only; skip Cloudflare Tunnel and saved relay config',
36453
- ' --access=remote Use the default managed Cloudflare Tunnel path',
36454
- ' --access=https://<url>',
36455
- ' Use a custom relay URL for remote access',
36456
- ' --access=<host>[,<fallback>]',
36457
- ' Use direct LAN/Tailscale hosts',
36458
- ' --access= Clear the saved relay URL for this run',
36459
- ' --force Restart the managed Cloudflare Tunnel before startup',
36460
- ' --bind <address> Bind to this interface (default 0.0.0.0)',
36461
- ' --agent <name> AI agent for /Ask: claude or codex (auto-detects)',
36462
- ' --agent-path <path> Override the agent binary path',
36463
- ' --foreground Run the server in the foreground',
36464
- ' --json Emit a single JSON line instead of human output',
36465
- ' --no-qr Skip the pairing QR on startup',
36466
- '',
36467
- 'Legacy access aliases still work:',
36468
- ' --local, --remote, --tunnel, --relay <url>, --host <address>, --fallback-hosts <x>',
36469
- ].join('\n');
36470
- }
36878
+ ;// CONCATENATED MODULE: ./bin/cli-args.mjs
36879
+ const cli_args_PROG = 'remosidian';
36471
36880
 
36472
36881
  function parseArgs(argv) {
36473
36882
  const args = { positionals: [], flags: {}, vaults: [] };
@@ -36498,8 +36907,8 @@ function parseArgs(argv) {
36498
36907
  return args;
36499
36908
  }
36500
36909
 
36501
- function failUsage(message, details = '') {
36502
- process.stderr.write(`[${remosidian_PROG}] ${message}\n`);
36910
+ function cli_args_failUsage(message, details = '') {
36911
+ process.stderr.write(`[${cli_args_PROG}] ${message}\n`);
36503
36912
  if (details) {
36504
36913
  process.stderr.write(`${details}\n`);
36505
36914
  }
@@ -36566,7 +36975,7 @@ function normalizeAccessHost(host) {
36566
36975
  .toLowerCase();
36567
36976
  }
36568
36977
 
36569
- function parseAccessValue(rawValue, fail = failUsage) {
36978
+ function parseAccessValue(rawValue, fail = cli_args_failUsage) {
36570
36979
  if (rawValue === true) {
36571
36980
  fail('--access requires an argument (e.g. --access=local)');
36572
36981
  }
@@ -36648,7 +37057,7 @@ function normalizePortValue(rawValue) {
36648
37057
  return parsed;
36649
37058
  }
36650
37059
 
36651
- function parsePortFlag(flags, fail = failUsage) {
37060
+ function parsePortFlag(flags, fail = cli_args_failUsage) {
36652
37061
  if (!hasOwnFlag(flags, 'port')) return null;
36653
37062
  if (flags.port === true) {
36654
37063
  fail('--port requires an argument (e.g. --port 41234)');
@@ -36661,7 +37070,7 @@ function parsePortFlag(flags, fail = failUsage) {
36661
37070
  return parsed;
36662
37071
  }
36663
37072
 
36664
- function validateLegacyAccessValues(flags, fail = failUsage) {
37073
+ function validateLegacyAccessValues(flags, fail = cli_args_failUsage) {
36665
37074
  for (const name of ['relay', 'host', 'fallback-hosts']) {
36666
37075
  if (flags[name] === true) {
36667
37076
  const hint =
@@ -36671,10 +37080,22 @@ function validateLegacyAccessValues(flags, fail = failUsage) {
36671
37080
  }
36672
37081
  }
36673
37082
 
36674
- function normalizeCliFlags(flags, env = process.env, fail = failUsage) {
37083
+ function resolvePersonalAgentOverride(flags, fail = cli_args_failUsage) {
37084
+ const enable = hasOwnFlag(flags, 'personal-agent') || hasOwnFlag(flags, 'pa');
37085
+ const disable = hasOwnFlag(flags, 'no-personal-agent') || hasOwnFlag(flags, 'no-pa');
37086
+ if (enable && disable) {
37087
+ fail('Do not combine Personal Agent enable and disable flags.');
37088
+ }
37089
+ if (enable) return true;
37090
+ if (disable) return false;
37091
+ return undefined;
37092
+ }
37093
+
37094
+ function normalizeCliFlags(flags, env = process.env, fail = cli_args_failUsage) {
36675
37095
  const normalized = { ...flags };
36676
37096
  applyNpmConfigArgFallback(normalized, 'port', env);
36677
37097
  applyNpmConfigFlagFallback(normalized, 'force', env);
37098
+ applyNpmConfigFlagFallback(normalized, 'cloudflare', env);
36678
37099
 
36679
37100
  const hasAccessFlag = hasOwnFlag(normalized, 'access');
36680
37101
  const envAccessValue = hasAccessFlag ? null : readNpmConfigArg('access', env);
@@ -36707,6 +37128,7 @@ function normalizeCliFlags(flags, env = process.env, fail = failUsage) {
36707
37128
  delete normalized.tunnel;
36708
37129
  validateLegacyAccessValues(normalized, fail);
36709
37130
  parsePortFlag(normalized, fail);
37131
+ resolvePersonalAgentOverride(normalized, fail);
36710
37132
  return normalized;
36711
37133
  }
36712
37134
 
@@ -36728,326 +37150,91 @@ function resolveInvocation(argv) {
36728
37150
  return { command, rest, useCurrentDirectoryVault: false };
36729
37151
  }
36730
37152
 
36731
- function resolveEntrypointHref(path) {
36732
- if (!path) return '';
36733
- try {
36734
- return (0,external_node_url_namespaceObject.pathToFileURL)((0,external_node_fs_namespaceObject.realpathSync)((0,external_node_path_namespaceObject.resolve)(path))).href;
36735
- } catch {
36736
- return (0,external_node_url_namespaceObject.pathToFileURL)((0,external_node_path_namespaceObject.resolve)(path)).href;
36737
- }
36738
- }
36739
-
36740
- function expandVaultSpec(spec) {
36741
- if (spec.trim().length === 0) return null;
36742
- const colon = spec.indexOf(':');
36743
- let id = '';
36744
- let root = spec;
36745
- if (colon > 0 && !/^[a-zA-Z]:[\\/]/.test(spec)) {
36746
- id = spec.slice(0, colon).trim();
36747
- root = spec.slice(colon + 1);
36748
- }
36749
- if (root.trim().length === 0) return null;
36750
- const resolved = resolveUserPath(root);
36751
- if (!id) {
36752
- id = resolved.split(/[\\/]/).filter(Boolean).pop() ?? 'vault';
36753
- }
36754
- return {
36755
- id,
36756
- name: id,
36757
- root: resolved,
36758
- ignored: ['.obsidian/**', '.git/**', '.DS_Store', '*.swp', '*.tmp'],
36759
- };
36760
- }
36761
-
36762
- function loadConfigOrNull(paths) {
36763
- if (!(0,external_node_fs_namespaceObject.existsSync)(paths.configPath)) {
36764
- return null;
36765
- }
36766
- try {
36767
- return JSON.parse((0,external_node_fs_namespaceObject.readFileSync)(paths.configPath, 'utf8'));
36768
- } catch (error) {
36769
- console.error(`[${remosidian_PROG}] failed to read ${paths.configPath}: ${error?.message ?? error}`);
36770
- process.exit(2);
36771
- }
36772
- }
36773
-
36774
- function resolvePaths(flags) {
36775
- const dataDir = typeof flags['data-dir'] === 'string' ? flags['data-dir'] : undefined;
36776
- const configPath = typeof flags['config'] === 'string' ? flags['config'] : undefined;
36777
- if (configPath) {
36778
- const paths = createRemosidianPaths(dataDir ?? process.env.REMOSIDIAN_HOME ?? '~/.remosidian');
36779
- return { ...paths, configPath: resolveUserPath(configPath) };
36780
- }
36781
- return createRemosidianPaths(dataDir ?? process.env.REMOSIDIAN_HOME ?? '~/.remosidian');
36782
- }
36783
-
36784
- function createRuntimePaths(paths) {
36785
- const runtimeDir = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'runtime', 'current');
36786
- const logDir = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'logs');
36787
- return {
36788
- runtimeDir,
36789
- runtimeEntryPath: (0,external_node_path_namespaceObject.join)(runtimeDir, 'dist', 'remosidian.mjs'),
36790
- pidPath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'daemon.pid'),
36791
- logDir,
36792
- stdoutLogPath: (0,external_node_path_namespaceObject.join)(logDir, 'daemon.stdout.log'),
36793
- stderrLogPath: (0,external_node_path_namespaceObject.join)(logDir, 'daemon.stderr.log'),
36794
- pairingQrPath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-qr.png'),
36795
- pairingCodePath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-code.txt'),
36796
- };
36797
- }
36798
-
36799
- function readPidFile(pidPath) {
36800
- try {
36801
- const pid = Number((0,external_node_fs_namespaceObject.readFileSync)(pidPath, 'utf8').trim());
36802
- return Number.isFinite(pid) && pid > 0 ? pid : null;
36803
- } catch {
36804
- return null;
36805
- }
36806
- }
36807
-
36808
- function writePidFile(pidPath, pid) {
36809
- (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(pidPath), { recursive: true });
36810
- (0,external_node_fs_namespaceObject.writeFileSync)(pidPath, `${pid}\n`, 'utf8');
36811
- }
36812
-
36813
- function removePidFile(pidPath) {
36814
- (0,external_node_fs_namespaceObject.rmSync)(pidPath, { force: true });
36815
- }
36816
-
36817
- function remosidian_isProcessAlive(pid) {
36818
- try {
36819
- process.kill(pid, 0);
36820
- return true;
36821
- } catch {
36822
- return false;
36823
- }
36824
- }
36825
-
36826
- function currentEntrypointPath() {
36827
- const entryPath = (0,external_node_url_namespaceObject.fileURLToPath)(import.meta.url);
36828
- try {
36829
- return (0,external_node_fs_namespaceObject.realpathSync)(entryPath);
36830
- } catch {
36831
- return entryPath;
36832
- }
36833
- }
36834
-
36835
- function installRuntime(paths) {
36836
- const runtime = createRuntimePaths(paths);
36837
- (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(runtime.runtimeEntryPath), { recursive: true });
36838
- (0,external_node_fs_namespaceObject.copyFileSync)(currentEntrypointPath(), runtime.runtimeEntryPath);
36839
- (0,external_node_fs_namespaceObject.chmodSync)(runtime.runtimeEntryPath, 0o755);
36840
- return runtime;
36841
- }
36842
-
36843
- function resolveRequestedPort(flags, paths) {
36844
- return parsePortFlag(flags) ?? loadConfigOrNull(paths)?.server?.port ?? 41234;
36845
- }
36846
-
36847
- function renderQrTerminal(payload) {
36848
- return new Promise((resolveTerm, rejectTerm) => {
36849
- lib.toString(payload, { type: 'terminal', small: true, errorCorrectionLevel: 'M' }, (err, str) => {
36850
- if (err) {
36851
- rejectTerm(err);
36852
- return;
36853
- }
36854
- resolveTerm(str);
36855
- });
36856
- });
36857
- }
36858
-
36859
- async function writeQrPng(payload, filePath) {
36860
- (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(filePath), { recursive: true });
36861
- await lib.toFile(filePath, payload, { type: 'png', errorCorrectionLevel: 'M', margin: 1, scale: 8 });
36862
- }
36863
-
36864
- function printHumanHealth(health) {
36865
- const lines = [];
36866
- lines.push(`Daemon ${health.daemonId} (${health.displayName})`);
36867
- lines.push(`Version ${health.version}`);
36868
- if (health.runtime) {
36869
- lines.push(`Runtime ${health.runtime === 'cli' ? 'CLI' : 'Obsidian plugin'}`);
36870
- }
36871
- lines.push(`Listening http://${health.canonicalHost}:${health.port}`);
36872
- if (health.relayUrl) {
36873
- lines.push(`Relay ${health.relayUrl}`);
36874
- }
36875
- if (Array.isArray(health.fallbackHosts) && health.fallbackHosts.length > 0) {
36876
- lines.push(`Fallbacks ${health.fallbackHosts.join(', ')}`);
36877
- }
36878
- if (Array.isArray(health.connections) && health.connections.length > 0) {
36879
- lines.push('Connections:');
36880
- for (const conn of health.connections) {
36881
- const proto = conn.secure ? 'wss' : 'ws';
36882
- lines.push(` - ${proto}://${conn.host}:${conn.port} (${conn.source}, ${conn.stability})`);
36883
- }
36884
- }
36885
- if (Array.isArray(health.vaults) && health.vaults.length > 0) {
36886
- lines.push('Vaults:');
36887
- for (const vault of health.vaults) {
36888
- lines.push(` - ${vault.id}: ${vault.root ?? '(unknown root)'}`);
36889
- }
36890
- } else {
36891
- lines.push('Vaults: (none configured — pass --vault or run `remosidian init`)');
36892
- }
36893
- lines.push(`Paired ${health.pairedDevices ?? 0} device(s)`);
36894
- if (health.ai?.configured) {
36895
- lines.push(`AI ${health.ai.agent ?? 'agent'} (local CLI)`);
36896
- } else {
36897
- lines.push('AI (no claude / codex CLI on PATH — install one to enable Ask)');
36898
- }
36899
- return lines.join('\n');
36900
- }
36901
-
36902
- function buildPairUri(payload) {
36903
- // Emit the full schema so @lifeos/client-core's default parser picks it
36904
- // up. We chose JSON-over-base64url because it survives clipboard paste,
36905
- // deep links, and QR data URIs without escaping headaches.
36906
- const full = {
36907
- type: payload.type,
36908
- daemonId: payload.daemonId,
36909
- displayName: payload.displayName,
36910
- canonicalHost: payload.canonicalHost,
36911
- port: payload.port,
36912
- ...(payload.fallbackHosts && payload.fallbackHosts.length > 0 ? { fallbackHosts: payload.fallbackHosts } : {}),
36913
- ...(payload.connections && payload.connections.length > 0 ? { connections: payload.connections } : {}),
36914
- pairNonce: payload.pairNonce,
36915
- expiresAt: payload.expiresAt,
36916
- ...(payload.runtime ? { runtime: payload.runtime } : {}),
36917
- ...(payload.vaults && payload.vaults.length > 0 ? { vaults: payload.vaults } : {}),
36918
- };
36919
- const encoded = Buffer.from(JSON.stringify(full), 'utf8').toString('base64url');
36920
- return `remosidian://pair?v=1&data=${encoded}`;
36921
- }
36922
-
36923
- function normalizePairingConnections(payload) {
36924
- const explicitConnections = Array.isArray(payload.connections)
36925
- ? payload.connections
36926
- .filter(
36927
- (target) =>
36928
- target &&
36929
- typeof target.host === 'string' &&
36930
- Number.isFinite(target.port) &&
36931
- typeof target.source === 'string' &&
36932
- typeof target.stability === 'string',
36933
- )
36934
- .map((target) => ({
36935
- kind: target.kind === 'relay' ? 'relay' : 'direct',
36936
- host: target.host.trim(),
36937
- port: Math.floor(Number(target.port)),
36938
- ...(target.secure === true ? { secure: true } : {}),
36939
- source: target.source,
36940
- stability: target.stability,
36941
- }))
36942
- .filter((target) => target.host && target.port > 0)
36943
- : [];
36944
- if (explicitConnections.length > 0) {
36945
- return explicitConnections;
36946
- }
36947
- const fallbackHosts = Array.isArray(payload.fallbackHosts)
36948
- ? payload.fallbackHosts.filter((host) => typeof host === 'string' && host.trim()).map((host) => host.trim())
36949
- : [];
36950
- const port = Number.isFinite(payload.port) && payload.port > 0 ? Math.floor(payload.port) : 41234;
36951
- return [
36952
- {
36953
- kind: 'direct',
36954
- host: payload.canonicalHost,
36955
- port,
36956
- source: 'configured_host',
36957
- stability: 'stable',
36958
- },
36959
- ...fallbackHosts.map((host) => ({
36960
- kind: 'direct',
36961
- host,
36962
- port,
36963
- source: 'fallback',
36964
- stability: 'stable',
36965
- })),
36966
- ];
36967
- }
36968
-
36969
- function createCompactPairingPayload(payload) {
36970
- const compact = {
36971
- t: 'rp',
36972
- d: payload.daemonId,
36973
- n: payload.displayName,
36974
- h: payload.canonicalHost,
36975
- p: payload.port,
36976
- c: payload.pairNonce,
36977
- e: payload.expiresAt,
36978
- };
36979
- if (Array.isArray(payload.fallbackHosts) && payload.fallbackHosts.length > 0) {
36980
- compact.f = payload.fallbackHosts;
36981
- }
36982
- const connections = normalizePairingConnections(payload);
36983
- if (connections.length > 0) {
36984
- compact.o = connections.map((target) => [
36985
- target.kind,
36986
- target.host,
36987
- target.port,
36988
- target.source,
36989
- target.stability,
36990
- target.secure === true,
36991
- ]);
36992
- }
36993
- if (payload.runtime) {
36994
- compact.r = payload.runtime;
36995
- }
36996
- if (Array.isArray(payload.vaults) && payload.vaults.length > 0) {
36997
- compact.v = payload.vaults;
36998
- }
36999
- return compact;
37000
- }
37001
-
37002
- function buildPairCode(payload) {
37003
- return JSON.stringify(createCompactPairingPayload(payload));
37004
- }
37005
-
37006
- function buildPairDeepLink(pairingCode) {
37007
- return `remosidian://pair?data=${encodeURIComponent(pairingCode)}`;
37008
- }
37009
-
37010
- function formatConnectionSummary(target) {
37011
- const scheme = target.secure === true || target.port === 443 ? 'https' : 'http';
37012
- return `${scheme}://${target.host}:${target.port} (${target.source}, ${target.stability})`;
37013
- }
37014
-
37015
- async function printPairingQr(pairingPayload, paths) {
37016
- const runtime = createRuntimePaths(paths);
37017
- const pairingCode = buildPairCode(pairingPayload);
37018
- const deepLink = buildPairDeepLink(pairingCode);
37019
- const qr = await renderQrTerminal(pairingCode);
37020
- (0,external_node_fs_namespaceObject.writeFileSync)(runtime.pairingCodePath, `${pairingCode}\n`, 'utf8');
37021
- await writeQrPng(pairingCode, runtime.pairingQrPath).catch(() => undefined);
37022
- process.stdout.write(`\nScan this QR code with the Remosidian app:\n\n${qr}\n`);
37023
- process.stdout.write(`If the terminal QR is hard to scan, open this PNG instead:\n${runtime.pairingQrPath}\n`);
37024
- process.stdout.write(`If this device cannot scan, paste this pairing code in Remosidian:\n${pairingCode}\n`);
37025
- process.stdout.write(`Pairing code saved to:\n${runtime.pairingCodePath}\n`);
37026
- process.stdout.write(`Or open this deep link on a device with Remosidian installed:\n${deepLink}\n`);
37027
- }
37028
-
37029
- async function printPairingSummary(port, paths, health = null, flags = {}) {
37030
- const healthPayload = health ?? (await waitForHealth(port));
37031
- if (flags.json) {
37032
- process.stdout.write(`${JSON.stringify({ ok: true, ...healthPayload })}\n`);
37033
- return;
37034
- }
37035
- process.stdout.write(`Remosidian daemon is ready.\n\n`);
37036
- process.stdout.write(`${printHumanHealth(healthPayload)}\n`);
37037
- if (flags['no-qr']) {
37038
- return;
37153
+ function buildServeArgs(flags) {
37154
+ const args = ['serve'];
37155
+ for (const vault of flags.__vaults ?? []) {
37156
+ args.push('--vault', vault);
37039
37157
  }
37040
- const pairingPayload = await postJson(port, '/pair/open');
37041
- const connections = normalizePairingConnections(pairingPayload);
37042
- if (connections.length > 0) {
37043
- process.stdout.write('\nPairing paths:\n');
37044
- for (const target of connections) {
37045
- process.stdout.write(` - ${formatConnectionSummary(target)}\n`);
37158
+ const skipFlags = new Set(['__vaults', 'foreground', 'json']);
37159
+ for (const [key, value] of Object.entries(flags)) {
37160
+ if (skipFlags.has(key)) continue;
37161
+ if (key === 'no-qr') continue;
37162
+ if (value === true) {
37163
+ args.push(`--${key}`);
37164
+ } else if (typeof value === 'string') {
37165
+ args.push(`--${key}`, value);
37046
37166
  }
37047
37167
  }
37048
- await printPairingQr(pairingPayload, paths);
37168
+ args.push('--no-qr');
37169
+ return args;
37170
+ }
37171
+
37172
+
37173
+
37174
+ ;// CONCATENATED MODULE: ./bin/cli-help.mjs
37175
+
37176
+ const cli_help_PROG = 'remosidian';
37177
+ const PRIMARY_NPM_PACKAGE = '@remosidian/cli';
37178
+ const cli_help_ALIAS_NPM_PACKAGE = 'remosidian';
37179
+ function helpText() {
37180
+ return [
37181
+ `${cli_help_PROG} v${VERSION} — remote vault server for Obsidian / LifeOS`,
37182
+ '',
37183
+ 'Package names:',
37184
+ ` ${PRIMARY_NPM_PACKAGE}`,
37185
+ ` ${cli_help_ALIAS_NPM_PACKAGE}`,
37186
+ '',
37187
+ 'Usage:',
37188
+ ` npx ${cli_help_ALIAS_NPM_PACKAGE} Start the current directory as a vault`,
37189
+ ` npx ${cli_help_ALIAS_NPM_PACKAGE} <command> [options]`,
37190
+ ` ${cli_help_PROG} <command> [options]`,
37191
+ '',
37192
+ 'Commands:',
37193
+ ' start Start or reuse the Remosidian background daemon',
37194
+ ' pair Open a one-time pairing window and print a QR code',
37195
+ ' status Print the running daemon health snapshot',
37196
+ ' stop Stop the background daemon',
37197
+ ' reset-pairings Revoke every paired device (local only)',
37198
+ ' init Create ~/.remosidian/config.json interactively',
37199
+ ' help Show this help',
37200
+ '',
37201
+ 'Global options:',
37202
+ ' --data-dir <path> Override $REMOSIDIAN_HOME (default: ~/.remosidian)',
37203
+ ' --config <path> Override $REMOSIDIAN_CONFIG (default: <data-dir>/config.json)',
37204
+ ' --version Print version and exit',
37205
+ '',
37206
+ 'start options:',
37207
+ ' --vault <id>:<path> Use an ad-hoc vault instead of the config file',
37208
+ ' (repeatable; id defaults to folder name)',
37209
+ ' --port <number> Listen on this TCP port (default 41234)',
37210
+ ' --access=local LAN/local only; skip Cloudflare Tunnel and saved relay config',
37211
+ ' --access=remote Use the default Remote Direct path',
37212
+ ' --access=https://<url>',
37213
+ ' Use a custom relay URL for remote access',
37214
+ ' --access=<host>[,<fallback>]',
37215
+ ' Use direct LAN/Tailscale hosts',
37216
+ ' --access= Clear the saved relay URL for this run',
37217
+ ' --force Restart the managed Cloudflare Tunnel before startup',
37218
+ ' --bind <address> Bind to this interface (default 0.0.0.0)',
37219
+ ' --agent <name> AI agent for /Ask: claude or codex (auto-detects)',
37220
+ ' --agent-path <path> Override the agent binary path',
37221
+ ' --personal-agent Expose the Personal Agent in paired mobile apps',
37222
+ ' --pa Short alias for --personal-agent',
37223
+ ' --no-personal-agent Hide the Personal Agent in paired mobile apps',
37224
+ ' --no-pa Short alias for --no-personal-agent',
37225
+ ' --cloudflare Also enable managed Cloudflare access',
37226
+ ' --foreground Run the server in the foreground',
37227
+ ' --json Emit a single JSON line instead of human output',
37228
+ ' --no-qr Skip the pairing QR on startup',
37229
+ '',
37230
+ 'Legacy access aliases still work:',
37231
+ ' --local, --remote, --tunnel, --relay <url>, --host <address>, --fallback-hosts <x>',
37232
+ ].join('\n');
37049
37233
  }
37050
37234
 
37235
+
37236
+
37237
+ ;// CONCATENATED MODULE: ./bin/cli-daemon-api.mjs
37051
37238
  async function probeHealth(port, timeoutMs = 0) {
37052
37239
  const deadline = Date.now() + timeoutMs;
37053
37240
  do {
@@ -37086,23 +37273,179 @@ async function postJson(port, pathname, body = undefined) {
37086
37273
  return payload;
37087
37274
  }
37088
37275
 
37089
- function buildServeArgs(flags) {
37090
- const args = ['serve'];
37091
- for (const vault of flags.__vaults ?? []) {
37092
- args.push('--vault', vault);
37276
+ async function waitForDaemonExit(port, timeoutMs = 5_000) {
37277
+ const deadline = Date.now() + timeoutMs;
37278
+ while (Date.now() < deadline) {
37279
+ const health = await probeHealth(port, 0);
37280
+ if (!health) {
37281
+ return true;
37282
+ }
37283
+ await new Promise((resolveWait) => setTimeout(resolveWait, 200));
37093
37284
  }
37094
- const skipFlags = new Set(['__vaults', 'foreground', 'json']);
37095
- for (const [key, value] of Object.entries(flags)) {
37096
- if (skipFlags.has(key)) continue;
37097
- if (key === 'no-qr') continue;
37098
- if (value === true) {
37099
- args.push(`--${key}`);
37100
- } else if (typeof value === 'string') {
37101
- args.push(`--${key}`, value);
37285
+ return false;
37286
+ }
37287
+
37288
+ ;// CONCATENATED MODULE: ./bin/remosidian.mjs
37289
+
37290
+
37291
+
37292
+
37293
+
37294
+
37295
+
37296
+
37297
+
37298
+
37299
+
37300
+
37301
+
37302
+
37303
+
37304
+
37305
+
37306
+
37307
+ const remosidian_PROG = 'remosidian';
37308
+ const remosidian_ALIAS_NPM_PACKAGE = 'remosidian';
37309
+
37310
+ function resolveEntrypointHref(path) {
37311
+ if (!path) return '';
37312
+ try {
37313
+ return (0,external_node_url_namespaceObject.pathToFileURL)((0,external_node_fs_namespaceObject.realpathSync)((0,external_node_path_namespaceObject.resolve)(path))).href;
37314
+ } catch {
37315
+ return (0,external_node_url_namespaceObject.pathToFileURL)((0,external_node_path_namespaceObject.resolve)(path)).href;
37316
+ }
37317
+ }
37318
+
37319
+ function expandVaultSpec(spec) {
37320
+ if (spec.trim().length === 0) return null;
37321
+ const colon = spec.indexOf(':');
37322
+ let id = '';
37323
+ let root = spec;
37324
+ if (colon > 0 && !/^[a-zA-Z]:[\\/]/.test(spec)) {
37325
+ id = spec.slice(0, colon).trim();
37326
+ root = spec.slice(colon + 1);
37327
+ }
37328
+ if (root.trim().length === 0) return null;
37329
+ const resolved = resolveUserPath(root);
37330
+ if (!id) {
37331
+ id = resolved.split(/[\\/]/).filter(Boolean).pop() ?? 'vault';
37332
+ }
37333
+ return {
37334
+ id,
37335
+ name: id,
37336
+ root: resolved,
37337
+ ignored: ['.obsidian/**', '.git/**', '.DS_Store', '*.swp', '*.tmp'],
37338
+ };
37339
+ }
37340
+
37341
+ function loadConfigOrNull(paths) {
37342
+ if (!(0,external_node_fs_namespaceObject.existsSync)(paths.configPath)) {
37343
+ return null;
37344
+ }
37345
+ try {
37346
+ return JSON.parse((0,external_node_fs_namespaceObject.readFileSync)(paths.configPath, 'utf8'));
37347
+ } catch (error) {
37348
+ console.error(`[${remosidian_PROG}] failed to read ${paths.configPath}: ${error?.message ?? error}`);
37349
+ process.exit(2);
37350
+ }
37351
+ }
37352
+
37353
+ function resolvePaths(flags) {
37354
+ const dataDir = typeof flags['data-dir'] === 'string' ? flags['data-dir'] : undefined;
37355
+ const configPath = typeof flags['config'] === 'string' ? flags['config'] : undefined;
37356
+ if (configPath) {
37357
+ const paths = createRemosidianPaths(dataDir ?? process.env.REMOSIDIAN_HOME ?? '~/.remosidian');
37358
+ return { ...paths, configPath: resolveUserPath(configPath) };
37359
+ }
37360
+ return createRemosidianPaths(dataDir ?? process.env.REMOSIDIAN_HOME ?? '~/.remosidian');
37361
+ }
37362
+
37363
+ function createRuntimePaths(paths) {
37364
+ const runtimeDir = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'runtime', 'current');
37365
+ const logDir = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'logs');
37366
+ return {
37367
+ runtimeDir,
37368
+ runtimeEntryPath: (0,external_node_path_namespaceObject.join)(runtimeDir, 'dist', 'remosidian.mjs'),
37369
+ pidPath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'daemon.pid'),
37370
+ logDir,
37371
+ stdoutLogPath: (0,external_node_path_namespaceObject.join)(logDir, 'daemon.stdout.log'),
37372
+ stderrLogPath: (0,external_node_path_namespaceObject.join)(logDir, 'daemon.stderr.log'),
37373
+ pairingQrPath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-qr.png'),
37374
+ pairingCodePath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-code.txt'),
37375
+ };
37376
+ }
37377
+
37378
+ function readPidFile(pidPath) {
37379
+ try {
37380
+ const pid = Number((0,external_node_fs_namespaceObject.readFileSync)(pidPath, 'utf8').trim());
37381
+ return Number.isFinite(pid) && pid > 0 ? pid : null;
37382
+ } catch {
37383
+ return null;
37384
+ }
37385
+ }
37386
+
37387
+ function writePidFile(pidPath, pid) {
37388
+ (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(pidPath), { recursive: true });
37389
+ (0,external_node_fs_namespaceObject.writeFileSync)(pidPath, `${pid}\n`, 'utf8');
37390
+ }
37391
+
37392
+ function removePidFile(pidPath) {
37393
+ (0,external_node_fs_namespaceObject.rmSync)(pidPath, { force: true });
37394
+ }
37395
+
37396
+ function remosidian_isProcessAlive(pid) {
37397
+ try {
37398
+ process.kill(pid, 0);
37399
+ return true;
37400
+ } catch {
37401
+ return false;
37402
+ }
37403
+ }
37404
+
37405
+ function currentEntrypointPath() {
37406
+ const entryPath = (0,external_node_url_namespaceObject.fileURLToPath)(import.meta.url);
37407
+ try {
37408
+ return (0,external_node_fs_namespaceObject.realpathSync)(entryPath);
37409
+ } catch {
37410
+ return entryPath;
37411
+ }
37412
+ }
37413
+
37414
+ function installRuntime(paths) {
37415
+ const runtime = createRuntimePaths(paths);
37416
+ (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(runtime.runtimeEntryPath), { recursive: true });
37417
+ (0,external_node_fs_namespaceObject.copyFileSync)(currentEntrypointPath(), runtime.runtimeEntryPath);
37418
+ const sidecarDir = (0,external_node_path_namespaceObject.join)((0,external_node_path_namespaceObject.dirname)(currentEntrypointPath()), 'remoteDirect');
37419
+ if ((0,external_node_fs_namespaceObject.existsSync)(sidecarDir))
37420
+ (0,external_node_fs_namespaceObject.cpSync)(sidecarDir, (0,external_node_path_namespaceObject.join)((0,external_node_path_namespaceObject.dirname)(runtime.runtimeEntryPath), 'remoteDirect'), { recursive: true });
37421
+ (0,external_node_fs_namespaceObject.chmodSync)(runtime.runtimeEntryPath, 0o755);
37422
+ return runtime;
37423
+ }
37424
+
37425
+ function resolveRequestedPort(flags, paths) {
37426
+ return parsePortFlag(flags) ?? loadConfigOrNull(paths)?.server?.port ?? 41234;
37427
+ }
37428
+
37429
+ async function printPairingSummary(port, paths, health = null, flags = {}) {
37430
+ const healthPayload = health ?? (await waitForHealth(port));
37431
+ if (flags.json) {
37432
+ process.stdout.write(`${JSON.stringify({ ok: true, ...healthPayload })}\n`);
37433
+ return;
37434
+ }
37435
+ process.stdout.write(`Remosidian daemon is ready.\n\n`);
37436
+ process.stdout.write(`${printHumanHealth(healthPayload)}\n`);
37437
+ if (flags['no-qr']) {
37438
+ return;
37439
+ }
37440
+ const pairingPayload = await postJson(port, '/pair/open');
37441
+ const connections = normalizePairingConnections(pairingPayload);
37442
+ if (connections.length > 0) {
37443
+ process.stdout.write('\nPairing paths:\n');
37444
+ for (const target of connections) {
37445
+ process.stdout.write(` - ${formatConnectionSummary(target)}\n`);
37102
37446
  }
37103
37447
  }
37104
- args.push('--no-qr');
37105
- return args;
37448
+ await printPairingQr(pairingPayload, createRuntimePaths(paths));
37106
37449
  }
37107
37450
 
37108
37451
  function startDetachedDaemon(paths, flags) {
@@ -37138,6 +37481,7 @@ async function cmdServe(flags, paths) {
37138
37481
  server: {
37139
37482
  port: portFlag ?? config?.server?.port ?? 41234,
37140
37483
  advertisedHosts: config?.server?.advertisedHosts ?? 'auto',
37484
+ ...(config?.server?.features ? { features: config.server.features } : {}),
37141
37485
  },
37142
37486
  ...(config?.ai ? { ai: config.ai } : {}),
37143
37487
  };
@@ -37154,6 +37498,17 @@ async function cmdServe(flags, paths) {
37154
37498
  process.exit(2);
37155
37499
  }
37156
37500
 
37501
+ const personalAgentOverride = resolvePersonalAgentOverride(flags);
37502
+ if (personalAgentOverride !== undefined) {
37503
+ finalConfig.server = {
37504
+ ...finalConfig.server,
37505
+ features: {
37506
+ ...(finalConfig.server?.features ?? {}),
37507
+ personalAgent: personalAgentOverride,
37508
+ },
37509
+ };
37510
+ }
37511
+
37157
37512
  const agentFlag = typeof flags.agent === 'string' ? flags.agent.trim() : '';
37158
37513
  const agentPathFlag =
37159
37514
  typeof flags['agent-path'] === 'string' && flags['agent-path'].trim().length > 0 ? flags['agent-path'] : '';
@@ -37178,6 +37533,7 @@ async function cmdServe(flags, paths) {
37178
37533
  : '0.0.0.0';
37179
37534
  const server = await startRemosidianServer({
37180
37535
  config: finalConfig,
37536
+ remoteDirect: connectionConfig.localOnly !== true,
37181
37537
  dataDir: paths.dataDir,
37182
37538
  host: bindHost,
37183
37539
  port,
@@ -37211,7 +37567,7 @@ async function cmdServe(flags, paths) {
37211
37567
  const openResp = await fetch(`http://127.0.0.1:${server.port}/pair/open`, { method: 'POST' });
37212
37568
  if (openResp.ok) {
37213
37569
  const payload = await openResp.json();
37214
- await printPairingQr(payload, paths);
37570
+ await printPairingQr(payload, createRuntimePaths(paths));
37215
37571
  }
37216
37572
  } catch (error) {
37217
37573
  process.stderr.write(`[${remosidian_PROG}] pair payload not available: ${error?.message ?? error}\n`);
@@ -37240,7 +37596,29 @@ async function cmdStart(flags, paths) {
37240
37596
  return;
37241
37597
  }
37242
37598
  const port = resolveRequestedPort(flags, paths);
37243
- const existing = await probeHealth(port, 1_000);
37599
+ let existing = await probeHealth(port, 1_000);
37600
+ const personalAgentOverride = resolvePersonalAgentOverride(flags);
37601
+ if (existing && personalAgentOverride !== undefined && existing.features?.personalAgent !== personalAgentOverride) {
37602
+ const runtime = createRuntimePaths(paths);
37603
+ const pid = readPidFile(runtime.pidPath);
37604
+ if (!pid || !remosidian_isProcessAlive(pid)) {
37605
+ failUsage(
37606
+ `The daemon on port ${port} was not started by this CLI, so its Personal Agent setting cannot be changed automatically.`,
37607
+ 'Stop that foreground/service process, then run this command again.',
37608
+ );
37609
+ }
37610
+ process.kill(pid, 'SIGTERM');
37611
+ const stopped = await waitForDaemonExit(port);
37612
+ if (!stopped) {
37613
+ throw new Error(`Timed out restarting Remosidian daemon on 127.0.0.1:${port}`);
37614
+ }
37615
+ removePidFile(runtime.pidPath);
37616
+ existing = null;
37617
+ }
37618
+ if (existing && shouldRestartDaemon(existing, flags)) {
37619
+ await cmdStop(flags, paths);
37620
+ existing = null;
37621
+ }
37244
37622
  if (existing) {
37245
37623
  if (!flags.json) {
37246
37624
  process.stdout.write('Remosidian daemon is already running. Reusing it.\n\n');
@@ -37306,18 +37684,6 @@ async function cmdStatus(flags, paths) {
37306
37684
  }
37307
37685
  }
37308
37686
 
37309
- async function waitForDaemonExit(port, timeoutMs = 5_000) {
37310
- const deadline = Date.now() + timeoutMs;
37311
- while (Date.now() < deadline) {
37312
- const health = await probeHealth(port, 0);
37313
- if (!health) {
37314
- return true;
37315
- }
37316
- await new Promise((resolveWait) => setTimeout(resolveWait, 200));
37317
- }
37318
- return false;
37319
- }
37320
-
37321
37687
  async function cmdStop(flags, paths) {
37322
37688
  const port = resolveRequestedPort(flags, paths);
37323
37689
  const runtime = createRuntimePaths(paths);
@@ -37331,7 +37697,7 @@ async function cmdStop(flags, paths) {
37331
37697
  await waitForDaemonExit(port);
37332
37698
  removePidFile(runtime.pidPath);
37333
37699
  stopTunnel({ tunnelStatePath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'tunnel.json') });
37334
- process.stdout.write('Remosidian daemon stopped.\n');
37700
+ if (!flags.json) process.stdout.write('Remosidian daemon stopped.\n');
37335
37701
  return;
37336
37702
  }
37337
37703
  removePidFile(runtime.pidPath);
@@ -37343,7 +37709,7 @@ async function cmdStop(flags, paths) {
37343
37709
  process.exit(1);
37344
37710
  }
37345
37711
  stopTunnel({ tunnelStatePath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'tunnel.json') });
37346
- process.stdout.write('Remosidian daemon is not running.\n');
37712
+ if (!flags.json) process.stdout.write('Remosidian daemon is not running.\n');
37347
37713
  }
37348
37714
 
37349
37715
  async function cmdResetPairings(flags, paths) {
@@ -37474,4 +37840,5 @@ var __webpack_exports__parseAccessValue = __webpack_exports__.bo;
37474
37840
  var __webpack_exports__parseArgs = __webpack_exports__.Zy;
37475
37841
  var __webpack_exports__resolveEntrypointHref = __webpack_exports__.Mf;
37476
37842
  var __webpack_exports__resolveInvocation = __webpack_exports__.t0;
37477
- 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__hasLegacyAccessArgs as hasLegacyAccessArgs, __webpack_exports__helpText as helpText, __webpack_exports__main as main, __webpack_exports__normalizeCliFlags as normalizeCliFlags, __webpack_exports__normalizePortValue as normalizePortValue, __webpack_exports__parseAccessValue as parseAccessValue, __webpack_exports__parseArgs as parseArgs, __webpack_exports__resolveEntrypointHref as resolveEntrypointHref, __webpack_exports__resolveInvocation as resolveInvocation };
37843
+ var __webpack_exports__resolvePersonalAgentOverride = __webpack_exports__.kr;
37844
+ 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__hasLegacyAccessArgs as hasLegacyAccessArgs, __webpack_exports__helpText as helpText, __webpack_exports__main as main, __webpack_exports__normalizeCliFlags as normalizeCliFlags, __webpack_exports__normalizePortValue as normalizePortValue, __webpack_exports__parseAccessValue as parseAccessValue, __webpack_exports__parseArgs as parseArgs, __webpack_exports__resolveEntrypointHref as resolveEntrypointHref, __webpack_exports__resolveInvocation as resolveInvocation, __webpack_exports__resolvePersonalAgentOverride as resolvePersonalAgentOverride };