wherdr 1.3.1 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/nitro.json +1 -1
- package/.output/public/_nuxt/{AgentView.C4YOwvZG.css → AgentView.DzFWBd2A.css} +1 -1
- package/.output/public/_nuxt/B4Yk2tB1.js +23 -0
- package/.output/public/_nuxt/BBw_0a4M.js +6 -0
- package/.output/public/_nuxt/{B6qBWHWB.js → BSy6OCIY.js} +1 -1
- package/.output/public/_nuxt/BX5PM2JQ.js +1 -0
- package/.output/public/_nuxt/BgPDnf2S.js +1 -0
- package/.output/public/_nuxt/BkOxQzbS.js +2 -0
- package/.output/public/_nuxt/{WLQjK8uJ.js → CKhgznq4.js} +45 -46
- package/.output/public/_nuxt/{C2T9fQ4f.js → CP8o925U.js} +1 -1
- package/.output/public/_nuxt/CkbmecwS.js +1 -0
- package/.output/public/_nuxt/Cr3SrGky.js +1 -0
- package/.output/public/_nuxt/DAg9fiVh.js +89 -0
- package/.output/public/_nuxt/DT8qZ0Wv.js +1 -0
- package/.output/public/_nuxt/DaaJIRB3.js +1 -0
- package/.output/public/_nuxt/DcJi16XA.js +1 -0
- package/.output/public/_nuxt/VpjCgSXu.js +36 -0
- package/.output/public/_nuxt/builds/latest.json +1 -1
- package/.output/public/_nuxt/builds/meta/c735b6c0-1bd0-47d7-a208-d310f0a06287.json +1 -0
- package/.output/public/_nuxt/dPxB2L94.js +89 -0
- package/.output/public/_nuxt/{entry.cKR16VqG.css → entry.CGWKmYU3.css} +1 -1
- package/.output/public/_nuxt/hePW80VL.js +1 -0
- package/.output/public/_nuxt/xYX85SPI.js +1 -0
- package/.output/public/sw.js +1 -1
- package/.output/server/chunks/_/branding.mjs +1 -1
- package/.output/server/chunks/_/filePaths.mjs +25 -0
- package/.output/server/chunks/_/files.mjs +204 -0
- package/.output/server/chunks/_/machineOrder.mjs +1 -1
- package/.output/server/chunks/_/onboarding.mjs +1 -1
- package/.output/server/chunks/_/package.mjs +97 -0
- package/.output/server/chunks/_/pluginPolicy.mjs +93 -0
- package/.output/server/chunks/_/plugins.mjs +4 -94
- package/.output/server/chunks/_/projectBoard.mjs +120 -29
- package/.output/server/chunks/_/projectDocs.mjs +106 -0
- package/.output/server/chunks/_/projectInbox.mjs +135 -0
- package/.output/server/chunks/_/projectThreads.mjs +46 -0
- package/.output/server/chunks/_/projectThreads2.mjs +61 -0
- package/.output/server/chunks/_/projectsActions.mjs +1 -1
- package/.output/server/chunks/_/projectsPlugin.mjs +1 -1
- package/.output/server/chunks/_/restart.mjs +2 -312
- package/.output/server/chunks/_/reveal.mjs +2 -24
- package/.output/server/chunks/_/selfupdate.mjs +83 -0
- package/.output/server/chunks/_/updates.mjs +91 -0
- package/.output/server/chunks/_/worktrees.mjs +1 -1
- package/.output/server/chunks/_/ws.mjs +1 -1
- package/.output/server/chunks/nitro/nitro.mjs +2660 -1061
- package/.output/server/chunks/raw/bump-homebrew.mjs +1 -1
- package/.output/server/chunks/raw/herdr-plugin.mjs +1 -1
- package/.output/server/chunks/raw/release-ready.mjs +5 -0
- package/.output/server/chunks/routes/api/_..._.mjs +6 -2
- package/.output/server/chunks/routes/api/agents.post.mjs +6 -2
- package/.output/server/chunks/routes/api/ask-tab.post.mjs +6 -2
- package/.output/server/chunks/routes/api/attach.post.mjs +6 -2
- package/.output/server/chunks/routes/api/auth/disable.post.mjs +6 -2
- package/.output/server/chunks/routes/api/auth/lock-all.post.mjs +6 -2
- package/.output/server/chunks/routes/api/auth/lock.post.mjs +6 -2
- package/.output/server/chunks/routes/api/auth/login/options.post.mjs +6 -2
- package/.output/server/chunks/routes/api/auth/login/verify.post.mjs +6 -2
- package/.output/server/chunks/routes/api/auth/max-session.post.mjs +6 -2
- package/.output/server/chunks/routes/api/auth/register/options.post.mjs +6 -2
- package/.output/server/chunks/routes/api/auth/register/verify.post.mjs +6 -2
- package/.output/server/chunks/routes/api/auth/status.get.mjs +6 -2
- package/.output/server/chunks/routes/api/board.get.mjs +147 -0
- package/.output/server/chunks/routes/api/changes.get.mjs +7 -3
- package/.output/server/chunks/routes/api/chat/image.get.mjs +7 -3
- package/.output/server/chunks/routes/api/chat.get.mjs +7 -3
- package/.output/server/chunks/routes/api/choose.post.mjs +34 -4
- package/.output/server/chunks/routes/api/claude-statusline.post.mjs +7 -3
- package/.output/server/chunks/routes/api/close/status.get.mjs +7 -3
- package/.output/server/chunks/routes/api/close.post.mjs +6 -2
- package/.output/server/chunks/routes/api/codex-update.post.mjs +7 -3
- package/.output/server/chunks/routes/api/commands.get.mjs +7 -3
- package/.output/server/chunks/routes/api/config.get.mjs +8 -3
- package/.output/server/chunks/routes/api/dirs.get.mjs +7 -3
- package/.output/server/chunks/routes/api/dismiss-stopped.post.mjs +43 -0
- package/.output/server/chunks/routes/api/dismiss.post.mjs +7 -3
- package/.output/server/chunks/routes/api/effort.post.mjs +7 -3
- package/.output/server/chunks/routes/api/efforts.get.mjs +7 -3
- package/.output/server/chunks/routes/api/file.post.mjs +7 -3
- package/.output/server/chunks/routes/api/files/download.get.mjs +53 -0
- package/.output/server/chunks/routes/api/files/read.get.mjs +45 -0
- package/.output/server/chunks/routes/api/files.get.mjs +44 -0
- package/.output/server/chunks/routes/api/gitroot.get.mjs +7 -3
- package/.output/server/chunks/routes/api/health.get.mjs +36 -0
- package/.output/server/chunks/routes/api/index.get.mjs +6 -2
- package/.output/server/chunks/routes/api/input.post.mjs +18 -4
- package/.output/server/chunks/routes/api/interrupt.post.mjs +7 -3
- package/.output/server/chunks/routes/api/isgit.get.mjs +7 -3
- package/.output/server/chunks/routes/api/machine/awake.get.mjs +7 -3
- package/.output/server/chunks/routes/api/machine/awake.post.mjs +7 -3
- package/.output/server/chunks/routes/api/machine/order.get.mjs +6 -2
- package/.output/server/chunks/routes/api/machine/order.post.mjs +6 -2
- package/.output/server/chunks/routes/api/machine/rename.post.mjs +7 -3
- package/.output/server/chunks/routes/api/menu.post.mjs +7 -3
- package/.output/server/chunks/routes/api/model.post.mjs +7 -3
- package/.output/server/chunks/routes/api/models.get.mjs +7 -3
- package/.output/server/chunks/routes/api/nav.post.mjs +10 -5
- package/.output/server/chunks/routes/api/onboarding.get.mjs +6 -2
- package/.output/server/chunks/routes/api/onboarding.post.mjs +6 -2
- package/.output/server/chunks/routes/api/phone.get.mjs +6 -7
- package/.output/server/chunks/routes/api/phone.post.mjs +6 -7
- package/.output/server/chunks/routes/api/plugins/actions.get.mjs +7 -2
- package/.output/server/chunks/routes/api/plugins/invoke.post.mjs +7 -2
- package/.output/server/chunks/routes/api/plugins/profiles.get.mjs +7 -2
- package/.output/server/chunks/routes/api/plugins/projects.get.mjs +7 -3
- package/.output/server/chunks/routes/api/plugins/projects.post.mjs +7 -3
- package/.output/server/chunks/routes/api/plugins/thread-limits.get.mjs +7 -3
- package/.output/server/chunks/routes/api/plugins/thread-limits.post.mjs +7 -3
- package/.output/server/chunks/routes/api/project/inbox.get.mjs +43 -0
- package/.output/server/chunks/routes/api/project/inbox.post.mjs +43 -0
- package/.output/server/chunks/routes/api/project/memory.get.mjs +42 -0
- package/.output/server/chunks/routes/api/project/report.get.mjs +7 -3
- package/.output/server/chunks/routes/api/project/routines.get.mjs +43 -0
- package/.output/server/chunks/routes/api/project/settings.get.mjs +41 -0
- package/.output/server/chunks/routes/api/project/thread.get.mjs +44 -0
- package/.output/server/chunks/routes/api/project/thread.post.mjs +45 -0
- package/.output/server/chunks/routes/api/prompt.post.mjs +26 -4
- package/.output/server/chunks/routes/api/push/quiet.post.mjs +7 -3
- package/.output/server/chunks/routes/api/push/subscribe.post.mjs +7 -3
- package/.output/server/chunks/routes/api/push/test.post.mjs +7 -3
- package/.output/server/chunks/routes/api/push/unsubscribe.post.mjs +7 -3
- package/.output/server/chunks/routes/api/quotas.get.mjs +7 -3
- package/.output/server/chunks/routes/api/rename.post.mjs +6 -2
- package/.output/server/chunks/routes/api/requeue.post.mjs +7 -3
- package/.output/server/chunks/routes/api/restart.get.mjs +6 -2
- package/.output/server/chunks/routes/api/restart.post.mjs +6 -2
- package/.output/server/chunks/routes/api/reveal.post.mjs +7 -2
- package/.output/server/chunks/routes/api/screen-key.post.mjs +7 -3
- package/.output/server/chunks/routes/api/screen.get.mjs +10 -4
- package/.output/server/chunks/routes/api/search.get.mjs +7 -3
- package/.output/server/chunks/routes/api/seen.post.mjs +7 -3
- package/.output/server/chunks/routes/api/sessions.get.mjs +7 -3
- package/.output/server/chunks/routes/api/sessions.post.mjs +7 -3
- package/.output/server/chunks/routes/api/space.post.mjs +19 -15
- package/.output/server/chunks/routes/api/state.get.mjs +7 -3
- package/.output/server/chunks/routes/api/unqueue.post.mjs +7 -3
- package/.output/server/chunks/routes/api/update/status.get.mjs +38 -0
- package/.output/server/chunks/routes/api/update.get.mjs +12 -51
- package/.output/server/chunks/routes/api/update.post.mjs +41 -0
- package/.output/server/chunks/routes/api/upload.post.mjs +7 -3
- package/.output/server/chunks/routes/api/worktrees/remove.post.mjs +6 -2
- package/.output/server/chunks/routes/api/worktrees.get.mjs +6 -2
- package/.output/server/chunks/routes/favicon.ico.get.mjs +6 -2
- package/.output/server/chunks/routes/icons/_name_.get.mjs +7 -3
- package/.output/server/chunks/routes/renderer.mjs +8 -4
- package/.output/server/chunks/routes/uploads/_...name_.get.mjs +7 -3
- package/.output/server/chunks/routes/ws/_..._.mjs +7 -3
- package/.output/server/chunks/routes/ws/events.mjs +7 -3
- package/.output/server/chunks/routes/ws/term.mjs +9 -5
- package/.output/server/chunks/virtual/precomputed.mjs +1 -1
- package/.output/server/index.mjs +7 -3
- package/.output/server/package.json +1 -1
- package/README.md +440 -46
- package/bin/install-docker-skill.sh +20 -0
- package/bin/lib/commands.mjs +47 -9
- package/bin/lib/core.mjs +18 -2
- package/bin/lib/install-skill.sh +100 -0
- package/bin/lib/skill.mjs +16 -0
- package/bin/lib/tailnet.d.mts +12 -3
- package/bin/lib/tailnet.mjs +103 -25
- package/bin/lib/updater.mjs +320 -0
- package/bin/lib/verify-release.mjs +20 -0
- package/package.json +4 -2
- package/skills/wherdr/SKILL.md +180 -0
- package/.output/public/_nuxt/BDNMzG2s.js +0 -1
- package/.output/public/_nuxt/BMIe8p_3.js +0 -2
- package/.output/public/_nuxt/BpQH8Ho1.js +0 -1
- package/.output/public/_nuxt/BrAeJIZR.js +0 -30
- package/.output/public/_nuxt/C12eXl5z.js +0 -1
- package/.output/public/_nuxt/CXG-cOdh.js +0 -1
- package/.output/public/_nuxt/CYxHnMNH.js +0 -83
- package/.output/public/_nuxt/CctS2Xn6.js +0 -65
- package/.output/public/_nuxt/CiYar6hg.js +0 -6
- package/.output/public/_nuxt/DLLQgP0d.js +0 -1
- package/.output/public/_nuxt/Dwh-l2rQ.js +0 -17
- package/.output/public/_nuxt/bLP7Jj-b.js +0 -1
- package/.output/public/_nuxt/builds/meta/6466b0ab-7935-460c-abdf-2ea632dae6d0.json +0 -1
- package/.output/public/_nuxt/whFLy_Ne.js +0 -23
- package/.output/server/chunks/_/phone.mjs +0 -343
- package/.output/server/chunks/routes/ws/mirror.mjs +0 -157
|
@@ -19,10 +19,14 @@ import webpush from 'web-push';
|
|
|
19
19
|
import os from 'node:os';
|
|
20
20
|
import { spawn, execFile } from 'node:child_process';
|
|
21
21
|
import net from 'node:net';
|
|
22
|
-
import readline from 'node:readline';
|
|
23
22
|
import fs$1 from 'node:fs/promises';
|
|
24
|
-
import
|
|
23
|
+
import readline from 'node:readline';
|
|
24
|
+
import dns from 'node:dns';
|
|
25
|
+
import { promisify } from 'node:util';
|
|
26
|
+
import QRCode from 'qrcode-terminal/vendor/QRCode/index.js';
|
|
27
|
+
import QRErrorCorrectLevel from 'qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js';
|
|
25
28
|
import { verifyAuthenticationResponse, generateAuthenticationOptions, verifyRegistrationResponse, generateRegistrationOptions } from '@simplewebauthn/server';
|
|
29
|
+
import { fileURLToPath } from 'node:url';
|
|
26
30
|
|
|
27
31
|
crypto.webcrypto?.subtle || {};
|
|
28
32
|
const randomUUID = () => {
|
|
@@ -5892,12 +5896,12 @@ function createRouter$1(options = {}) {
|
|
|
5892
5896
|
}
|
|
5893
5897
|
return {
|
|
5894
5898
|
ctx,
|
|
5895
|
-
lookup: (path) => lookup(ctx, normalizeTrailingSlash(path)),
|
|
5899
|
+
lookup: (path) => lookup$1(ctx, normalizeTrailingSlash(path)),
|
|
5896
5900
|
insert: (path, data) => insert(ctx, normalizeTrailingSlash(path), data),
|
|
5897
5901
|
remove: (path) => remove(ctx, normalizeTrailingSlash(path))
|
|
5898
5902
|
};
|
|
5899
5903
|
}
|
|
5900
|
-
function lookup(ctx, path) {
|
|
5904
|
+
function lookup$1(ctx, path) {
|
|
5901
5905
|
const staticPathNode = ctx.staticRoutesMap[path];
|
|
5902
5906
|
if (staticPathNode) {
|
|
5903
5907
|
return staticPathNode.data;
|
|
@@ -8286,7 +8290,7 @@ function createStorage(options = {}) {
|
|
|
8286
8290
|
}
|
|
8287
8291
|
context.watching = true;
|
|
8288
8292
|
for (const mountpoint in context.mounts) {
|
|
8289
|
-
context.unwatch[mountpoint] = await watch(
|
|
8293
|
+
context.unwatch[mountpoint] = await watch$1(
|
|
8290
8294
|
context.mounts[mountpoint],
|
|
8291
8295
|
onChange,
|
|
8292
8296
|
mountpoint
|
|
@@ -8582,7 +8586,7 @@ function createStorage(options = {}) {
|
|
|
8582
8586
|
}
|
|
8583
8587
|
context.mounts[base] = driver;
|
|
8584
8588
|
if (context.watching) {
|
|
8585
|
-
Promise.resolve(watch(driver, onChange, base)).then((unwatcher) => {
|
|
8589
|
+
Promise.resolve(watch$1(driver, onChange, base)).then((unwatcher) => {
|
|
8586
8590
|
context.unwatch[base] = unwatcher;
|
|
8587
8591
|
}).catch(console.error);
|
|
8588
8592
|
}
|
|
@@ -8629,7 +8633,7 @@ function createStorage(options = {}) {
|
|
|
8629
8633
|
};
|
|
8630
8634
|
return storage;
|
|
8631
8635
|
}
|
|
8632
|
-
function watch(driver, onChange, base) {
|
|
8636
|
+
function watch$1(driver, onChange, base) {
|
|
8633
8637
|
return driver.watch ? driver.watch((event, key) => onChange(event, base + key)) : () => {
|
|
8634
8638
|
};
|
|
8635
8639
|
}
|
|
@@ -8642,83 +8646,87 @@ async function dispose(driver) {
|
|
|
8642
8646
|
const _assets = {
|
|
8643
8647
|
["scripts:build-nerd-font.py"]: {
|
|
8644
8648
|
import: () => import('../raw/build-nerd-font.mjs').then(r => r.default || r),
|
|
8645
|
-
meta: {"type":"text/plain; charset=utf-8","etag":"\"13aa-ys9YUI0kyQcBWrUDXjdBVLF8Z3E\"","mtime":"2026-10-
|
|
8649
|
+
meta: {"type":"text/plain; charset=utf-8","etag":"\"13aa-ys9YUI0kyQcBWrUDXjdBVLF8Z3E\"","mtime":"2026-10-12T02:42:41.115Z"}
|
|
8646
8650
|
},
|
|
8647
8651
|
["scripts:build-terminal-font.py"]: {
|
|
8648
8652
|
import: () => import('../raw/build-terminal-font.mjs').then(r => r.default || r),
|
|
8649
|
-
meta: {"type":"text/plain; charset=utf-8","etag":"\"cf5-BDqHg2xDFu+6dg84HBWtyBtrEoU\"","mtime":"2026-10-
|
|
8653
|
+
meta: {"type":"text/plain; charset=utf-8","etag":"\"cf5-BDqHg2xDFu+6dg84HBWtyBtrEoU\"","mtime":"2026-10-12T02:42:41.115Z"}
|
|
8650
8654
|
},
|
|
8651
8655
|
["scripts:bump-homebrew.sh"]: {
|
|
8652
8656
|
import: () => import('../raw/bump-homebrew.mjs').then(r => r.default || r),
|
|
8653
|
-
meta: {"type":"application/x-sh","etag":"\"
|
|
8657
|
+
meta: {"type":"application/x-sh","etag":"\"8b5-n/EiMEIUoHPuQl2XTJU0tSNn7FY\"","mtime":"2026-10-12T02:42:41.115Z"}
|
|
8654
8658
|
},
|
|
8655
8659
|
["scripts:check-leaks.mjs"]: {
|
|
8656
8660
|
import: () => import('../raw/check-leaks.mjs').then(r => r.default || r),
|
|
8657
|
-
meta: {"type":"text/javascript; charset=utf-8","etag":"\"35b3-41HZueHfw7xuicx4q83qwdTEpbc\"","mtime":"2026-10-
|
|
8661
|
+
meta: {"type":"text/javascript; charset=utf-8","etag":"\"35b3-41HZueHfw7xuicx4q83qwdTEpbc\"","mtime":"2026-10-12T02:42:41.115Z"}
|
|
8658
8662
|
},
|
|
8659
8663
|
["scripts:dev.wherdr.open.plist"]: {
|
|
8660
8664
|
import: () => import('../raw/dev.wherdr.open.mjs').then(r => r.default || r),
|
|
8661
|
-
meta: {"type":"text/plain; charset=utf-8","etag":"\"1de-12VO2xq63mciP26rEzRh3cGfEkI\"","mtime":"2026-10-
|
|
8665
|
+
meta: {"type":"text/plain; charset=utf-8","etag":"\"1de-12VO2xq63mciP26rEzRh3cGfEkI\"","mtime":"2026-10-12T02:42:41.115Z"}
|
|
8662
8666
|
},
|
|
8663
8667
|
["scripts:docker-entrypoint.sh"]: {
|
|
8664
8668
|
import: () => import('../raw/docker-entrypoint.mjs').then(r => r.default || r),
|
|
8665
|
-
meta: {"type":"application/x-sh","etag":"\"3f3-8om3/mMriIIJYO+YcdXNJsv8tkI\"","mtime":"2026-10-
|
|
8669
|
+
meta: {"type":"application/x-sh","etag":"\"3f3-8om3/mMriIIJYO+YcdXNJsv8tkI\"","mtime":"2026-10-12T02:42:41.115Z"}
|
|
8666
8670
|
},
|
|
8667
8671
|
["scripts:herdr-plugin.sh"]: {
|
|
8668
8672
|
import: () => import('../raw/herdr-plugin.mjs').then(r => r.default || r),
|
|
8669
|
-
meta: {"type":"application/x-sh","etag":"\"
|
|
8673
|
+
meta: {"type":"application/x-sh","etag":"\"9b85-YfAcqPKGuMgKmGSVqEIuA91sAE4\"","mtime":"2026-10-12T02:42:41.115Z"}
|
|
8670
8674
|
},
|
|
8671
8675
|
["scripts:install-claude-statusline.sh"]: {
|
|
8672
8676
|
import: () => import('../raw/install-claude-statusline.mjs').then(r => r.default || r),
|
|
8673
|
-
meta: {"type":"application/x-sh","etag":"\"db6-p7KGlvbZ+Qjt+JiT5yVtU4w3kfg\"","mtime":"2026-10-
|
|
8677
|
+
meta: {"type":"application/x-sh","etag":"\"db6-p7KGlvbZ+Qjt+JiT5yVtU4w3kfg\"","mtime":"2026-10-12T02:42:41.115Z"}
|
|
8674
8678
|
},
|
|
8675
8679
|
["scripts:install-host-open.sh"]: {
|
|
8676
8680
|
import: () => import('../raw/install-host-open.mjs').then(r => r.default || r),
|
|
8677
|
-
meta: {"type":"application/x-sh","etag":"\"103a-/zplHeQSS471/Vz3quwajHmzFQs\"","mtime":"2026-10-
|
|
8681
|
+
meta: {"type":"application/x-sh","etag":"\"103a-/zplHeQSS471/Vz3quwajHmzFQs\"","mtime":"2026-10-12T02:42:41.115Z"}
|
|
8678
8682
|
},
|
|
8679
8683
|
["scripts:pack-output.mjs"]: {
|
|
8680
8684
|
import: () => import('../raw/pack-output.mjs').then(r => r.default || r),
|
|
8681
|
-
meta: {"type":"text/javascript; charset=utf-8","etag":"\"54b-eU4eNNIxIecG1kwaLdtiTYODSSI\"","mtime":"2026-10-
|
|
8685
|
+
meta: {"type":"text/javascript; charset=utf-8","etag":"\"54b-eU4eNNIxIecG1kwaLdtiTYODSSI\"","mtime":"2026-10-12T02:42:41.115Z"}
|
|
8686
|
+
},
|
|
8687
|
+
["scripts:release-ready.mjs"]: {
|
|
8688
|
+
import: () => import('../raw/release-ready.mjs').then(r => r.default || r),
|
|
8689
|
+
meta: {"type":"text/javascript; charset=utf-8","etag":"\"a44-ceu/5BQO5TUWzE/HNW2C/LvdCqw\"","mtime":"2026-10-12T02:42:41.115Z"}
|
|
8682
8690
|
},
|
|
8683
8691
|
["scripts:reveal.sh"]: {
|
|
8684
8692
|
import: () => import('../raw/reveal.mjs').then(r => r.default || r),
|
|
8685
|
-
meta: {"type":"application/x-sh","etag":"\"72e-h/LTEU7exXdiR3GKqhj71UOwAqQ\"","mtime":"2026-10-
|
|
8693
|
+
meta: {"type":"application/x-sh","etag":"\"72e-h/LTEU7exXdiR3GKqhj71UOwAqQ\"","mtime":"2026-10-12T02:42:41.115Z"}
|
|
8686
8694
|
},
|
|
8687
8695
|
["scripts:wherdr-open.sh"]: {
|
|
8688
8696
|
import: () => import('../raw/wherdr-open.mjs').then(r => r.default || r),
|
|
8689
|
-
meta: {"type":"application/x-sh","etag":"\"b5b-U5OyQk3hziU9KiUyB/1FT5TkOxk\"","mtime":"2026-10-
|
|
8697
|
+
meta: {"type":"application/x-sh","etag":"\"b5b-U5OyQk3hziU9KiUyB/1FT5TkOxk\"","mtime":"2026-10-12T02:42:41.115Z"}
|
|
8690
8698
|
},
|
|
8691
8699
|
["server:branding:favicon.ico"]: {
|
|
8692
8700
|
import: () => import('../raw/favicon.mjs').then(r => r.default || r),
|
|
8693
|
-
meta: {"type":"image/vnd.microsoft.icon","etag":"\"3aee-Oem7XTWv+9HEwv9u/3K5QcIjS3E\"","mtime":"2026-10-
|
|
8701
|
+
meta: {"type":"image/vnd.microsoft.icon","etag":"\"3aee-Oem7XTWv+9HEwv9u/3K5QcIjS3E\"","mtime":"2026-10-12T02:42:41.120Z"}
|
|
8694
8702
|
},
|
|
8695
8703
|
["server:branding:icons:apple-touch-icon.png"]: {
|
|
8696
8704
|
import: () => import('../raw/apple-touch-icon.mjs').then(r => r.default || r),
|
|
8697
|
-
meta: {"type":"image/png","etag":"\"1f8-AVRrBnAhalXrGkFPGdvl0fM7DH8\"","mtime":"2026-10-
|
|
8705
|
+
meta: {"type":"image/png","etag":"\"1f8-AVRrBnAhalXrGkFPGdvl0fM7DH8\"","mtime":"2026-10-12T02:42:41.120Z"}
|
|
8698
8706
|
},
|
|
8699
8707
|
["server:branding:icons:favicon-32.png"]: {
|
|
8700
8708
|
import: () => import('../raw/favicon-32.mjs').then(r => r.default || r),
|
|
8701
|
-
meta: {"type":"image/png","etag":"\"a3-hAEc+Ymq1aYS3kFLyZK/H18Pge8\"","mtime":"2026-10-
|
|
8709
|
+
meta: {"type":"image/png","etag":"\"a3-hAEc+Ymq1aYS3kFLyZK/H18Pge8\"","mtime":"2026-10-12T02:42:41.120Z"}
|
|
8702
8710
|
},
|
|
8703
8711
|
["server:branding:icons:icon-192.png"]: {
|
|
8704
8712
|
import: () => import('../raw/icon-192.mjs').then(r => r.default || r),
|
|
8705
|
-
meta: {"type":"image/png","etag":"\"222-kioUoPaSCUaRhKKWw0PRtFbAhxk\"","mtime":"2026-10-
|
|
8713
|
+
meta: {"type":"image/png","etag":"\"222-kioUoPaSCUaRhKKWw0PRtFbAhxk\"","mtime":"2026-10-12T02:42:41.120Z"}
|
|
8706
8714
|
},
|
|
8707
8715
|
["server:branding:icons:icon-512-maskable.png"]: {
|
|
8708
8716
|
import: () => import('../raw/icon-512-maskable.mjs').then(r => r.default || r),
|
|
8709
|
-
meta: {"type":"image/png","etag":"\"84b-mCXgbBkXk7sqOhmJ8hjp80SN3Qk\"","mtime":"2026-10-
|
|
8717
|
+
meta: {"type":"image/png","etag":"\"84b-mCXgbBkXk7sqOhmJ8hjp80SN3Qk\"","mtime":"2026-10-12T02:42:41.120Z"}
|
|
8710
8718
|
},
|
|
8711
8719
|
["server:branding:icons:icon-512.png"]: {
|
|
8712
8720
|
import: () => import('../raw/icon-512.mjs').then(r => r.default || r),
|
|
8713
|
-
meta: {"type":"image/png","etag":"\"8fc-ZwRMqAqQZeLLx4iNTZxs09C4Q1I\"","mtime":"2026-10-
|
|
8721
|
+
meta: {"type":"image/png","etag":"\"8fc-ZwRMqAqQZeLLx4iNTZxs09C4Q1I\"","mtime":"2026-10-12T02:42:41.120Z"}
|
|
8714
8722
|
},
|
|
8715
8723
|
["server:branding:icons:icon-maskable.svg"]: {
|
|
8716
8724
|
import: () => import('../raw/icon-maskable.mjs').then(r => r.default || r),
|
|
8717
|
-
meta: {"type":"image/svg+xml","etag":"\"4a1-7XtnuN5ZOwiwIjpgpj+/Rg9LssY\"","mtime":"2026-10-
|
|
8725
|
+
meta: {"type":"image/svg+xml","etag":"\"4a1-7XtnuN5ZOwiwIjpgpj+/Rg9LssY\"","mtime":"2026-10-12T02:42:41.120Z"}
|
|
8718
8726
|
},
|
|
8719
8727
|
["server:branding:icons:icon.svg"]: {
|
|
8720
8728
|
import: () => import('../raw/icon.mjs').then(r => r.default || r),
|
|
8721
|
-
meta: {"type":"image/svg+xml","etag":"\"49c-O1XxkWwKTf6VBoC0i4TEPdGPsCw\"","mtime":"2026-10-
|
|
8729
|
+
meta: {"type":"image/svg+xml","etag":"\"49c-O1XxkWwKTf6VBoC0i4TEPdGPsCw\"","mtime":"2026-10-12T02:42:41.120Z"}
|
|
8722
8730
|
}
|
|
8723
8731
|
};
|
|
8724
8732
|
|
|
@@ -9621,7 +9629,7 @@ function _expandFromEnv(value) {
|
|
|
9621
9629
|
const _inlineRuntimeConfig = {
|
|
9622
9630
|
"app": {
|
|
9623
9631
|
"baseURL": "/",
|
|
9624
|
-
"buildId": "
|
|
9632
|
+
"buildId": "c735b6c0-1bd0-47d7-a208-d310f0a06287",
|
|
9625
9633
|
"buildAssetsDir": "/_nuxt/",
|
|
9626
9634
|
"cdnURL": ""
|
|
9627
9635
|
},
|
|
@@ -10197,6 +10205,7 @@ const HERDR_CHILD_ENV = Object.fromEntries(
|
|
|
10197
10205
|
const AGENT_KINDS = (env.AGENT_KINDS || "pi,claude,codex,gemini,cursor,devin,agy,cline,omp,mastracode,opencode,copilot,kimi,kiro,droid,amp,grok,hermes,kilo,qodercli,qwen,letta,maki,muse").split(",").map((s) => s.trim()).filter(Boolean);
|
|
10198
10206
|
const APP_URL_FILE = path$1.join(DATA_DIR, "app-url.json");
|
|
10199
10207
|
const ENV_APP_URL = /^https:\/\//i.test(env.APP_URL || "") ? env.APP_URL : "";
|
|
10208
|
+
const BOOT_APP_URL = env.APP_URL || "";
|
|
10200
10209
|
function savedAppUrl() {
|
|
10201
10210
|
var _a;
|
|
10202
10211
|
try {
|
|
@@ -10209,8 +10218,13 @@ function savedAppUrl() {
|
|
|
10209
10218
|
if (!ENV_APP_URL && savedAppUrl()) env.APP_URL = savedAppUrl();
|
|
10210
10219
|
const appUrl = () => env.APP_URL || "mailto:herdr-web@localhost";
|
|
10211
10220
|
const HOST_LABEL = (env.HOST_LABEL || os.hostname()).trim().slice(0, 40);
|
|
10221
|
+
function machineLabel(tailnetName2, fallback = HOST_LABEL, containerName = IN_DOCKER && !(env.HOST_LABEL || "").trim()) {
|
|
10222
|
+
const short = (tailnetName2 || "").split(".")[0].trim().slice(0, 40);
|
|
10223
|
+
return containerName && short ? short : fallback;
|
|
10224
|
+
}
|
|
10212
10225
|
const PASSKEY_USER = (env.PASSKEY_USER || "wherdr").trim().slice(0, 64);
|
|
10213
10226
|
const POLL_MS = Number(env.POLL_MS || 1e3);
|
|
10227
|
+
const TAILNET_REFRESH_MS = Number(env.TAILNET_REFRESH_MS || 3e4);
|
|
10214
10228
|
const NOTIFY_SETTLE_MS = Number(env.NOTIFY_SETTLE_MS || 4e3);
|
|
10215
10229
|
const UPLOAD_DIR = env.UPLOAD_DIR || path$1.join(HOME, ".cache/herdr-web/uploads");
|
|
10216
10230
|
const UPLOAD_TTL_MS = 7 * 864e5;
|
|
@@ -10542,20 +10556,20 @@ function herdrSock(path, method, params = {}, timeoutMs = 15e3) {
|
|
|
10542
10556
|
sock.on("close", () => finish(new HerdrError("closed", "Herdr connection closed without a response")));
|
|
10543
10557
|
});
|
|
10544
10558
|
}
|
|
10545
|
-
async function agentPrompt(
|
|
10559
|
+
async function agentPrompt(target2, text) {
|
|
10546
10560
|
try {
|
|
10547
|
-
await herdr("agent.prompt", { target, text });
|
|
10561
|
+
await herdr("agent.prompt", { target: target2, text });
|
|
10548
10562
|
} catch (e) {
|
|
10549
10563
|
if (!(e instanceof HerdrError) || e.code !== "agent_not_ready") throw e;
|
|
10550
10564
|
try {
|
|
10551
|
-
await herdr("agent.get", { target });
|
|
10565
|
+
await herdr("agent.get", { target: target2 });
|
|
10552
10566
|
} catch {
|
|
10553
10567
|
throw e;
|
|
10554
10568
|
}
|
|
10555
|
-
await herdr("agent.prompt", { target, text });
|
|
10569
|
+
await herdr("agent.prompt", { target: target2, text });
|
|
10556
10570
|
}
|
|
10557
10571
|
}
|
|
10558
|
-
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
10572
|
+
const sleep$1 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
10559
10573
|
|
|
10560
10574
|
var __defProp$5 = Object.defineProperty;
|
|
10561
10575
|
var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -10707,7 +10721,7 @@ class SessionGate {
|
|
|
10707
10721
|
stats() {
|
|
10708
10722
|
return { execs: this.execs, streams: this.streams, queued: this.waiting.length, peak: this.peak, refusals: this.refusals };
|
|
10709
10723
|
}
|
|
10710
|
-
// A long-lived session (terminal
|
|
10724
|
+
// A long-lived session (terminal) opens; call the result when it closes.
|
|
10711
10725
|
openStream() {
|
|
10712
10726
|
this.streams++;
|
|
10713
10727
|
this.track();
|
|
@@ -12835,6 +12849,61 @@ async function ompCommandTemplates(fs, home) {
|
|
|
12835
12849
|
return out;
|
|
12836
12850
|
}
|
|
12837
12851
|
|
|
12852
|
+
const PASTE_MIN_LINES = 12;
|
|
12853
|
+
const PASTE_MIN_CHARS = 1500;
|
|
12854
|
+
const lineCount = (s) => s ? s.split("\n").length : 0;
|
|
12855
|
+
const isLongPaste = (s) => {
|
|
12856
|
+
const t = s.trim();
|
|
12857
|
+
return lineCount(t) > PASTE_MIN_LINES || t.length > PASTE_MIN_CHARS;
|
|
12858
|
+
};
|
|
12859
|
+
const BLOCK = /<pasted_content(?:\s[^>]*)?>\n?([\s\S]*?)\n?<\/pasted_content(?:\s[^>]*)?>/g;
|
|
12860
|
+
function pastedBlocks(raw) {
|
|
12861
|
+
if (!raw.includes("<pasted_content")) return [];
|
|
12862
|
+
if (!raw.replace(BLOCK, "").replace(/\[Image #\d+[^\]]*\]/g, "").trim()) return [];
|
|
12863
|
+
return [...raw.matchAll(BLOCK)].map((m) => m[1].trim()).filter(isLongPaste);
|
|
12864
|
+
}
|
|
12865
|
+
const QUEUED_CHARS = 4e3;
|
|
12866
|
+
const cutShort = (text) => text.endsWith("\u2026") || text.length === QUEUED_CHARS;
|
|
12867
|
+
function cut(text, block, open) {
|
|
12868
|
+
const body = open ? text.replace(/…$/, "") : null;
|
|
12869
|
+
let at = text.indexOf(block);
|
|
12870
|
+
let end = at + block.length;
|
|
12871
|
+
let part = block;
|
|
12872
|
+
if (at < 0) {
|
|
12873
|
+
if (body === null) return null;
|
|
12874
|
+
at = body.indexOf(block.slice(0, 200));
|
|
12875
|
+
if (at < 0 || !block.startsWith(body.slice(at))) return null;
|
|
12876
|
+
part = body.slice(at).trimEnd();
|
|
12877
|
+
if (!isLongPaste(part)) return null;
|
|
12878
|
+
end = text.length;
|
|
12879
|
+
}
|
|
12880
|
+
const short = body !== null && end >= body.trimEnd().length;
|
|
12881
|
+
if (short) end = text.length;
|
|
12882
|
+
return { rest: (text.slice(0, at).replace(/\s+$/, "") + "\n" + text.slice(end).replace(/^\s+/, "")).trim(), part, short };
|
|
12883
|
+
}
|
|
12884
|
+
function splitPasted(text, listed = [], known = [], whole = false) {
|
|
12885
|
+
let rest = text;
|
|
12886
|
+
const pastes = [];
|
|
12887
|
+
const longKnown = known.filter(isLongPaste).sort((a, b) => b.length - a.length);
|
|
12888
|
+
let open = cutShort(text);
|
|
12889
|
+
const take = (block) => {
|
|
12890
|
+
const r = cut(rest, block, open);
|
|
12891
|
+
if (r === null) return;
|
|
12892
|
+
rest = r.rest;
|
|
12893
|
+
if (r.short) open = false;
|
|
12894
|
+
const sent = whole && r.short ? longKnown.filter((k) => k.startsWith(r.part)) : [];
|
|
12895
|
+
pastes.push(sent.length === 1 ? sent[0] : r.part);
|
|
12896
|
+
};
|
|
12897
|
+
for (const b of listed) {
|
|
12898
|
+
if (longKnown.some((k) => k !== b && b.includes(k))) continue;
|
|
12899
|
+
take(b);
|
|
12900
|
+
}
|
|
12901
|
+
for (const k of longKnown) take(k);
|
|
12902
|
+
if (!pastes.length) return { text, pastes };
|
|
12903
|
+
pastes.sort((a, b) => text.indexOf(a.slice(0, 200)) - text.indexOf(b.slice(0, 200)));
|
|
12904
|
+
return { text: rest, pastes };
|
|
12905
|
+
}
|
|
12906
|
+
|
|
12838
12907
|
const WINDOW_BYTES = 2 * 1024 * 1024;
|
|
12839
12908
|
const MAX_READ_BYTES = 24 * 1024 * 1024;
|
|
12840
12909
|
const PAGE_ITEMS = 120;
|
|
@@ -12853,6 +12922,10 @@ function humanText(t) {
|
|
|
12853
12922
|
if (u !== s) return u;
|
|
12854
12923
|
return isNoise(s) ? null : s;
|
|
12855
12924
|
}
|
|
12925
|
+
const pastedOf = (...parts) => {
|
|
12926
|
+
const b = parts.flatMap(pastedBlocks).map((s) => clip(s));
|
|
12927
|
+
return b.length ? { pasted: b } : {};
|
|
12928
|
+
};
|
|
12856
12929
|
const normMsg = (t) => stripImageTags(t).replace(/\s+/g, " ").trim().toLowerCase();
|
|
12857
12930
|
function sameMsg(queued, said) {
|
|
12858
12931
|
const q = normMsg(queued).slice(0, 60);
|
|
@@ -13030,10 +13103,11 @@ function parseClaude(lines, home = "") {
|
|
|
13030
13103
|
const ts = d.timestamp || null;
|
|
13031
13104
|
if (d.type === "attachment" && d.attachment && d.attachment.type === "queued_command" && d.attachment.commandMode === "prompt" && (d.attachment.origin || {}).kind === "human") {
|
|
13032
13105
|
const parts = Array.isArray(d.attachment.prompt) ? d.attachment.prompt : [{ type: "text", text: String(d.attachment.prompt || "") }];
|
|
13033
|
-
const
|
|
13106
|
+
const texts = parts.filter((p) => p.type === "text").map((p) => String(p.text || ""));
|
|
13107
|
+
const text = stripImageTags(unwrapPasted(texts.join("\n")));
|
|
13034
13108
|
const images = parts.filter((p) => p.type === "image").length;
|
|
13035
13109
|
if (text || images) {
|
|
13036
|
-
items.push({ role: "user", text: clip(text), images, ref: images ? ref : void 0, ts: d.attachment.timestamp || ts });
|
|
13110
|
+
items.push({ role: "user", text: clip(text), images, ref: images ? ref : void 0, ts: d.attachment.timestamp || ts, ...pastedOf(...texts) });
|
|
13037
13111
|
pendingCmd = null;
|
|
13038
13112
|
said(text, d.attachment.timestamp || ts, images);
|
|
13039
13113
|
}
|
|
@@ -13084,7 +13158,7 @@ function parseClaude(lines, home = "") {
|
|
|
13084
13158
|
} else {
|
|
13085
13159
|
const text2 = humanText(content);
|
|
13086
13160
|
if (text2) {
|
|
13087
|
-
items.push({ role: "user", text: clip(text2), ts });
|
|
13161
|
+
items.push({ role: "user", text: clip(text2), ts, ...pastedOf(content) });
|
|
13088
13162
|
pendingCmd = null;
|
|
13089
13163
|
said(text2, ts);
|
|
13090
13164
|
noteSaid(d);
|
|
@@ -13094,6 +13168,7 @@ function parseClaude(lines, home = "") {
|
|
|
13094
13168
|
}
|
|
13095
13169
|
if (!Array.isArray(content)) continue;
|
|
13096
13170
|
let text = "";
|
|
13171
|
+
const raw = [];
|
|
13097
13172
|
let images = 0;
|
|
13098
13173
|
let toolImages = content.filter((p) => p && p.type === "image").length;
|
|
13099
13174
|
for (const part of content) {
|
|
@@ -13111,12 +13186,13 @@ function parseClaude(lines, home = "") {
|
|
|
13111
13186
|
if (special(String(part.text || ""), ts)) continue;
|
|
13112
13187
|
const t = humanText(part.text);
|
|
13113
13188
|
if (t === null) continue;
|
|
13189
|
+
raw.push(String(part.text || ""));
|
|
13114
13190
|
if (/^\[Request interrupted/.test(t)) items.push({ role: "system", text: "Interrupted", ts });
|
|
13115
13191
|
else text += (text ? "\n" : "") + unwrapPasted(t.replace(/\[Image #\d+\]\s*/g, ""));
|
|
13116
13192
|
} else if (part.type === "image") images++;
|
|
13117
13193
|
}
|
|
13118
13194
|
if (text.trim() || images) {
|
|
13119
|
-
items.push({ role: "user", text: clip(text.trim()), images, ref: images ? ref : void 0, ts });
|
|
13195
|
+
items.push({ role: "user", text: clip(text.trim()), images, ref: images ? ref : void 0, ts, ...pastedOf(...raw) });
|
|
13120
13196
|
pendingCmd = null;
|
|
13121
13197
|
said(text, ts, images);
|
|
13122
13198
|
noteSaid(d);
|
|
@@ -13651,7 +13727,7 @@ function createTranscripts({ home, herdr: herdr2, fs = localFs }) {
|
|
|
13651
13727
|
return { start, items, queue: queue || [] };
|
|
13652
13728
|
}
|
|
13653
13729
|
async function tail(file, kind, size, from) {
|
|
13654
|
-
const key = `${file}|${from}`;
|
|
13730
|
+
const key = `${file}|${kind}|${from}`;
|
|
13655
13731
|
const c = parseCache.get(key);
|
|
13656
13732
|
if (c && c.size === size) return c.r;
|
|
13657
13733
|
let r;
|
|
@@ -13688,15 +13764,16 @@ function createTranscripts({ home, herdr: herdr2, fs = localFs }) {
|
|
|
13688
13764
|
}
|
|
13689
13765
|
const fileId = path$1.basename(loc.file);
|
|
13690
13766
|
const base = { available: true, file: fileId, session: loc.session, guessed: Boolean(loc.guessed) };
|
|
13691
|
-
|
|
13767
|
+
const kind = transcriptKind(loc.file) || pane.agent;
|
|
13768
|
+
if (kind === "omp") await refreshOmpTemplates();
|
|
13692
13769
|
if (opts.before !== null && opts.before !== void 0) {
|
|
13693
|
-
const r2 = await readBackwards(loc.file, Math.min(opts.before, st.size),
|
|
13770
|
+
const r2 = await readBackwards(loc.file, Math.min(opts.before, st.size), kind);
|
|
13694
13771
|
return { ...base, older: true, items: r2.items, start: r2.start };
|
|
13695
13772
|
}
|
|
13696
13773
|
const from = opts.from === void 0 ? null : opts.from;
|
|
13697
13774
|
const token = `${fileId}:${st.size}:${from}`;
|
|
13698
13775
|
if (opts.since && opts.since === token) return { ...base, unchanged: true, token };
|
|
13699
|
-
const r = await tail(loc.file,
|
|
13776
|
+
const r = await tail(loc.file, kind, st.size, from);
|
|
13700
13777
|
return { ...base, token, items: r.items, start: r.start, queue: r.queue || [] };
|
|
13701
13778
|
}
|
|
13702
13779
|
async function preview(pane) {
|
|
@@ -13964,14 +14041,25 @@ var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key,
|
|
|
13964
14041
|
var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
13965
14042
|
const fsp$3 = fs.promises;
|
|
13966
14043
|
const LOCAL_LABEL_FILE = path$1.join(DATA_DIR, "machine-label.txt");
|
|
13967
|
-
function
|
|
14044
|
+
function renamedLabel() {
|
|
13968
14045
|
try {
|
|
13969
|
-
return fs.readFileSync(LOCAL_LABEL_FILE, "utf8").trim().slice(0, 40)
|
|
14046
|
+
return fs.readFileSync(LOCAL_LABEL_FILE, "utf8").trim().slice(0, 40);
|
|
13970
14047
|
} catch {
|
|
13971
|
-
return
|
|
14048
|
+
return "";
|
|
13972
14049
|
}
|
|
13973
14050
|
}
|
|
13974
14051
|
const localMachineLabel = () => localMachine.label;
|
|
14052
|
+
let tailnetLabel = "";
|
|
14053
|
+
function adoptTailnetName(name) {
|
|
14054
|
+
const label = machineLabel(name);
|
|
14055
|
+
if (label === HOST_LABEL || label === tailnetLabel) return;
|
|
14056
|
+
tailnetLabel = label;
|
|
14057
|
+
if (renamedLabel() || localMachine.label === label) return;
|
|
14058
|
+
localMachine.label = label;
|
|
14059
|
+
for (const other of sessions.values()) if (!(other instanceof RemoteMachine)) other.label = label;
|
|
14060
|
+
log(`machine name: ${label} (from Tailscale)`);
|
|
14061
|
+
changed();
|
|
14062
|
+
}
|
|
13975
14063
|
function localOs() {
|
|
13976
14064
|
const p = os.platform();
|
|
13977
14065
|
return p === "darwin" ? "Darwin" : p === "linux" ? "Linux" : p;
|
|
@@ -13979,7 +14067,7 @@ function localOs() {
|
|
|
13979
14067
|
const localMachine = {
|
|
13980
14068
|
key: LOCAL,
|
|
13981
14069
|
profileId: null,
|
|
13982
|
-
label:
|
|
14070
|
+
label: renamedLabel() || HOST_LABEL,
|
|
13983
14071
|
local: true,
|
|
13984
14072
|
target: null,
|
|
13985
14073
|
session: HERDR_SESSION || "default",
|
|
@@ -14015,7 +14103,7 @@ function localMachineId() {
|
|
|
14015
14103
|
}
|
|
14016
14104
|
return selfId;
|
|
14017
14105
|
}
|
|
14018
|
-
const selfNames = () => [HOST_LABEL, os.hostname(), ...SELF_HOSTS].filter(Boolean);
|
|
14106
|
+
const selfNames = () => [HOST_LABEL, os.hostname(), tailnetLabel, ...SELF_HOSTS].filter(Boolean);
|
|
14019
14107
|
class SkipMachine extends Error {
|
|
14020
14108
|
}
|
|
14021
14109
|
const PROBE_SCRIPT = `echo "home=$HOME"
|
|
@@ -14168,7 +14256,7 @@ ${cmd}`;
|
|
|
14168
14256
|
child.stdin.end(opts.input || void 0);
|
|
14169
14257
|
});
|
|
14170
14258
|
}
|
|
14171
|
-
// Long-lived session (terminal
|
|
14259
|
+
// Long-lived session (terminal): counted in the budget, never queued.
|
|
14172
14260
|
spawnHerdr(args) {
|
|
14173
14261
|
const sess = this.session && this.session !== "default" ? ["--session", this.session] : [];
|
|
14174
14262
|
const cmd = `exec ${[this.bin || "herdr", ...sess, ...args].map(shq).join(" ")}`;
|
|
@@ -14657,16 +14745,6 @@ function splitPath(id) {
|
|
|
14657
14745
|
}
|
|
14658
14746
|
const PANE_DIRECTIONS = ["left", "right", "up", "down"];
|
|
14659
14747
|
|
|
14660
|
-
function mirrorSize(o) {
|
|
14661
|
-
var _a, _b;
|
|
14662
|
-
const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, Math.round(v)));
|
|
14663
|
-
const longest = Math.max(0, ...String(o.text || "").split("\n").map((l) => [...l.replace(/\s+$/, "")].length));
|
|
14664
|
-
return {
|
|
14665
|
-
cols: clamp(Math.max(longest, ((_a = o.rect) == null ? void 0 : _a.width) || 0, 20), 20, 300),
|
|
14666
|
-
rows: clamp(o.rows || ((_b = o.rect) == null ? void 0 : _b.height) || 24, 5, 200)
|
|
14667
|
-
};
|
|
14668
|
-
}
|
|
14669
|
-
|
|
14670
14748
|
const SLUG$1 = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
14671
14749
|
const validProject = (v) => typeof v === "string" && SLUG$1.test(v.trim()) ? v.trim() : void 0;
|
|
14672
14750
|
function projectToken(tokens) {
|
|
@@ -14689,6 +14767,16 @@ function projectOf(p) {
|
|
|
14689
14767
|
function isCoordinator(p) {
|
|
14690
14768
|
return Boolean(p.agent && projectOf(p) && !isProjectThread(p));
|
|
14691
14769
|
}
|
|
14770
|
+
function pickCoordinator(panes, slug, machine) {
|
|
14771
|
+
const key = slug.toLowerCase();
|
|
14772
|
+
const rank = (p) => (0) + (norm$4(p.cwd).toLowerCase().endsWith(`/.herdr-projects/${key}`) ? 0 : 1);
|
|
14773
|
+
let best = null;
|
|
14774
|
+
for (const p of panes) {
|
|
14775
|
+
if (!isCoordinator(p) || projectOf(p).toLowerCase() !== key) continue;
|
|
14776
|
+
if (!best || rank(p) < rank(best)) best = p;
|
|
14777
|
+
}
|
|
14778
|
+
return best;
|
|
14779
|
+
}
|
|
14692
14780
|
|
|
14693
14781
|
function cleanLabel(label) {
|
|
14694
14782
|
return (label != null ? label : "").replace(/[\u200B-\u200D\u2060\uFEFF]/g, "").replace(/[\u2800\s]+/g, " ").trim();
|
|
@@ -14833,7 +14921,7 @@ function foregroundCommand(info) {
|
|
|
14833
14921
|
const SCREEN_MS$1 = 3e3;
|
|
14834
14922
|
function keepReading(c, rev, strict, watch, now) {
|
|
14835
14923
|
if (!c || c.rev !== rev || c.strict !== strict) return false;
|
|
14836
|
-
const live = watch || !strict || Boolean(c.choices || c.screen || c.menu);
|
|
14924
|
+
const live = watch || !strict && !c.usageLimit || Boolean(c.choices || c.screen || c.menu || c.interaction === "terminal");
|
|
14837
14925
|
return !(live && now - c.at >= SCREEN_MS$1);
|
|
14838
14926
|
}
|
|
14839
14927
|
|
|
@@ -15202,6 +15290,65 @@ function clickMovesOnly(menu) {
|
|
|
15202
15290
|
return Boolean(enter && !/^(?:select|confirm|choose|open|resume|continue)$/i.test(enter.label.trim()));
|
|
15203
15291
|
}
|
|
15204
15292
|
|
|
15293
|
+
function parseUsageLimit(text, agent) {
|
|
15294
|
+
var _a, _b;
|
|
15295
|
+
if (agent !== "claude") return null;
|
|
15296
|
+
const lines = text.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]|\r/g, "").split("\n");
|
|
15297
|
+
const field = lines.findLastIndex((l, i) => /^\s*❯(?:\s|$)/.test(l) && /^\s*─{3,}/.test(lines[i - 1] || ""));
|
|
15298
|
+
if (field < 0) return null;
|
|
15299
|
+
const rule = lines.findIndex((l, i) => i > field && /^\s*─{3,}/.test(l));
|
|
15300
|
+
if (rule < 0) return null;
|
|
15301
|
+
const tail = lines.slice(rule + 1);
|
|
15302
|
+
if (tail.length > 10 || tail.some((l) => /[╭┌]─|\benter to (?:select|confirm)\b|^\s*[❯›]\s*\d+\./i.test(l))) return null;
|
|
15303
|
+
const footer = tail.join("\n");
|
|
15304
|
+
const limit = footer.match(/^\s*(?:⚠\s*)?Usage limit reached(?:\s*·\s*limit resets ([^\n]+))?\s*$/m);
|
|
15305
|
+
if (!limit) return null;
|
|
15306
|
+
const resume = footer.match(/^\s*Continuing automatically (.+?)\s*·\s*esc to cancel\b/im);
|
|
15307
|
+
return {
|
|
15308
|
+
resets: ((_a = limit[1]) == null ? void 0 : _a.trim()) || ((_b = resume == null ? void 0 : resume[1]) == null ? void 0 : _b.trim()) || null,
|
|
15309
|
+
automatic: Boolean(resume),
|
|
15310
|
+
credits: /\/usage-credits to continue now\b/.test(footer),
|
|
15311
|
+
resetLink: /Use your limit reset to reset it now: clau\.de\/reset\b/.test(lines.slice(Math.max(0, field - 16), field).join("\n"))
|
|
15312
|
+
};
|
|
15313
|
+
}
|
|
15314
|
+
|
|
15315
|
+
function terminalException(text) {
|
|
15316
|
+
var _a;
|
|
15317
|
+
const lines = text.trimEnd().split("\n");
|
|
15318
|
+
const at = lines.findLastIndex((l) => /^\s*(?:Agent Command Center|Usage)\s*$/.test(l));
|
|
15319
|
+
if (at < 0) return false;
|
|
15320
|
+
const body = lines.slice(at + 1).filter((l) => l.trim());
|
|
15321
|
+
const legend = (_a = body.pop()) == null ? void 0 : _a.trim();
|
|
15322
|
+
if (lines[at].trim() === "Agent Command Center") {
|
|
15323
|
+
return legend === "Use arrows to navigate, Enter to open, Escape to return." && body.some((l) => /^\s*›\s+\S/.test(l)) && body.every((l) => /^(?:›\s+)?\S.* {2,}\S/.test(l.trim()));
|
|
15324
|
+
}
|
|
15325
|
+
return legend === "enter open \xB7 esc back" && body.length === 2 && /^[ \t›]*1\. View analytics {2,}Usage history[ \t]*$/.test(body[0]) && /^[ \t›]*2\. Redeem reset {2,}Check availability[ \t]*$/.test(body[1]);
|
|
15326
|
+
}
|
|
15327
|
+
function usesTerminalException(o) {
|
|
15328
|
+
return o.agent === "codex" && o.status !== "unknown" && o.status !== "working" && terminalException(o.text);
|
|
15329
|
+
}
|
|
15330
|
+
function confirmedInput(text) {
|
|
15331
|
+
const lines = text.trimEnd().split("\n");
|
|
15332
|
+
const at = lines.findLastIndex((l) => /^\s*[❯›](?:\s|$)/.test(l) || /^!(?:\s|$)/.test(l) || /^╰─(?:\s|$)/.test(l));
|
|
15333
|
+
if (at < 0) return false;
|
|
15334
|
+
const line = lines[at];
|
|
15335
|
+
const below = lines.slice(at + 1).join("\n");
|
|
15336
|
+
if (/\b(?:enter|esc|escape)\s+(?:to\s+)?(?:select|open|back|cancel|confirm|close)\b/i.test(below)) return false;
|
|
15337
|
+
if (/^\s*[❯›]\s+\d{1,2}\.\s/.test(line)) return false;
|
|
15338
|
+
if (/^╰─(?:\s|$)/.test(line)) return true;
|
|
15339
|
+
if (/^\s*›(?:\s|$)/.test(line)) return /for shortcuts|context left|\b(?:gpt-[\w.-]+|o[1-9](?:-[\w.-]+)?)\b.*·/i.test(below);
|
|
15340
|
+
return /^\s*─{3,}/.test(lines[at - 1] || "");
|
|
15341
|
+
}
|
|
15342
|
+
function classifyScreen(o) {
|
|
15343
|
+
if (o.status === "working") return "working";
|
|
15344
|
+
if (usesTerminalException(o)) return "terminal";
|
|
15345
|
+
if (o.status !== "unknown" && parseUsageLimit(o.text, o.agent)) return "native";
|
|
15346
|
+
if (o.choices || o.menu || o.screen || o.settings) return "native";
|
|
15347
|
+
if (o.status === "unknown" || !hasTranscript(o.agent) || !o.text.trim()) return "unknown";
|
|
15348
|
+
if (confirmedInput(o.text)) return "input";
|
|
15349
|
+
return "terminal";
|
|
15350
|
+
}
|
|
15351
|
+
|
|
15205
15352
|
const GLYPHS = "\xB7\u2722\u2733\u2736\u273B\u273D\u273A\u2739\u2737\u2738*\u2217";
|
|
15206
15353
|
const LINE_RE = new RegExp(`^([${GLYPHS}])\\s+(\\p{L}[\\p{L}'\u2019\\-]*(?: \\p{L}[\\p{L}'\u2019\\-]*){0,3})(?:\u2026|\\.\\.\\.)(?:\\s+\\((.*)\\))?\\s*$`, "u");
|
|
15207
15354
|
const RULE_RE$1 = /^\s*[─━]{8,}\s*$/;
|
|
@@ -15434,20 +15581,28 @@ function checkQueue(list, status, now, menu = true) {
|
|
|
15434
15581
|
}
|
|
15435
15582
|
return changed;
|
|
15436
15583
|
}
|
|
15584
|
+
function giveBackHeld(list) {
|
|
15585
|
+
for (const q of list || []) if (q.held) {
|
|
15586
|
+
q.failed = true;
|
|
15587
|
+
q.back = true;
|
|
15588
|
+
}
|
|
15589
|
+
}
|
|
15590
|
+
const leftForGood = (p, before) => !p.agent && !p.leaving && Boolean(before == null ? void 0 : before.leaving);
|
|
15437
15591
|
const publicEntry = (q) => ({
|
|
15438
15592
|
id: q.id,
|
|
15439
15593
|
text: q.text,
|
|
15440
15594
|
at: q.at,
|
|
15441
15595
|
...q.held && !q.failed ? { state: "held" } : {},
|
|
15442
15596
|
...q.failed ? { state: "failed" } : {},
|
|
15443
|
-
...(q.held || q.failed) && q.busy ? { reason: "busy" } : q.failed && q.noInput ? { reason: "no_input" } : {}
|
|
15597
|
+
...(q.held || q.failed) && q.busy ? { reason: "busy" } : q.failed && q.noInput ? { reason: "no_input" } : {},
|
|
15598
|
+
...q.back ? { back: true } : {}
|
|
15444
15599
|
});
|
|
15445
15600
|
function loadQueued(raw, now) {
|
|
15446
15601
|
if (!raw || typeof raw !== "object") return [];
|
|
15447
15602
|
const out = [];
|
|
15448
15603
|
for (const [pane, list] of Object.entries(raw)) {
|
|
15449
15604
|
if (!Array.isArray(list)) continue;
|
|
15450
|
-
const ok = list.filter((q) => Boolean(q) && typeof q.id === "string" && typeof q.text === "string" && typeof q.at === "number" && now - q.at < QUEUED_TTL_MS).map((q) => ({ id: q.id, text: q.text, at: q.at, ...q.held ? { held: true } : {}, ...q.failed ? { failed: true } : {}, ...q.busy ? { busy: true } : {} }));
|
|
15605
|
+
const ok = list.filter((q) => Boolean(q) && typeof q.id === "string" && typeof q.text === "string" && typeof q.at === "number" && now - q.at < QUEUED_TTL_MS).map((q) => ({ id: q.id, text: q.text, at: q.at, ...q.held ? { held: true } : {}, ...q.failed ? { failed: true } : {}, ...q.busy ? { busy: true } : {}, ...q.back ? { back: true } : {} }));
|
|
15451
15606
|
if (ok.length) out.push([pane, ok]);
|
|
15452
15607
|
}
|
|
15453
15608
|
return out;
|
|
@@ -15760,6 +15915,114 @@ async function interruptAgent(d, p) {
|
|
|
15760
15915
|
return res;
|
|
15761
15916
|
}
|
|
15762
15917
|
|
|
15918
|
+
const PASTES_MAX = 100;
|
|
15919
|
+
const PASTES_MAX_CHARS = 2e6;
|
|
15920
|
+
const PASTES_TTL_MS = 30 * 864e5;
|
|
15921
|
+
const live = (p, now) => now - p.at < PASTES_TTL_MS;
|
|
15922
|
+
const PER_MESSAGE = 20;
|
|
15923
|
+
function pastesAt(text, ranges) {
|
|
15924
|
+
if (!Array.isArray(ranges)) return [];
|
|
15925
|
+
const out = [];
|
|
15926
|
+
for (const r of ranges.slice(0, PER_MESSAGE)) {
|
|
15927
|
+
if (!Array.isArray(r)) continue;
|
|
15928
|
+
const [at, len] = r;
|
|
15929
|
+
if (typeof at !== "number" || typeof len !== "number" || !Number.isInteger(at) || !Number.isInteger(len)) continue;
|
|
15930
|
+
if (at < 0 || len <= 0 || at + len > text.length) continue;
|
|
15931
|
+
const b = text.slice(at, at + len).trim();
|
|
15932
|
+
if (isLongPaste(b) && !out.includes(b)) out.push(b);
|
|
15933
|
+
}
|
|
15934
|
+
return out;
|
|
15935
|
+
}
|
|
15936
|
+
function addPastes(list, blocks, now) {
|
|
15937
|
+
if (!blocks.length) return list;
|
|
15938
|
+
let total = 0;
|
|
15939
|
+
return [...blocks.map((text) => ({ text, at: now })), ...list.filter((p) => !blocks.includes(p.text) && live(p, now))].slice(0, PASTES_MAX).filter((p) => {
|
|
15940
|
+
total += p.text.length;
|
|
15941
|
+
return total <= PASTES_MAX_CHARS;
|
|
15942
|
+
});
|
|
15943
|
+
}
|
|
15944
|
+
function loadPastes(raw, now) {
|
|
15945
|
+
if (!Array.isArray(raw)) return [];
|
|
15946
|
+
const out = [];
|
|
15947
|
+
let total = 0;
|
|
15948
|
+
for (const r of raw) {
|
|
15949
|
+
const p = typeof r === "string" ? { text: r, at: now } : r;
|
|
15950
|
+
if (!p || typeof p.text !== "string" || typeof p.at !== "number" || !Number.isFinite(p.at)) continue;
|
|
15951
|
+
if (!isLongPaste(p.text) || !live(p, now) || out.some((o) => o.text === p.text)) continue;
|
|
15952
|
+
if (out.length >= PASTES_MAX || (total += p.text.length) > PASTES_MAX_CHARS) break;
|
|
15953
|
+
out.push({ text: p.text, at: Math.min(p.at, now) });
|
|
15954
|
+
}
|
|
15955
|
+
return out;
|
|
15956
|
+
}
|
|
15957
|
+
function pastedIn(list, text, listed = []) {
|
|
15958
|
+
if (!list.length || !isLongPaste(text)) return listed;
|
|
15959
|
+
const known = list.filter((b) => !listed.includes(b)).sort((a, b) => b.length - a.length);
|
|
15960
|
+
return splitPasted(text, listed, known).pastes;
|
|
15961
|
+
}
|
|
15962
|
+
function withPasted(items, list) {
|
|
15963
|
+
if (!list.length) return items;
|
|
15964
|
+
return items.map((i) => {
|
|
15965
|
+
if (i.role !== "user") return i;
|
|
15966
|
+
const pasted = pastedIn(list, i.text, i.pasted);
|
|
15967
|
+
return pasted.length && pasted !== i.pasted ? { ...i, pasted } : i;
|
|
15968
|
+
});
|
|
15969
|
+
}
|
|
15970
|
+
function withQueuedPasted(q, list) {
|
|
15971
|
+
const pasted = pastedIn(list, q.text);
|
|
15972
|
+
return pasted.length ? { ...q, pasted } : q;
|
|
15973
|
+
}
|
|
15974
|
+
const PASTES_FILE = path$1.join(DATA_DIR, "pastes.json");
|
|
15975
|
+
let kept = null;
|
|
15976
|
+
let saved = null;
|
|
15977
|
+
let texts = null;
|
|
15978
|
+
function pastes() {
|
|
15979
|
+
if (kept) return kept;
|
|
15980
|
+
kept = [];
|
|
15981
|
+
saved = kept;
|
|
15982
|
+
try {
|
|
15983
|
+
const raw = fs.readFileSync(PASTES_FILE, "utf8");
|
|
15984
|
+
kept = loadPastes(JSON.parse(raw), Date.now());
|
|
15985
|
+
saved = JSON.stringify(kept) + "\n" === raw ? kept : null;
|
|
15986
|
+
} catch {
|
|
15987
|
+
}
|
|
15988
|
+
return kept;
|
|
15989
|
+
}
|
|
15990
|
+
function keptTexts() {
|
|
15991
|
+
const list = pastes();
|
|
15992
|
+
if ((texts == null ? void 0 : texts.of) !== list) texts = { of: list, list: list.map((p) => p.text) };
|
|
15993
|
+
return texts.list;
|
|
15994
|
+
}
|
|
15995
|
+
let writing = Promise.resolve();
|
|
15996
|
+
function save() {
|
|
15997
|
+
writing = writing.then(async () => {
|
|
15998
|
+
const list = kept;
|
|
15999
|
+
if (!list || list === saved) return;
|
|
16000
|
+
const temp = `${PASTES_FILE}.${randomUUID$1()}.tmp`;
|
|
16001
|
+
try {
|
|
16002
|
+
await fs$1.mkdir(DATA_DIR, { recursive: true });
|
|
16003
|
+
await fs$1.writeFile(temp, JSON.stringify(list) + "\n", { mode: 384 });
|
|
16004
|
+
await fs$1.rename(temp, PASTES_FILE);
|
|
16005
|
+
saved = list;
|
|
16006
|
+
} catch {
|
|
16007
|
+
await fs$1.rm(temp, { force: true }).catch(() => {
|
|
16008
|
+
});
|
|
16009
|
+
}
|
|
16010
|
+
});
|
|
16011
|
+
}
|
|
16012
|
+
function keepPastes(text, ranges) {
|
|
16013
|
+
const next = addPastes(pastes(), pastesAt(text, ranges), Date.now());
|
|
16014
|
+
if (next === kept) return;
|
|
16015
|
+
kept = next;
|
|
16016
|
+
save();
|
|
16017
|
+
}
|
|
16018
|
+
function purgePastes(now = Date.now()) {
|
|
16019
|
+
const list = pastes();
|
|
16020
|
+
if (list.some((p) => !live(p, now))) kept = list.filter((p) => live(p, now));
|
|
16021
|
+
if (kept !== saved) save();
|
|
16022
|
+
}
|
|
16023
|
+
const chatPasted = (items) => withPasted(items, keptTexts());
|
|
16024
|
+
const queuedPasted = (q) => withQueuedPasted(q, keptTexts());
|
|
16025
|
+
|
|
15763
16026
|
const DEFAULTS = { freeWaitMs: 2e3, quietMs: 400, showWaitMs: 4e3, submitWaitMs: 4e3, pollMs: 50 };
|
|
15764
16027
|
const RECORD_MS = 600;
|
|
15765
16028
|
const MAX_ATTEMPTS = 3;
|
|
@@ -16171,7 +16434,7 @@ async function createAgent(body) {
|
|
|
16171
16434
|
created = await hx("worktree.create", params, 3e4);
|
|
16172
16435
|
} catch (e) {
|
|
16173
16436
|
if (e.code !== "not_git_worktree") throw e;
|
|
16174
|
-
await sleep(1e3);
|
|
16437
|
+
await sleep$1(1e3);
|
|
16175
16438
|
created = await hx("worktree.create", params, 3e4);
|
|
16176
16439
|
}
|
|
16177
16440
|
} else if (!target) {
|
|
@@ -16196,7 +16459,7 @@ async function createAgent(body) {
|
|
|
16196
16459
|
const pathSetup = 'export PATH="$HOME/.local/bin:$HOME/.kimi-code/bin:$HOME/.bun/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"';
|
|
16197
16460
|
await herdr("pane.send_input", { pane_id: paneId, text: pathSetup });
|
|
16198
16461
|
await herdr("pane.send_input", { pane_id: paneId, keys: ["enter"] });
|
|
16199
|
-
await sleep(400);
|
|
16462
|
+
await sleep$1(400);
|
|
16200
16463
|
let lastErr = null;
|
|
16201
16464
|
for (let i = 0; i < 16; i++) {
|
|
16202
16465
|
try {
|
|
@@ -16211,7 +16474,7 @@ async function createAgent(body) {
|
|
|
16211
16474
|
break;
|
|
16212
16475
|
}
|
|
16213
16476
|
if (lastErr.code === "timeout" || lastErr.code === "unreachable" || /name/i.test(lastErr.code)) break;
|
|
16214
|
-
await sleep(500);
|
|
16477
|
+
await sleep$1(500);
|
|
16215
16478
|
}
|
|
16216
16479
|
}
|
|
16217
16480
|
if (lastErr) {
|
|
@@ -16297,7 +16560,7 @@ async function closePanel(paneId) {
|
|
|
16297
16560
|
if (!panelOpen(r.read && r.read.text, p.agent)) return sent;
|
|
16298
16561
|
await herdr("pane.send_input", { pane_id: paneId, keys: ["esc"] });
|
|
16299
16562
|
sent = true;
|
|
16300
|
-
await sleep(400);
|
|
16563
|
+
await sleep$1(400);
|
|
16301
16564
|
}
|
|
16302
16565
|
return sent;
|
|
16303
16566
|
}
|
|
@@ -16336,10 +16599,184 @@ async function readUpload(name) {
|
|
|
16336
16599
|
return { type: ext === "jpg" ? "image/jpeg" : `image/${ext}`, body: await fsp$2.readFile(path$1.join(UPLOAD_DIR, name)) };
|
|
16337
16600
|
}
|
|
16338
16601
|
|
|
16602
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
16603
|
+
function createPaneSizes(deps) {
|
|
16604
|
+
const entries = /* @__PURE__ */ new Map();
|
|
16605
|
+
const log = deps.log || (() => {
|
|
16606
|
+
});
|
|
16607
|
+
function save() {
|
|
16608
|
+
if (!deps.save) return;
|
|
16609
|
+
const out = {};
|
|
16610
|
+
for (const [pane, e] of entries) if (e.original) out[pane] = e.original;
|
|
16611
|
+
deps.save(out);
|
|
16612
|
+
}
|
|
16613
|
+
function drop(pane, e) {
|
|
16614
|
+
if (entries.get(pane) !== e) return;
|
|
16615
|
+
entries.delete(pane);
|
|
16616
|
+
save();
|
|
16617
|
+
}
|
|
16618
|
+
function keepReason(e, now) {
|
|
16619
|
+
const o = e.original;
|
|
16620
|
+
if (!now) return "pane gone";
|
|
16621
|
+
if (o.terminal && now.terminal && o.terminal !== now.terminal) return "another terminal";
|
|
16622
|
+
if (now.rows === o.rows && now.cols === o.cols) return "already at its size";
|
|
16623
|
+
const set = e.set;
|
|
16624
|
+
if (set && (now.rows !== set.rows || now.exact && now.cols !== set.cols)) return `resized by another client to ${now.cols}x${now.rows}`;
|
|
16625
|
+
return null;
|
|
16626
|
+
}
|
|
16627
|
+
async function restore(pane, e) {
|
|
16628
|
+
var _a;
|
|
16629
|
+
const o = e.original;
|
|
16630
|
+
const why = keepReason(e, await deps.read(pane).catch(() => null));
|
|
16631
|
+
if (why) return log(`term ${pane} size kept (${why})`);
|
|
16632
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
16633
|
+
if (attempt) await sleep((_a = deps.retryMs) != null ? _a : 1500);
|
|
16634
|
+
if (e.clients > 0) return;
|
|
16635
|
+
if (await deps.resize(pane, o.cols, o.rows).catch(() => false)) return;
|
|
16636
|
+
}
|
|
16637
|
+
log(`term ${pane} could not get its ${o.cols}x${o.rows} back`);
|
|
16638
|
+
}
|
|
16639
|
+
function schedule(pane, e, ms) {
|
|
16640
|
+
clearTimeout(e.timer);
|
|
16641
|
+
e.timer = setTimeout(() => {
|
|
16642
|
+
e.timer = void 0;
|
|
16643
|
+
e.restoring = restore(pane, e).catch(() => {
|
|
16644
|
+
}).finally(() => {
|
|
16645
|
+
e.restoring = null;
|
|
16646
|
+
if (e.clients === 0 && !e.timer) drop(pane, e);
|
|
16647
|
+
});
|
|
16648
|
+
}, ms);
|
|
16649
|
+
}
|
|
16650
|
+
async function hold(pane) {
|
|
16651
|
+
let e = entries.get(pane);
|
|
16652
|
+
if (e) {
|
|
16653
|
+
clearTimeout(e.timer);
|
|
16654
|
+
e.timer = void 0;
|
|
16655
|
+
e.clients++;
|
|
16656
|
+
} else {
|
|
16657
|
+
const fresh = { original: null, ready: Promise.resolve(), clients: 1, set: null, restoring: null };
|
|
16658
|
+
fresh.ready = deps.read(pane).catch(() => null).then((size) => {
|
|
16659
|
+
fresh.original = size;
|
|
16660
|
+
if (size) save();
|
|
16661
|
+
});
|
|
16662
|
+
entries.set(pane, fresh);
|
|
16663
|
+
e = fresh;
|
|
16664
|
+
}
|
|
16665
|
+
const entry = e;
|
|
16666
|
+
await entry.ready;
|
|
16667
|
+
if (entry.restoring) await entry.restoring;
|
|
16668
|
+
let released = false;
|
|
16669
|
+
return {
|
|
16670
|
+
resized(cols, rows) {
|
|
16671
|
+
if (!released) entry.set = { cols, rows };
|
|
16672
|
+
},
|
|
16673
|
+
release(clean) {
|
|
16674
|
+
if (released) return;
|
|
16675
|
+
released = true;
|
|
16676
|
+
if (--entry.clients > 0) return;
|
|
16677
|
+
if (!entry.original) return drop(pane, entry);
|
|
16678
|
+
schedule(pane, entry, clean ? deps.closeMs : deps.graceMs);
|
|
16679
|
+
}
|
|
16680
|
+
};
|
|
16681
|
+
}
|
|
16682
|
+
function adopt(sizes) {
|
|
16683
|
+
for (const [pane, size] of Object.entries(sizes)) {
|
|
16684
|
+
if (entries.has(pane)) continue;
|
|
16685
|
+
const e = { original: size, ready: Promise.resolve(), clients: 0, set: null, restoring: null };
|
|
16686
|
+
entries.set(pane, e);
|
|
16687
|
+
schedule(pane, e, deps.graceMs);
|
|
16688
|
+
}
|
|
16689
|
+
}
|
|
16690
|
+
function stop() {
|
|
16691
|
+
for (const e of entries.values()) clearTimeout(e.timer);
|
|
16692
|
+
}
|
|
16693
|
+
return { hold, adopt, stop, held: () => [...entries.keys()] };
|
|
16694
|
+
}
|
|
16695
|
+
function parseSttySize(out) {
|
|
16696
|
+
const m = /^\s*(\d{1,4})\s+(\d{1,4})\s*$/.exec(out);
|
|
16697
|
+
if (!m) return null;
|
|
16698
|
+
const rows = Number(m[1]);
|
|
16699
|
+
const cols = Number(m[2]);
|
|
16700
|
+
return rows > 0 && cols > 0 ? { cols, rows } : null;
|
|
16701
|
+
}
|
|
16702
|
+
function cleanSavedSizes(raw, isPane) {
|
|
16703
|
+
const out = {};
|
|
16704
|
+
if (!raw || typeof raw !== "object") return out;
|
|
16705
|
+
for (const [pane, v] of Object.entries(raw)) {
|
|
16706
|
+
const s = v;
|
|
16707
|
+
if (!isPane(pane) || !s || !Number.isInteger(s.cols) || !Number.isInteger(s.rows)) continue;
|
|
16708
|
+
if (s.cols < 10 || s.cols > 400 || s.rows < 5 || s.rows > 200) continue;
|
|
16709
|
+
out[pane] = { cols: s.cols, rows: s.rows, exact: s.exact === true, ...typeof s.terminal === "string" ? { terminal: s.terminal } : {} };
|
|
16710
|
+
}
|
|
16711
|
+
return out;
|
|
16712
|
+
}
|
|
16713
|
+
|
|
16339
16714
|
const clampInt = (v, lo, hi, dflt) => {
|
|
16340
16715
|
const n = parseInt(String(v), 10);
|
|
16341
16716
|
return Number.isFinite(n) ? Math.min(hi, Math.max(lo, n)) : dflt;
|
|
16342
16717
|
};
|
|
16718
|
+
const TTY_SIZE_SCRIPT = 't=$(ps -o tty= -p "$1" 2>/dev/null | tr -d " "); [ -n "$t" ] && [ -c "/dev/$t" ] && stty size < "/dev/$t"';
|
|
16719
|
+
function ttySize(pane, pid) {
|
|
16720
|
+
const machine = machineOfPane(pane);
|
|
16721
|
+
if (!machine) return Promise.resolve("");
|
|
16722
|
+
if (machine.exec) return machine.exec(TTY_SIZE_SCRIPT, [String(pid)], { timeoutMs: 4e3 }).then((r) => r.code === 0 ? r.stdout.toString("utf8") : "", () => "");
|
|
16723
|
+
if (IN_DOCKER) return Promise.resolve("");
|
|
16724
|
+
return new Promise((resolve) => execFile("sh", ["-c", TTY_SIZE_SCRIPT, "sh", String(pid)], { timeout: 2e3 }, (err, stdout) => resolve(err ? "" : String(stdout))));
|
|
16725
|
+
}
|
|
16726
|
+
async function readPaneSize(pane) {
|
|
16727
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
16728
|
+
const p = (_a = await herdr("pane.get", { pane_id: pane }, 3e3).catch(() => null)) == null ? void 0 : _a.pane;
|
|
16729
|
+
if (!p) return null;
|
|
16730
|
+
const terminal = typeof p.terminal_id === "string" ? { terminal: p.terminal_id } : {};
|
|
16731
|
+
const rows = Number((_b = p.scroll) == null ? void 0 : _b.viewport_rows) || 0;
|
|
16732
|
+
const pid = Number((_d = (_c = await herdr("pane.process_info", { pane_id: pane }, 3e3).catch(() => null)) == null ? void 0 : _c.process_info) == null ? void 0 : _d.shell_pid);
|
|
16733
|
+
if (Number.isInteger(pid) && pid > 1) {
|
|
16734
|
+
const tty = parseSttySize(await ttySize(pane, pid));
|
|
16735
|
+
if (tty && (!rows || tty.rows === rows)) return { ...tty, exact: true, ...terminal };
|
|
16736
|
+
}
|
|
16737
|
+
const local = splitId(pane).local;
|
|
16738
|
+
const rect = (_h = (_g = (_f = (_e = await herdr("pane.layout", { pane_id: pane }, 3e3).catch(() => null)) == null ? void 0 : _e.layout) == null ? void 0 : _f.panes) == null ? void 0 : _g.find((x) => x.pane_id === local)) == null ? void 0 : _h.rect;
|
|
16739
|
+
const cols = Number(rect == null ? void 0 : rect.width) || 0;
|
|
16740
|
+
if (!rows || !cols) return null;
|
|
16741
|
+
return { cols, rows, exact: false, ...terminal };
|
|
16742
|
+
}
|
|
16743
|
+
const sizesFile = () => path$1.join(DATA_DIR, "term-sizes.json");
|
|
16744
|
+
const delay = (name, dflt) => {
|
|
16745
|
+
const n = Number(process.env[name]);
|
|
16746
|
+
return Number.isFinite(n) && n >= 0 ? n : dflt;
|
|
16747
|
+
};
|
|
16748
|
+
let sizes = null;
|
|
16749
|
+
const paneSizes = () => sizes || (sizes = createPaneSizes({
|
|
16750
|
+
read: readPaneSize,
|
|
16751
|
+
resize: (pane, cols, rows) => resizePane(pane, cols, rows),
|
|
16752
|
+
// Longer than the 1.5 s left to a closed control session to go away.
|
|
16753
|
+
closeMs: delay("WHERDR_TERM_CLOSE_MS", 2e3),
|
|
16754
|
+
graceMs: delay("WHERDR_TERM_GRACE_MS", 3e4),
|
|
16755
|
+
save(held) {
|
|
16756
|
+
const file = sizesFile();
|
|
16757
|
+
try {
|
|
16758
|
+
if (!Object.keys(held).length) return fs.rmSync(file, { force: true });
|
|
16759
|
+
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
16760
|
+
fs.writeFileSync(`${file}.tmp`, JSON.stringify(held) + "\n", { mode: 384 });
|
|
16761
|
+
fs.renameSync(`${file}.tmp`, file);
|
|
16762
|
+
} catch (e) {
|
|
16763
|
+
log(`term sizes: ${e.message}`);
|
|
16764
|
+
}
|
|
16765
|
+
},
|
|
16766
|
+
log
|
|
16767
|
+
}));
|
|
16768
|
+
function startPaneSizes() {
|
|
16769
|
+
let saved = null;
|
|
16770
|
+
try {
|
|
16771
|
+
saved = JSON.parse(fs.readFileSync(sizesFile(), "utf8"));
|
|
16772
|
+
} catch {
|
|
16773
|
+
return;
|
|
16774
|
+
}
|
|
16775
|
+
paneSizes().adopt(cleanSavedSizes(saved, (id) => PANE_RE.test(id)));
|
|
16776
|
+
}
|
|
16777
|
+
function stopPaneSizes() {
|
|
16778
|
+
sizes == null ? void 0 : sizes.stop();
|
|
16779
|
+
}
|
|
16343
16780
|
function openTerm(ws, url) {
|
|
16344
16781
|
const pane = url.searchParams.get("pane") || "";
|
|
16345
16782
|
if (!PANE_RE.test(pane)) {
|
|
@@ -16356,30 +16793,53 @@ function openTerm(ws, url) {
|
|
|
16356
16793
|
const rows = clampInt(url.searchParams.get("rows"), 5, 200, 24);
|
|
16357
16794
|
const args = ["terminal", "session", "control", splitId(pane).local, "--cols", String(cols), "--rows", String(rows)];
|
|
16358
16795
|
if (url.searchParams.get("takeover") === "1") args.push("--takeover");
|
|
16359
|
-
const child = machine.spawnHerdr(args);
|
|
16360
16796
|
const sess = { pane, visible: true };
|
|
16361
16797
|
termSessions.add(sess);
|
|
16362
|
-
|
|
16363
|
-
|
|
16364
|
-
|
|
16365
|
-
}
|
|
16366
|
-
|
|
16367
|
-
|
|
16368
|
-
if (ws.isOpen()) ws.send(JSON.stringify({ type: "herdr.stderr", message: line }));
|
|
16369
|
-
});
|
|
16370
|
-
child.on("error", (e) => {
|
|
16371
|
-
if (ws.isOpen()) ws.send(JSON.stringify({ type: "terminal.closed", reason: `herdr: ${e.message}` }));
|
|
16372
|
-
ws.close(4500, "herdr error");
|
|
16373
|
-
});
|
|
16374
|
-
child.on("exit", (code) => {
|
|
16375
|
-
termSessions.delete(sess);
|
|
16376
|
-
if (ws.isOpen()) ws.close(4e3, `herdr exit ${code}`);
|
|
16377
|
-
});
|
|
16378
|
-
child.stdin.on("error", () => {
|
|
16379
|
-
});
|
|
16798
|
+
let child = null;
|
|
16799
|
+
let hold = null;
|
|
16800
|
+
let closed = false;
|
|
16801
|
+
let size = { cols, rows };
|
|
16802
|
+
let attached = false;
|
|
16803
|
+
const early = [];
|
|
16380
16804
|
const toChild = (obj) => {
|
|
16381
|
-
if (child
|
|
16805
|
+
if (!child) {
|
|
16806
|
+
if (early.length < 64) early.push(obj);
|
|
16807
|
+
} else if (child.stdin.writable) {
|
|
16808
|
+
child.stdin.write(JSON.stringify(obj) + "\n");
|
|
16809
|
+
}
|
|
16382
16810
|
};
|
|
16811
|
+
function start() {
|
|
16812
|
+
const c = machine.spawnHerdr(args);
|
|
16813
|
+
child = c;
|
|
16814
|
+
log(`term ${pane} opened ${cols}x${rows}${args.includes("--takeover") ? " (takeover)" : ""}${machine.local ? "" : ` on ${machine.label}`}`);
|
|
16815
|
+
readline.createInterface({ input: c.stdout }).on("line", (line) => {
|
|
16816
|
+
if (!attached && line.includes('"terminal.frame"')) {
|
|
16817
|
+
attached = true;
|
|
16818
|
+
hold == null ? void 0 : hold.resized(size.cols, size.rows);
|
|
16819
|
+
}
|
|
16820
|
+
if (ws.isOpen()) ws.send(line);
|
|
16821
|
+
});
|
|
16822
|
+
readline.createInterface({ input: c.stderr }).on("line", (line) => {
|
|
16823
|
+
log(`term ${pane} stderr: ${line}`);
|
|
16824
|
+
if (ws.isOpen()) ws.send(JSON.stringify({ type: "herdr.stderr", message: line }));
|
|
16825
|
+
});
|
|
16826
|
+
c.on("error", (e) => {
|
|
16827
|
+
if (ws.isOpen()) ws.send(JSON.stringify({ type: "terminal.closed", reason: `herdr: ${e.message}` }));
|
|
16828
|
+
ws.close(4500, "herdr error");
|
|
16829
|
+
});
|
|
16830
|
+
c.on("exit", (code) => {
|
|
16831
|
+
termSessions.delete(sess);
|
|
16832
|
+
if (ws.isOpen()) ws.close(4e3, `herdr exit ${code}`);
|
|
16833
|
+
});
|
|
16834
|
+
c.stdin.on("error", () => {
|
|
16835
|
+
});
|
|
16836
|
+
for (const m of early.splice(0)) toChild(m);
|
|
16837
|
+
}
|
|
16838
|
+
paneSizes().hold(pane).then((h) => {
|
|
16839
|
+
if (closed) return h.release(true);
|
|
16840
|
+
hold = h;
|
|
16841
|
+
start();
|
|
16842
|
+
});
|
|
16383
16843
|
return {
|
|
16384
16844
|
onMessage(raw) {
|
|
16385
16845
|
let m;
|
|
@@ -16393,9 +16853,12 @@ function openTerm(ws, url) {
|
|
|
16393
16853
|
if (typeof m.text === "string" && m.text.length <= 65536) toChild({ type: "terminal.input", text: m.text });
|
|
16394
16854
|
else if (typeof m.bytes === "string" && m.bytes.length <= 9e4) toChild({ type: "terminal.input", bytes: m.bytes });
|
|
16395
16855
|
break;
|
|
16396
|
-
case "terminal.resize":
|
|
16397
|
-
|
|
16856
|
+
case "terminal.resize": {
|
|
16857
|
+
size = { cols: clampInt(m.cols, 10, 400, cols), rows: clampInt(m.rows, 5, 200, rows) };
|
|
16858
|
+
if (attached) hold == null ? void 0 : hold.resized(size.cols, size.rows);
|
|
16859
|
+
toChild({ type: "terminal.resize", ...size });
|
|
16398
16860
|
break;
|
|
16861
|
+
}
|
|
16399
16862
|
case "terminal.scroll":
|
|
16400
16863
|
if (m.direction === "up" || m.direction === "down") {
|
|
16401
16864
|
toChild({ type: "terminal.scroll", direction: m.direction, lines: clampInt(m.lines, 1, 500, 3) });
|
|
@@ -16419,13 +16882,18 @@ function openTerm(ws, url) {
|
|
|
16419
16882
|
break;
|
|
16420
16883
|
}
|
|
16421
16884
|
},
|
|
16422
|
-
onClose() {
|
|
16885
|
+
onClose(clean = false) {
|
|
16886
|
+
closed = true;
|
|
16423
16887
|
termSessions.delete(sess);
|
|
16424
|
-
|
|
16425
|
-
|
|
16426
|
-
|
|
16427
|
-
|
|
16428
|
-
|
|
16888
|
+
const c = child;
|
|
16889
|
+
if (c) {
|
|
16890
|
+
toChild({ type: "terminal.release" });
|
|
16891
|
+
c.stdin.end();
|
|
16892
|
+
setTimeout(() => {
|
|
16893
|
+
if (c.exitCode === null) c.kill("SIGTERM");
|
|
16894
|
+
}, 1500);
|
|
16895
|
+
}
|
|
16896
|
+
hold == null ? void 0 : hold.release(clean && sess.visible);
|
|
16429
16897
|
}
|
|
16430
16898
|
};
|
|
16431
16899
|
}
|
|
@@ -16560,7 +17028,8 @@ const listKey = (paneId, agent) => {
|
|
|
16560
17028
|
var _a;
|
|
16561
17029
|
return `${((_a = machineOfPane(paneId)) == null ? void 0 : _a.key) || ""}|${agent}`;
|
|
16562
17030
|
};
|
|
16563
|
-
const busy = /* @__PURE__ */ new Set();
|
|
17031
|
+
const busy$1 = /* @__PURE__ */ new Set();
|
|
17032
|
+
const modelControlBusy = (paneId) => busy$1.has(paneId);
|
|
16564
17033
|
async function withPane(paneId, fn) {
|
|
16565
17034
|
const p = findPane(paneId);
|
|
16566
17035
|
if (!p) throw new HerdrError("bad_pane", "Pane not found");
|
|
@@ -16568,12 +17037,12 @@ async function withPane(paneId, fn) {
|
|
|
16568
17037
|
if (!READY.has(p.status || "")) {
|
|
16569
17038
|
throw new HerdrError("busy", p.status === "blocked" ? "The agent is waiting for an answer \u2014 reply first." : "The agent is working \u2014 change the model once it\u2019s done.");
|
|
16570
17039
|
}
|
|
16571
|
-
if (busy.has(paneId)) throw new HerdrError("busy", "Model change already in progress");
|
|
16572
|
-
busy.add(paneId);
|
|
17040
|
+
if (busy$1.has(paneId)) throw new HerdrError("busy", "Model change already in progress");
|
|
17041
|
+
busy$1.add(paneId);
|
|
16573
17042
|
try {
|
|
16574
17043
|
return await fn(p);
|
|
16575
17044
|
} finally {
|
|
16576
|
-
busy.delete(paneId);
|
|
17045
|
+
busy$1.delete(paneId);
|
|
16577
17046
|
}
|
|
16578
17047
|
}
|
|
16579
17048
|
const screen = async (paneId) => {
|
|
@@ -16586,14 +17055,14 @@ async function waitMenu(paneId, ok, timeoutMs = 6e3) {
|
|
|
16586
17055
|
const m = parseModelMenu(await screen(paneId));
|
|
16587
17056
|
if (m && ok(m)) return m;
|
|
16588
17057
|
if (Date.now() > until) return null;
|
|
16589
|
-
await sleep(250);
|
|
17058
|
+
await sleep$1(250);
|
|
16590
17059
|
}
|
|
16591
17060
|
}
|
|
16592
17061
|
async function closeMenu(paneId) {
|
|
16593
17062
|
for (let i = 0; i < 3; i++) {
|
|
16594
17063
|
if (!parseModelMenu(await screen(paneId))) return;
|
|
16595
17064
|
await herdr("pane.send_input", { pane_id: paneId, keys: ["esc"] });
|
|
16596
|
-
await sleep(400);
|
|
17065
|
+
await sleep$1(400);
|
|
16597
17066
|
}
|
|
16598
17067
|
}
|
|
16599
17068
|
async function openMenu(p) {
|
|
@@ -16621,7 +17090,7 @@ async function readAllOptions(paneId, first) {
|
|
|
16621
17090
|
for (let i = 0; i < 8 && m.scrollDown; i++) {
|
|
16622
17091
|
const last = m.options[m.options.length - 1].n;
|
|
16623
17092
|
await herdr("pane.send_input", { pane_id: paneId, keys: moveKeys(m.cursor, last + 1) });
|
|
16624
|
-
await sleep(350);
|
|
17093
|
+
await sleep$1(350);
|
|
16625
17094
|
const next = await waitMenu(paneId, (x) => x.kind === "model", 2e3);
|
|
16626
17095
|
if (!next || next.cursor <= last) break;
|
|
16627
17096
|
const before = all.size;
|
|
@@ -16703,7 +17172,7 @@ async function setModel(paneId, wanted) {
|
|
|
16703
17172
|
const confirm = p.agent === "claude" ? switchConfirmKeys(text, confirmAs) : null;
|
|
16704
17173
|
if (confirm) await herdr("pane.send_input", { pane_id: p.id, keys: confirm });
|
|
16705
17174
|
clear = confirm || parseModelMenu(text) ? 0 : clear + 1;
|
|
16706
|
-
await sleep(confirm ? 400 : 250);
|
|
17175
|
+
await sleep$1(confirm ? 400 : 250);
|
|
16707
17176
|
}
|
|
16708
17177
|
if (clear < 2) throw new HerdrError("stale", "The /model menu changed \u2014 try again.");
|
|
16709
17178
|
const info = { id: null, label: confirmAs, effort, at: new Date(started).toISOString() };
|
|
@@ -16726,14 +17195,14 @@ async function waitOmpSelector(paneId, ok, timeoutMs = 6e3) {
|
|
|
16726
17195
|
const s = await ompSelector(paneId).catch(() => null);
|
|
16727
17196
|
if (s && ok(s)) return s;
|
|
16728
17197
|
if (Date.now() > until) return null;
|
|
16729
|
-
await sleep(250);
|
|
17198
|
+
await sleep$1(250);
|
|
16730
17199
|
}
|
|
16731
17200
|
}
|
|
16732
17201
|
async function closeOmpSelector(paneId) {
|
|
16733
17202
|
for (let i = 0; i < 3; i++) {
|
|
16734
17203
|
if (!await ompSelector(paneId).catch(() => null)) return;
|
|
16735
17204
|
await herdr("pane.send_input", { pane_id: paneId, keys: ["esc"] });
|
|
16736
|
-
await sleep(400);
|
|
17205
|
+
await sleep$1(400);
|
|
16737
17206
|
}
|
|
16738
17207
|
}
|
|
16739
17208
|
const OMP_SELECTOR_COLS = 160;
|
|
@@ -16773,7 +17242,7 @@ async function waitOmpModel(p, id, since) {
|
|
|
16773
17242
|
const info = await transcripts.model(p).catch(() => null);
|
|
16774
17243
|
if (info && info.id === id && info.at && Date.parse(info.at) >= since - 1500) return true;
|
|
16775
17244
|
if (Date.now() > until) return false;
|
|
16776
|
-
await sleep(300);
|
|
17245
|
+
await sleep$1(300);
|
|
16777
17246
|
}
|
|
16778
17247
|
}
|
|
16779
17248
|
async function ompSetModel(paneId, wanted) {
|
|
@@ -16797,7 +17266,7 @@ async function ompSetModel(paneId, wanted) {
|
|
|
16797
17266
|
}
|
|
16798
17267
|
for (const ch of typed) {
|
|
16799
17268
|
await herdr("pane.send_input", { pane_id: p.id, keys: [ch === " " ? "space" : ch] });
|
|
16800
|
-
await sleep(80);
|
|
17269
|
+
await sleep$1(80);
|
|
16801
17270
|
}
|
|
16802
17271
|
s = await waitOmpSelector(p.id, (x) => x.search.length > 0, 3e3) || s;
|
|
16803
17272
|
at = rank(s);
|
|
@@ -16808,7 +17277,7 @@ async function ompSetModel(paneId, wanted) {
|
|
|
16808
17277
|
const delta = at - s.cursor;
|
|
16809
17278
|
if (delta === 0) break;
|
|
16810
17279
|
await herdr("pane.send_input", { pane_id: p.id, keys: [delta > 0 ? "down" : "up"] });
|
|
16811
|
-
await sleep(120);
|
|
17280
|
+
await sleep$1(120);
|
|
16812
17281
|
if (n === 79) throw new HerdrError("stale", "The model selector did not follow \u2014 nothing was changed.");
|
|
16813
17282
|
}
|
|
16814
17283
|
s = (_b = (_a = await waitOmpSelector(p.id, (x) => x.cursor === at && x.options[at].label === id, 3e3)) != null ? _a : await ompSelector(p.id)) != null ? _b : s;
|
|
@@ -16867,7 +17336,7 @@ async function waitSlider(paneId, ok, timeoutMs) {
|
|
|
16867
17336
|
const s = parseClaudeEffortScreen(await screen(paneId).catch(() => ""));
|
|
16868
17337
|
if (s && ok(s)) return s;
|
|
16869
17338
|
if (Date.now() > until) return null;
|
|
16870
|
-
await sleep(250);
|
|
17339
|
+
await sleep$1(250);
|
|
16871
17340
|
}
|
|
16872
17341
|
}
|
|
16873
17342
|
async function sliderGone(paneId, timeoutMs = 3e3) {
|
|
@@ -16875,7 +17344,7 @@ async function sliderGone(paneId, timeoutMs = 3e3) {
|
|
|
16875
17344
|
let clear = 0;
|
|
16876
17345
|
while (clear < 2 && Date.now() < until) {
|
|
16877
17346
|
clear = parseClaudeEffortScreen(await screen(paneId).catch(() => "")) ? 0 : clear + 1;
|
|
16878
|
-
if (clear < 2) await sleep(250);
|
|
17347
|
+
if (clear < 2) await sleep$1(250);
|
|
16879
17348
|
}
|
|
16880
17349
|
return clear >= 2;
|
|
16881
17350
|
}
|
|
@@ -16884,7 +17353,7 @@ async function closeEffortSlider(paneId) {
|
|
|
16884
17353
|
if (!parseClaudeEffortScreen(await screen(paneId).catch(() => ""))) return;
|
|
16885
17354
|
await herdr("pane.send_input", { pane_id: paneId, keys: ["esc"] }).catch(() => {
|
|
16886
17355
|
});
|
|
16887
|
-
await sleep(400);
|
|
17356
|
+
await sleep$1(400);
|
|
16888
17357
|
}
|
|
16889
17358
|
}
|
|
16890
17359
|
async function openEffortSlider(p) {
|
|
@@ -16918,7 +17387,7 @@ async function waitOmpEffort(p, model, prev, shown, sinceAt, hasTranscript) {
|
|
|
16918
17387
|
if (now && prev === "auto" && shown !== "auto" && Date.now() - pressed > OMP_REDRAW_MS) return { level: now, at: sinceAt, shown: now };
|
|
16919
17388
|
}
|
|
16920
17389
|
if (Date.now() - pressed > 3e3) return null;
|
|
16921
|
-
await sleep(200);
|
|
17390
|
+
await sleep$1(200);
|
|
16922
17391
|
}
|
|
16923
17392
|
}
|
|
16924
17393
|
async function ompCycleTo(p, before, level) {
|
|
@@ -17031,7 +17500,7 @@ async function setEffort(paneId, level) {
|
|
|
17031
17500
|
let clear = 0;
|
|
17032
17501
|
while (clear < 2 && Date.now() < until) {
|
|
17033
17502
|
clear = parseModelMenu(await screen(p.id)) ? 0 : clear + 1;
|
|
17034
|
-
await sleep(250);
|
|
17503
|
+
await sleep$1(250);
|
|
17035
17504
|
}
|
|
17036
17505
|
if (clear < 2) throw new HerdrError("stale", "The /model menu changed \u2014 try again.");
|
|
17037
17506
|
} catch (e) {
|
|
@@ -17064,16 +17533,26 @@ function compareVersions(a, b) {
|
|
|
17064
17533
|
for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return x[i] - y[i];
|
|
17065
17534
|
return 0;
|
|
17066
17535
|
}
|
|
17536
|
+
const MODES = ["docker", "docker-build", "npm", "npm-global", "brew", "plugin"];
|
|
17067
17537
|
function installMode(env) {
|
|
17068
|
-
|
|
17538
|
+
var _a;
|
|
17539
|
+
return (_a = MODES.find((m) => m === env)) != null ? _a : "native";
|
|
17069
17540
|
}
|
|
17070
|
-
function updateCommand(mode) {
|
|
17541
|
+
function updateCommand(mode, repo) {
|
|
17071
17542
|
if (mode === "docker") return "docker compose pull && docker compose up -d";
|
|
17072
17543
|
if (mode === "docker-build") return "git pull && docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build";
|
|
17073
17544
|
if (mode === "npm") return "npx wherdr@latest";
|
|
17074
|
-
if (mode === "
|
|
17545
|
+
if (mode === "npm-global") return "npm install -g wherdr@latest && wherdr restart";
|
|
17546
|
+
if (mode === "brew") return "brew upgrade wherdr && brew services restart wherdr";
|
|
17547
|
+
if (mode === "plugin") return `herdr plugin install ${repo} --yes`;
|
|
17075
17548
|
return "git pull && npm ci && npm run build";
|
|
17076
17549
|
}
|
|
17550
|
+
function oneTapMode(mode) {
|
|
17551
|
+
return mode === "plugin" || mode === "npm-global" || mode === "brew";
|
|
17552
|
+
}
|
|
17553
|
+
function updateRunning(job, now) {
|
|
17554
|
+
return !!job && !["done", "rolled-back", "failed"].includes(job.state) && now - job.updatedAt < 10 * 6e4;
|
|
17555
|
+
}
|
|
17077
17556
|
|
|
17078
17557
|
const CODEX_RELEASE_NOTES = "https://github.com/openai/codex/releases/latest";
|
|
17079
17558
|
const CODEX_STANDALONE_COMMAND = `sh -c 'curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 sh'`;
|
|
@@ -17860,6 +18339,494 @@ async function installClaudeStatusline(m) {
|
|
|
17860
18339
|
return last(r.stdout.toString("utf8"));
|
|
17861
18340
|
}
|
|
17862
18341
|
|
|
18342
|
+
const CLAUDE = {
|
|
18343
|
+
keep: {
|
|
18344
|
+
"--model": 1,
|
|
18345
|
+
"--effort": 1,
|
|
18346
|
+
"--permission-mode": 1,
|
|
18347
|
+
"--fallback-model": 1,
|
|
18348
|
+
"--agent": 1,
|
|
18349
|
+
"--agents": 1,
|
|
18350
|
+
"--settings": 1,
|
|
18351
|
+
"--setting-sources": 1,
|
|
18352
|
+
"--system-prompt": 1,
|
|
18353
|
+
"--append-system-prompt": 1,
|
|
18354
|
+
"--autocompact": 1,
|
|
18355
|
+
"-n": 1,
|
|
18356
|
+
"--name": 1,
|
|
18357
|
+
"--debug-file": 1,
|
|
18358
|
+
"--add-dir": "*",
|
|
18359
|
+
"--mcp-config": "*",
|
|
18360
|
+
"--plugin-dir": "*",
|
|
18361
|
+
"--allowedTools": "*",
|
|
18362
|
+
"--allowed-tools": "*",
|
|
18363
|
+
"--disallowedTools": "*",
|
|
18364
|
+
"--disallowed-tools": "*",
|
|
18365
|
+
"--tools": "*",
|
|
18366
|
+
"--betas": "*",
|
|
18367
|
+
"--dangerously-skip-permissions": 0,
|
|
18368
|
+
"--allow-dangerously-skip-permissions": 0,
|
|
18369
|
+
"--verbose": 0,
|
|
18370
|
+
"--ide": 0,
|
|
18371
|
+
"--chrome": 0,
|
|
18372
|
+
"--no-chrome": 0,
|
|
18373
|
+
"--strict-mcp-config": 0,
|
|
18374
|
+
"--bare": 0,
|
|
18375
|
+
"--brief": 0,
|
|
18376
|
+
"--disable-slash-commands": 0,
|
|
18377
|
+
"--safe-mode": 0,
|
|
18378
|
+
"--restricted": 0
|
|
18379
|
+
},
|
|
18380
|
+
// Conversation choice, one-off launch or already done (worktree created).
|
|
18381
|
+
drop: {
|
|
18382
|
+
"-c": 0,
|
|
18383
|
+
"--continue": 0,
|
|
18384
|
+
"-r": "?",
|
|
18385
|
+
"--resume": "?",
|
|
18386
|
+
"--session-id": 1,
|
|
18387
|
+
"--fork-session": 0,
|
|
18388
|
+
"-w": "?",
|
|
18389
|
+
"--worktree": "?",
|
|
18390
|
+
"--tmux": 0,
|
|
18391
|
+
"--from-pr": "?",
|
|
18392
|
+
"--teleport": "?",
|
|
18393
|
+
"-p": 0,
|
|
18394
|
+
"--print": 0,
|
|
18395
|
+
"--bg": 0,
|
|
18396
|
+
"--background": 0,
|
|
18397
|
+
"--remote-control": "?",
|
|
18398
|
+
"--cloud": "?",
|
|
18399
|
+
"--file": "*",
|
|
18400
|
+
"-d": "?",
|
|
18401
|
+
"--debug": "?"
|
|
18402
|
+
}
|
|
18403
|
+
};
|
|
18404
|
+
const CODEX = {
|
|
18405
|
+
keep: {
|
|
18406
|
+
"-c": 1,
|
|
18407
|
+
"--config": 1,
|
|
18408
|
+
"--enable": 1,
|
|
18409
|
+
"--disable": 1,
|
|
18410
|
+
"-m": 1,
|
|
18411
|
+
"--model": 1,
|
|
18412
|
+
"--local-provider": 1,
|
|
18413
|
+
"-p": 1,
|
|
18414
|
+
"--profile": 1,
|
|
18415
|
+
"-s": 1,
|
|
18416
|
+
"--sandbox": 1,
|
|
18417
|
+
"-C": 1,
|
|
18418
|
+
"--cd": 1,
|
|
18419
|
+
"--add-dir": 1,
|
|
18420
|
+
"-a": 1,
|
|
18421
|
+
"--ask-for-approval": 1,
|
|
18422
|
+
"--remote": 1,
|
|
18423
|
+
"--remote-auth-token-env": 1,
|
|
18424
|
+
"--oss": 0,
|
|
18425
|
+
"--search": 0,
|
|
18426
|
+
"--no-alt-screen": 0,
|
|
18427
|
+
"--no-daemon": 0,
|
|
18428
|
+
"--full-auto": 0,
|
|
18429
|
+
"--dangerously-bypass-approvals-and-sandbox": 0,
|
|
18430
|
+
"--yolo": 0,
|
|
18431
|
+
"--approve-for-me": 0,
|
|
18432
|
+
"--strict-config": 0,
|
|
18433
|
+
"--dangerously-bypass-hook-trust": 0
|
|
18434
|
+
},
|
|
18435
|
+
drop: { "--last": 0, "--all": 0, "--include-non-interactive": 0, "-i": "*", "--image": "*", "--worktree": 0 }
|
|
18436
|
+
};
|
|
18437
|
+
const OMP = {
|
|
18438
|
+
keep: {
|
|
18439
|
+
"--model": 1,
|
|
18440
|
+
"--smol": 1,
|
|
18441
|
+
"--slow": 1,
|
|
18442
|
+
"--plan": 1,
|
|
18443
|
+
"--prewalk-into": 1,
|
|
18444
|
+
"--plan-yolo-into": 1,
|
|
18445
|
+
"--provider": 1,
|
|
18446
|
+
"--system-prompt": 1,
|
|
18447
|
+
"--system-prompt-template": 1,
|
|
18448
|
+
"--append-system-prompt": 1,
|
|
18449
|
+
"--profile": 1,
|
|
18450
|
+
"--cwd": 1,
|
|
18451
|
+
"--config": 1,
|
|
18452
|
+
"--add-dir": 1,
|
|
18453
|
+
"--session-dir": 1,
|
|
18454
|
+
"--models": 1,
|
|
18455
|
+
"--tools": 1,
|
|
18456
|
+
"--thinking": 1,
|
|
18457
|
+
"--service-tier": 1,
|
|
18458
|
+
"--hook": 1,
|
|
18459
|
+
"-e": 1,
|
|
18460
|
+
"--extension": 1,
|
|
18461
|
+
"--skills": 1,
|
|
18462
|
+
"--approval-mode": 1,
|
|
18463
|
+
"--plugin-dir": 1,
|
|
18464
|
+
"--allow-home": 0,
|
|
18465
|
+
"--prewalk": 0,
|
|
18466
|
+
"--no-prewalk": 0,
|
|
18467
|
+
"--no-tools": 0,
|
|
18468
|
+
"--no-lsp": 0,
|
|
18469
|
+
"--no-pty": 0,
|
|
18470
|
+
"--hide-thinking": 0,
|
|
18471
|
+
"--advisor": 0,
|
|
18472
|
+
"--external-thinking": 0,
|
|
18473
|
+
"--no-extensions": 0,
|
|
18474
|
+
"--no-skills": 0,
|
|
18475
|
+
"--no-rules": 0,
|
|
18476
|
+
"--no-title": 0,
|
|
18477
|
+
"--auto-approve": 0
|
|
18478
|
+
},
|
|
18479
|
+
// Conversation choice, one-off launch; the API key is never replayed
|
|
18480
|
+
// (it would show in the confirmation).
|
|
18481
|
+
drop: {
|
|
18482
|
+
"-c": 0,
|
|
18483
|
+
"--continue": 0,
|
|
18484
|
+
"-r": "?",
|
|
18485
|
+
"--resume": "?",
|
|
18486
|
+
"-p": 0,
|
|
18487
|
+
"--print": 0,
|
|
18488
|
+
"--plan-yolo": 0,
|
|
18489
|
+
"--from-claude": 0,
|
|
18490
|
+
"--from-codex": 0,
|
|
18491
|
+
"--no-session": 0,
|
|
18492
|
+
"--export": 1,
|
|
18493
|
+
"--mode": 1,
|
|
18494
|
+
"--max-time": 1,
|
|
18495
|
+
"--alias": 1,
|
|
18496
|
+
"--api-key": 1,
|
|
18497
|
+
"--no-ui": 0,
|
|
18498
|
+
"--print-thoughts": 0
|
|
18499
|
+
}
|
|
18500
|
+
};
|
|
18501
|
+
const SILENT = /* @__PURE__ */ new Set(["-c", "--continue", "-r", "--resume", "--session-id", "--fork-session", "--last", "--all"]);
|
|
18502
|
+
const SPECS = { claude: CLAUDE, codex: CODEX, omp: OMP };
|
|
18503
|
+
const RESTARTABLE = new Set(Object.keys(SPECS));
|
|
18504
|
+
const base$1 = (s) => s.replace(/^.*\//, "").replace(/\.(c?js|mjs)$/, "");
|
|
18505
|
+
function launchOptions(kind, argv) {
|
|
18506
|
+
const spec = SPECS[kind];
|
|
18507
|
+
const kept = [];
|
|
18508
|
+
const dropped = [];
|
|
18509
|
+
if (!spec) return { kept, dropped };
|
|
18510
|
+
let i = argv.findIndex((a) => base$1(a) === kind);
|
|
18511
|
+
i = i < 0 ? 1 : i + 1;
|
|
18512
|
+
for (; i < argv.length; i++) {
|
|
18513
|
+
const a = argv[i];
|
|
18514
|
+
if (a === "--") break;
|
|
18515
|
+
if (!a.startsWith("-") || a === "-") continue;
|
|
18516
|
+
const eq = a.indexOf("=");
|
|
18517
|
+
const flag = eq > 0 ? a.slice(0, eq) : a;
|
|
18518
|
+
const keep = flag in spec.keep;
|
|
18519
|
+
const arity = keep ? spec.keep[flag] : spec.drop[flag];
|
|
18520
|
+
const out = [a];
|
|
18521
|
+
if (keep) kept.push(out);
|
|
18522
|
+
else if (!SILENT.has(flag)) dropped.push(out);
|
|
18523
|
+
if (arity === void 0) {
|
|
18524
|
+
if (eq < 0 && argv[i + 1] !== void 0 && !argv[i + 1].startsWith("-")) out.push(argv[++i]);
|
|
18525
|
+
continue;
|
|
18526
|
+
}
|
|
18527
|
+
if (eq > 0 || arity === 0) continue;
|
|
18528
|
+
if (arity === 1) {
|
|
18529
|
+
if (argv[i + 1] !== void 0) out.push(argv[++i]);
|
|
18530
|
+
continue;
|
|
18531
|
+
}
|
|
18532
|
+
while (argv[i + 1] !== void 0 && !argv[i + 1].startsWith("-")) {
|
|
18533
|
+
out.push(argv[++i]);
|
|
18534
|
+
if (arity === "?") break;
|
|
18535
|
+
}
|
|
18536
|
+
}
|
|
18537
|
+
return { kept, dropped };
|
|
18538
|
+
}
|
|
18539
|
+
const EFFORTS = /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
18540
|
+
function planRestart(o) {
|
|
18541
|
+
const opts = o.argv ? launchOptions(o.kind, o.argv) : { kept: [], dropped: [] };
|
|
18542
|
+
const mode = o.session ? "resume" : o.hadSession ? "fresh" : "continue";
|
|
18543
|
+
let groups = opts.kept;
|
|
18544
|
+
const without = (...flags) => {
|
|
18545
|
+
groups = groups.filter((g) => !flags.includes(g[0].split("=")[0]));
|
|
18546
|
+
};
|
|
18547
|
+
if (o.kind === "omp" && mode === "resume") without("--model", "--thinking");
|
|
18548
|
+
if (o.kind === "claude") {
|
|
18549
|
+
const cur = o.current || {};
|
|
18550
|
+
if (mode === "resume") without("--model");
|
|
18551
|
+
const effort = String(cur.effort || "").toLowerCase();
|
|
18552
|
+
if (EFFORTS.has(effort)) {
|
|
18553
|
+
without("--effort");
|
|
18554
|
+
groups.push(["--effort", effort]);
|
|
18555
|
+
}
|
|
18556
|
+
if (cur.permissionMode) {
|
|
18557
|
+
without("--permission-mode");
|
|
18558
|
+
if (cur.permissionMode !== "default") groups.push(["--permission-mode", cur.permissionMode]);
|
|
18559
|
+
}
|
|
18560
|
+
}
|
|
18561
|
+
const kept = groups.flat();
|
|
18562
|
+
const dropped = opts.dropped.flat();
|
|
18563
|
+
let args;
|
|
18564
|
+
if (o.kind === "codex") {
|
|
18565
|
+
args = mode === "fresh" ? kept : ["resume", ...kept, mode === "resume" ? o.session : "--last"];
|
|
18566
|
+
} else {
|
|
18567
|
+
args = mode === "resume" ? [...kept, "--resume", o.session] : mode === "continue" ? [...kept, "--continue"] : kept;
|
|
18568
|
+
}
|
|
18569
|
+
return { args, mode, kept, dropped, unknownArgs: !o.argv };
|
|
18570
|
+
}
|
|
18571
|
+
const FOOTER_MODES = [
|
|
18572
|
+
[/\bplan mode on\b/i, "plan"],
|
|
18573
|
+
[/\baccept edits on\b/i, "acceptEdits"],
|
|
18574
|
+
[/\bauto mode on\b/i, "auto"],
|
|
18575
|
+
[/\bbypass permissions on\b/i, "bypassPermissions"],
|
|
18576
|
+
[/\bdon'?t ask (?:mode )?on\b/i, "dontAsk"]
|
|
18577
|
+
];
|
|
18578
|
+
function claudeFooterMode(text) {
|
|
18579
|
+
const lines = String(text || "").split("\n");
|
|
18580
|
+
let rule = -1;
|
|
18581
|
+
for (let i = lines.length - 1; i >= 0 && rule < 0; i--) if (/^\s*─{20,}\s*$/.test(lines[i])) rule = i;
|
|
18582
|
+
if (rule < 1 || !lines.slice(Math.max(0, rule - 6), rule).some((l) => /^\s*❯/.test(l))) return null;
|
|
18583
|
+
for (const l of lines.slice(rule + 1)) {
|
|
18584
|
+
for (const [re, mode] of FOOTER_MODES) if (re.test(l)) return mode;
|
|
18585
|
+
}
|
|
18586
|
+
return "default";
|
|
18587
|
+
}
|
|
18588
|
+
|
|
18589
|
+
function updateShellSignature(text) {
|
|
18590
|
+
const lines = text.replace(/\r/g, "").trimEnd().split("\n");
|
|
18591
|
+
const end = lines.findLastIndex((l) => /^\s*(?:🎉\s*)?Update ran successfully! Please restart Codex\.\s*$/.test(l));
|
|
18592
|
+
if (end < 0 || !lines.slice(Math.max(0, end - 4), end).some((l) => /^\s*Codex CLI \S+ installed successfully\.\s*$/.test(l))) return null;
|
|
18593
|
+
const tail = lines.slice(end + 1).filter((l) => l.trim());
|
|
18594
|
+
if (!tail.length || tail.length > 2) return null;
|
|
18595
|
+
const last = tail.at(-1);
|
|
18596
|
+
if (!/^\s*(?:[\w@./~:-]+(?: [\w@./~:-]+){0,2} )?[$%#❯]\s*$/.test(last)) return null;
|
|
18597
|
+
if (tail.length === 2 && (!/^\s*[\w./~:-]+ on [\w./:-]+\s*$/.test(tail[0]) || !/^\s*❯\s*$/.test(last))) return null;
|
|
18598
|
+
return tail.join("\n");
|
|
18599
|
+
}
|
|
18600
|
+
function updateChoices(launch) {
|
|
18601
|
+
return launch.conversation === "empty" && launch.argv && !launch.argv.some((a) => a === "resume" || a === "fork") ? ["restart"] : ["resume", "fresh"];
|
|
18602
|
+
}
|
|
18603
|
+
const UPDATE_LABELS = {
|
|
18604
|
+
restart: "Restart Codex",
|
|
18605
|
+
resume: "Resume conversation",
|
|
18606
|
+
fresh: "Start fresh"
|
|
18607
|
+
};
|
|
18608
|
+
function updatePrompt(launch) {
|
|
18609
|
+
return {
|
|
18610
|
+
kind: "self-update",
|
|
18611
|
+
question: "Codex updated itself and stopped",
|
|
18612
|
+
cursor: -1,
|
|
18613
|
+
options: updateChoices(launch).map((c) => ({ label: UPDATE_LABELS[c], hint: c === "resume" && !launch.session ? "Choose a conversation in Codex" : null }))
|
|
18614
|
+
};
|
|
18615
|
+
}
|
|
18616
|
+
function updateRestartPlan(launch, choice) {
|
|
18617
|
+
const kept = launch.argv ? launchOptions("codex", launch.argv).kept.flat() : [];
|
|
18618
|
+
let args = choice === "resume" ? ["resume", ...kept, ...launch.session ? [launch.session] : []] : kept;
|
|
18619
|
+
if (choice === "restart" && launch.argv) {
|
|
18620
|
+
const i = launch.argv.findIndex((a) => /(?:^|\/)codex(?:\.[cm]?js)?$/.test(a));
|
|
18621
|
+
if (i >= 0) args = launch.argv.slice(i + 1);
|
|
18622
|
+
}
|
|
18623
|
+
return { args, mode: choice === "resume" ? "resume" : "fresh", kept, dropped: [], unknownArgs: !launch.argv };
|
|
18624
|
+
}
|
|
18625
|
+
|
|
18626
|
+
const base = (s) => s.replace(/^.*\//, "").replace(/\.(c?js|mjs)$/, "");
|
|
18627
|
+
async function paneForeground(d, paneId, kind) {
|
|
18628
|
+
const r = await d.call("pane.process_info", { pane_id: paneId }, 4e3);
|
|
18629
|
+
const info = r && r.process_info || {};
|
|
18630
|
+
const procs = info.foreground_processes || [];
|
|
18631
|
+
const agent = procs.find((p) => p.name === kind || (p.argv || []).some((a) => base(a) === kind)) || null;
|
|
18632
|
+
const atShell = !agent && Boolean(info.shell_pid) && info.foreground_process_group_id === info.shell_pid;
|
|
18633
|
+
const sh = procs.find((p) => info.shell_pid && p.pid === info.shell_pid);
|
|
18634
|
+
const shell = { pid: Number(info.shell_pid) || null, name: sh ? String(sh.name || (sh.argv || [])[0] || "") || null : null };
|
|
18635
|
+
return { agent, atShell, shell, argv: agent && Array.isArray(agent.argv) ? agent.argv.map(String) : null };
|
|
18636
|
+
}
|
|
18637
|
+
async function waitFor(d, ms, test) {
|
|
18638
|
+
const end = d.now() + ms;
|
|
18639
|
+
while (d.now() < end) {
|
|
18640
|
+
await d.sleep(500);
|
|
18641
|
+
try {
|
|
18642
|
+
if (await test()) return true;
|
|
18643
|
+
} catch {
|
|
18644
|
+
}
|
|
18645
|
+
}
|
|
18646
|
+
return false;
|
|
18647
|
+
}
|
|
18648
|
+
const waitShell = (d, paneId, kind, ms) => waitFor(d, ms, async () => (await paneForeground(d, paneId, kind)).atShell);
|
|
18649
|
+
const busy = async (d, paneId) => {
|
|
18650
|
+
const r = await d.call("pane.get", { pane_id: paneId }, 4e3);
|
|
18651
|
+
const s = r && r.pane && r.pane.agent_status;
|
|
18652
|
+
return s === "working" || s === "blocked";
|
|
18653
|
+
};
|
|
18654
|
+
async function stopAgent(d, p) {
|
|
18655
|
+
const kind = p.agent;
|
|
18656
|
+
const key = (k) => d.call("pane.send_input", { pane_id: p.id, keys: [k] });
|
|
18657
|
+
if (p.status === "working" || p.status === "blocked") {
|
|
18658
|
+
for (let i = 0; i < 2; i++) {
|
|
18659
|
+
await key("esc");
|
|
18660
|
+
if (await waitFor(d, 3e3, async () => !await busy(d, p.id))) break;
|
|
18661
|
+
}
|
|
18662
|
+
}
|
|
18663
|
+
await d.call("pane.send_input", { pane_id: p.id, text: "/exit" });
|
|
18664
|
+
await d.sleep(300);
|
|
18665
|
+
await key("enter");
|
|
18666
|
+
if (await waitShell(d, p.id, kind, 8e3)) return "exit";
|
|
18667
|
+
await key("ctrl+c");
|
|
18668
|
+
await d.sleep(400);
|
|
18669
|
+
await key("ctrl+c");
|
|
18670
|
+
if (await waitShell(d, p.id, kind, 6e3)) return "ctrl-c";
|
|
18671
|
+
throw new HerdrError("restart_stop", "the agent did not stop");
|
|
18672
|
+
}
|
|
18673
|
+
async function startAgent(d, p, plan) {
|
|
18674
|
+
const name = p.name || `${p.agent}-${crypto.randomBytes(2).toString("hex")}`;
|
|
18675
|
+
let last = null;
|
|
18676
|
+
for (let i = 0; i < 6; i++) {
|
|
18677
|
+
try {
|
|
18678
|
+
await d.call("agent.start", { name, kind: p.agent, pane_id: p.id, timeout_ms: 6e4, ...plan.args.length ? { args: plan.args } : {} }, 75e3);
|
|
18679
|
+
return;
|
|
18680
|
+
} catch (e) {
|
|
18681
|
+
last = e;
|
|
18682
|
+
if (last.code === "agent_not_ready") return;
|
|
18683
|
+
if (last.code === "timeout" || last.code === "unreachable") break;
|
|
18684
|
+
await d.sleep(500);
|
|
18685
|
+
}
|
|
18686
|
+
}
|
|
18687
|
+
throw last || new HerdrError("restart_start", "the agent did not restart");
|
|
18688
|
+
}
|
|
18689
|
+
|
|
18690
|
+
const AT_SAVE_MS = 60 * 1e3;
|
|
18691
|
+
const OUTAGE_MS = 15e3;
|
|
18692
|
+
function createSelfUpdates(d, saved = []) {
|
|
18693
|
+
const records = new Map(saved);
|
|
18694
|
+
const restored = new Set(records.keys());
|
|
18695
|
+
const stable = () => JSON.stringify([...records].map(([id, r]) => [id, { ...r, at: 0 }]));
|
|
18696
|
+
const times = () => [...records.values()].map((r) => r.at).join();
|
|
18697
|
+
let written = stable();
|
|
18698
|
+
let writtenTimes = times();
|
|
18699
|
+
let writtenAt = d.now();
|
|
18700
|
+
const persist = () => {
|
|
18701
|
+
var _a;
|
|
18702
|
+
const s = stable();
|
|
18703
|
+
if (s === written && (d.now() - writtenAt < AT_SAVE_MS || times() === writtenTimes)) return;
|
|
18704
|
+
written = s;
|
|
18705
|
+
writtenTimes = times();
|
|
18706
|
+
writtenAt = d.now();
|
|
18707
|
+
(_a = d.save) == null ? void 0 : _a.call(d, [...records]);
|
|
18708
|
+
};
|
|
18709
|
+
const read = async (id) => {
|
|
18710
|
+
var _a, _b;
|
|
18711
|
+
return String(((_b = (_a = await d.call("pane.read", { pane_id: id, source: "recent_unwrapped", lines: 80 }, 4e3)) == null ? void 0 : _a.read) == null ? void 0 : _b.text) || "");
|
|
18712
|
+
};
|
|
18713
|
+
return {
|
|
18714
|
+
get: (id) => records.get(id),
|
|
18715
|
+
clear(id) {
|
|
18716
|
+
records.delete(id);
|
|
18717
|
+
persist();
|
|
18718
|
+
},
|
|
18719
|
+
// Service stopping: keep the exact time Codex was last seen alive, so a
|
|
18720
|
+
// quick restart still tells a short outage from a long one.
|
|
18721
|
+
flush() {
|
|
18722
|
+
var _a;
|
|
18723
|
+
if (records.size) (_a = d.save) == null ? void 0 : _a.call(d, [...records], true);
|
|
18724
|
+
},
|
|
18725
|
+
prune(ids, belongs) {
|
|
18726
|
+
let changed = false;
|
|
18727
|
+
for (const id of records.keys()) if (belongs(id) && !ids.has(id)) {
|
|
18728
|
+
records.delete(id);
|
|
18729
|
+
changed = true;
|
|
18730
|
+
}
|
|
18731
|
+
if (changed) persist();
|
|
18732
|
+
},
|
|
18733
|
+
async observe(p, owned) {
|
|
18734
|
+
var _a, _b, _c, _d;
|
|
18735
|
+
if (owned) return false;
|
|
18736
|
+
let last = records.get(p.id);
|
|
18737
|
+
if (p.agent === "codex") {
|
|
18738
|
+
const fg = await paneForeground(d, p.id, "codex");
|
|
18739
|
+
if (!fg.agent) return false;
|
|
18740
|
+
restored.delete(p.id);
|
|
18741
|
+
const pid = Number(fg.agent.pid);
|
|
18742
|
+
if (last && last.pid === pid && !last.gone && d.now() - last.at < 5e3 && p.agentSession === last.pane.agentSession && p.cwd === last.pane.cwd && p.bornAt === last.pane.bornAt) {
|
|
18743
|
+
last.argv = fg.argv || last.argv;
|
|
18744
|
+
persist();
|
|
18745
|
+
return false;
|
|
18746
|
+
}
|
|
18747
|
+
if (!last || last.pid !== pid || last.gone) {
|
|
18748
|
+
last = { pid, pane: { ...p }, at: d.now(), argv: fg.argv, session: null, conversation: "unknown" };
|
|
18749
|
+
records.set(p.id, last);
|
|
18750
|
+
}
|
|
18751
|
+
last.at = d.now();
|
|
18752
|
+
last.argv = fg.argv || last.argv;
|
|
18753
|
+
last.pane = { id: p.id, workspace: p.workspace, agent: p.agent, agentSession: p.agentSession, bornAt: p.bornAt, cwd: p.cwd, name: p.name, title: p.title };
|
|
18754
|
+
const screen = await read(p.id);
|
|
18755
|
+
const chat = await d.chat(p).catch(() => null);
|
|
18756
|
+
if (chat && (chat.guessed || chat.session && chat.session !== last.session)) {
|
|
18757
|
+
if (chat.session !== last.session) last.conversation = "unknown";
|
|
18758
|
+
last.session = null;
|
|
18759
|
+
}
|
|
18760
|
+
if ((chat == null ? void 0 : chat.available) && !chat.guessed && chat.session) {
|
|
18761
|
+
if (last.session && last.session !== chat.session) last.conversation = "unknown";
|
|
18762
|
+
last.session = chat.session;
|
|
18763
|
+
}
|
|
18764
|
+
if ((_a = chat == null ? void 0 : chat.items) == null ? void 0 : _a.some((i) => i.role === "user" || i.role === "assistant")) {
|
|
18765
|
+
last.conversation = "existing";
|
|
18766
|
+
if (!chat.guessed && chat.session) last.session = chat.session;
|
|
18767
|
+
} else if (chat && last.conversation !== "existing") {
|
|
18768
|
+
const startup = /(?:✨\s*)?Update available!|^Updating Codex via /m.test(screen);
|
|
18769
|
+
const witnessedBirth = Boolean(p.bornAt) && !((_b = fg.argv) == null ? void 0 : _b.some((a) => a === "resume" || a === "fork"));
|
|
18770
|
+
if (chat.available && !chat.guessed && !chat.start && !((_c = chat.items) == null ? void 0 : _c.length) || !p.agentSession && !chat.available && (startup || witnessedBirth)) last.conversation = "empty";
|
|
18771
|
+
}
|
|
18772
|
+
last.oldOutput = Boolean(updateShellSignature(screen));
|
|
18773
|
+
persist();
|
|
18774
|
+
return false;
|
|
18775
|
+
}
|
|
18776
|
+
if (!last) return false;
|
|
18777
|
+
if (p.agent) {
|
|
18778
|
+
records.delete(p.id);
|
|
18779
|
+
persist();
|
|
18780
|
+
return false;
|
|
18781
|
+
}
|
|
18782
|
+
if (!last.gone) {
|
|
18783
|
+
if (d.now() - last.at > OUTAGE_MS + (restored.has(p.id) ? AT_SAVE_MS : 0)) last.checked = true;
|
|
18784
|
+
last.gone = d.now();
|
|
18785
|
+
persist();
|
|
18786
|
+
}
|
|
18787
|
+
if (!last.checked) {
|
|
18788
|
+
if (d.now() - last.gone < 3e3) {
|
|
18789
|
+
p.leaving = true;
|
|
18790
|
+
return false;
|
|
18791
|
+
}
|
|
18792
|
+
last.checked = true;
|
|
18793
|
+
if (d.now() - last.gone < 15e3 && !last.oldOutput && (await paneForeground(d, p.id, "codex")).atShell) {
|
|
18794
|
+
last.signature = updateShellSignature(await read(p.id)) || void 0;
|
|
18795
|
+
if (last.signature) {
|
|
18796
|
+
const chat = await d.chat(last.pane).catch(() => null);
|
|
18797
|
+
if (!chat || chat.guessed || !chat.available && last.session) {
|
|
18798
|
+
if (last.conversation === "empty") last.conversation = "unknown";
|
|
18799
|
+
}
|
|
18800
|
+
if (chat && (chat.guessed || chat.session && chat.session !== last.session)) last.session = null;
|
|
18801
|
+
if ((chat == null ? void 0 : chat.available) && !chat.guessed && chat.session) last.session = chat.session;
|
|
18802
|
+
if ((_d = chat == null ? void 0 : chat.items) == null ? void 0 : _d.some((i) => i.role === "user" || i.role === "assistant")) {
|
|
18803
|
+
last.conversation = "existing";
|
|
18804
|
+
if (!chat.guessed && chat.session) last.session = chat.session;
|
|
18805
|
+
else if (chat.session !== last.session) last.session = null;
|
|
18806
|
+
}
|
|
18807
|
+
}
|
|
18808
|
+
}
|
|
18809
|
+
persist();
|
|
18810
|
+
}
|
|
18811
|
+
if (!last.signature) return false;
|
|
18812
|
+
p.agent = "codex";
|
|
18813
|
+
p.name = last.pane.name;
|
|
18814
|
+
p.title = last.pane.title;
|
|
18815
|
+
p.agentSession = last.session;
|
|
18816
|
+
p.bornAt = last.pane.bornAt;
|
|
18817
|
+
p.status = "blocked";
|
|
18818
|
+
p.stopped = { reason: "update" };
|
|
18819
|
+
p.prompt = updatePrompt(last);
|
|
18820
|
+
return true;
|
|
18821
|
+
},
|
|
18822
|
+
async check(id) {
|
|
18823
|
+
const last = records.get(id);
|
|
18824
|
+
if (!(last == null ? void 0 : last.signature) || !(await paneForeground(d, id, "codex")).atShell) return null;
|
|
18825
|
+
return updateShellSignature(await read(id)) === last.signature ? last : null;
|
|
18826
|
+
}
|
|
18827
|
+
};
|
|
18828
|
+
}
|
|
18829
|
+
|
|
17863
18830
|
var _a, _b;
|
|
17864
18831
|
const fsp$1 = fs.promises;
|
|
17865
18832
|
function trFor(p) {
|
|
@@ -17909,17 +18876,22 @@ function watchScreen(paneId, ms = 6e4) {
|
|
|
17909
18876
|
const choicesCache = /* @__PURE__ */ new Map();
|
|
17910
18877
|
const askChecks = /* @__PURE__ */ new Map();
|
|
17911
18878
|
async function choicesFor(p, rev, strict, watch = false) {
|
|
18879
|
+
if (modelControlBusy(p.id)) {
|
|
18880
|
+
choicesCache.delete(p.id);
|
|
18881
|
+
return { choices: null, screen: null, menu: null, interaction: "managed" };
|
|
18882
|
+
}
|
|
17912
18883
|
const c = choicesCache.get(p.id);
|
|
17913
18884
|
const watched = (screenWatch.get(p.id) || 0) > Date.now();
|
|
17914
18885
|
if (!watched) screenWatch.delete(p.id);
|
|
17915
|
-
if (c && keepReading(c, rev, strict, watch || watched, Date.now())) return c;
|
|
18886
|
+
if (c && !(p.status === "unknown" && c.interaction === "terminal") && keepReading(c, rev, strict, watch || watched, Date.now())) return c;
|
|
17916
18887
|
let out;
|
|
17917
18888
|
try {
|
|
17918
18889
|
const r = await herdr("pane.read", { pane_id: p.id, source: "detection" }, 4e3);
|
|
17919
18890
|
const text = r.read && r.read.text;
|
|
17920
18891
|
const framed = String(text || "").split("\n").some((l) => TOP.test(l));
|
|
17921
|
-
|
|
17922
|
-
|
|
18892
|
+
const usageLimit = p.status === "unknown" ? null : parseUsageLimit(String(text || ""), p.agent);
|
|
18893
|
+
let choices = usageLimit ? null : p.agent === "omp" ? parseOmpPrompt(text) : parseChoices(text, { strict });
|
|
18894
|
+
const menu = !usageLimit && p.agent !== "omp" && framed && (strict || !choices || !parseChoices(text, { strict: true })) ? await readMenu(p.id) : null;
|
|
17923
18895
|
if (menu) choices = null;
|
|
17924
18896
|
if (choices && p.agent === "omp" && !choices.detail) choices = await withOmpTab(p.id, completeOmpAsk(choices, await transcripts.pendingAsk(p).catch(() => [])));
|
|
17925
18897
|
if (choices && p.agent === "claude") {
|
|
@@ -17928,7 +18900,9 @@ async function choicesFor(p, rev, strict, watch = false) {
|
|
|
17928
18900
|
choices = completeClaudeAsk(choices, await transcripts.pendingClaudeQuestions(p).catch(() => []), checks);
|
|
17929
18901
|
} else if (!choices) askChecks.delete(p.id);
|
|
17930
18902
|
const settings = p.agent === "claude" && settingsTabs(text) !== null;
|
|
17931
|
-
|
|
18903
|
+
const screen = menu || settings ? null : parseWaitScreen(text, { choices: Boolean(choices) });
|
|
18904
|
+
const interaction = classifyScreen({ text: String(text || ""), agent: p.agent, choices, menu, screen, settings, status: p.status });
|
|
18905
|
+
out = interaction === "terminal" ? { choices: null, screen: null, menu: null, interaction } : { choices, screen: usageLimit ? null : screen, menu, interaction, ...usageLimit ? { usageLimit } : {} };
|
|
17932
18906
|
noteScreen(p.id, p.agent, text);
|
|
17933
18907
|
if (choices && (choices.detail || isPermissionQuestion(choices.question))) {
|
|
17934
18908
|
const named = p.agent === "omp" && choices.detail && choices.detail.tool !== "tool" ? choices.detail.tool : null;
|
|
@@ -17938,7 +18912,7 @@ async function choicesFor(p, rev, strict, watch = false) {
|
|
|
17938
18912
|
if (p.agent === "omp" && !choices.question && tr) choices.question = `Allow tool: ${tr.tool}`;
|
|
17939
18913
|
}
|
|
17940
18914
|
} catch {
|
|
17941
|
-
out = { choices: c ? c.choices : null, screen: c ? c.screen : null, menu: c ? c.menu : null };
|
|
18915
|
+
out = { choices: c ? c.choices : null, screen: c ? c.screen : null, menu: c ? c.menu : null, interaction: (c == null ? void 0 : c.interaction) || "unknown", ...(c == null ? void 0 : c.usageLimit) ? { usageLimit: c.usageLimit } : {} };
|
|
17942
18916
|
}
|
|
17943
18917
|
choicesCache.set(p.id, { rev, strict, at: Date.now(), ...out });
|
|
17944
18918
|
return out;
|
|
@@ -18089,7 +19063,7 @@ function addQueued(paneId, text, opts = {}) {
|
|
|
18089
19063
|
};
|
|
18090
19064
|
queued.set(paneId, [...list, e]);
|
|
18091
19065
|
sent.set(paneId, addSent(sent.get(paneId) || [], e.text, e.at));
|
|
18092
|
-
return publicEntry(e);
|
|
19066
|
+
return queuedPasted(publicEntry(e));
|
|
18093
19067
|
}
|
|
18094
19068
|
const sent = /* @__PURE__ */ new Map();
|
|
18095
19069
|
const sentText = (paneId, entry, taken = []) => {
|
|
@@ -18112,7 +19086,11 @@ const restarting = (paneId) => {
|
|
|
18112
19086
|
const GUARD_STATES = /* @__PURE__ */ new Set(["idle", "done", "working"]);
|
|
18113
19087
|
function sendPrompt(p, text) {
|
|
18114
19088
|
return withPaneLock(p.id, async () => {
|
|
18115
|
-
|
|
19089
|
+
const now = findPane(p.id);
|
|
19090
|
+
if ((now == null ? void 0 : now.stopped) || (now == null ? void 0 : now.leaving)) {
|
|
19091
|
+
throw new HerdrError(isSlashCommand(text) ? "stale" : "no_input", "Restart Codex before sending a command.");
|
|
19092
|
+
}
|
|
19093
|
+
if (p.agent !== "claude" || !GUARD_STATES.has(p.status || "") || isSlashCommand(text) && !p.usageLimit) return agentPrompt(p.id, text);
|
|
18116
19094
|
const keep = (texts) => {
|
|
18117
19095
|
for (const t of texts || []) {
|
|
18118
19096
|
addQueued(p.id, t, { held: true });
|
|
@@ -18131,7 +19109,7 @@ function sendPrompt(p, text) {
|
|
|
18131
19109
|
keys: async (keys) => {
|
|
18132
19110
|
await herdr("pane.send_input", { pane_id: p.id, keys });
|
|
18133
19111
|
},
|
|
18134
|
-
sleep,
|
|
19112
|
+
sleep: sleep$1,
|
|
18135
19113
|
sentSince: async (since) => {
|
|
18136
19114
|
const r2 = await transcripts.chat(p, {});
|
|
18137
19115
|
return (r2.items || []).filter((i) => i.role === "user" && i.ts && Date.parse(i.ts) >= since - 1e3).map((i) => i.text);
|
|
@@ -18149,7 +19127,7 @@ const isBusyError = (e) => e instanceof HerdrError && (e.code === "input_busy" |
|
|
|
18149
19127
|
const deliverBusy = /* @__PURE__ */ new Set();
|
|
18150
19128
|
function deliverHeld(p) {
|
|
18151
19129
|
const q = nextHeld(queued.get(p.id) || []);
|
|
18152
|
-
if (!q || deliverBusy.has(p.id) || !p.agent || !INPUT_STATES.has(p.status || "") || restarting(p.id)) return;
|
|
19130
|
+
if (!q || deliverBusy.has(p.id) || !p.agent || p.stopped || !INPUT_STATES.has(p.status || "") || restarting(p.id)) return;
|
|
18153
19131
|
deliverBusy.add(p.id);
|
|
18154
19132
|
herdr("pane.read", { pane_id: p.id, source: "detection" }, 4e3).then(async (r) => {
|
|
18155
19133
|
if (!inputVisible(r.read && r.read.text) || !q.held || q.failed) return;
|
|
@@ -18200,13 +19178,13 @@ function takeBackFromClaude(p) {
|
|
|
18200
19178
|
await herdr("pane.send_input", { pane_id: p.id, keys });
|
|
18201
19179
|
},
|
|
18202
19180
|
chat: async () => (await transcripts.chat(p, { fresh: true })).items || [],
|
|
18203
|
-
sleep,
|
|
19181
|
+
sleep: sleep$1,
|
|
18204
19182
|
pending: () => (queued.get(p.id) || []).map((q) => q.text)
|
|
18205
19183
|
}).catch(() => null);
|
|
18206
19184
|
}
|
|
18207
19185
|
async function cancelTurn(p, text) {
|
|
18208
19186
|
if (!unansweredTurn((await transcripts.chat(p, { fresh: true })).items || [], text)) return null;
|
|
18209
|
-
const r = await interruptAgent({ call: (m, params, t) => herdr(m, params, t), sleep, now: Date.now }, p);
|
|
19187
|
+
const r = await interruptAgent({ call: (m, params, t) => herdr(m, params, t), sleep: sleep$1, now: Date.now }, p);
|
|
18210
19188
|
if (!r.stopped) return null;
|
|
18211
19189
|
const item = await takeBackFromClaude(p);
|
|
18212
19190
|
if (!item) return null;
|
|
@@ -18220,13 +19198,14 @@ function retryQueued(paneId, id) {
|
|
|
18220
19198
|
q.failed = false;
|
|
18221
19199
|
q.held = true;
|
|
18222
19200
|
q.at = Date.now();
|
|
19201
|
+
delete q.back;
|
|
18223
19202
|
delete q.busy;
|
|
18224
19203
|
delete q.readySince;
|
|
18225
19204
|
delete q.turnSeen;
|
|
18226
19205
|
delete q.stuckSince;
|
|
18227
19206
|
delete q.noInput;
|
|
18228
19207
|
setTimeout(poll, 50);
|
|
18229
|
-
return publicEntry(q);
|
|
19208
|
+
return queuedPasted(publicEntry(q));
|
|
18230
19209
|
}
|
|
18231
19210
|
const reconcileBusy = /* @__PURE__ */ new Set();
|
|
18232
19211
|
function reconcileQueued(p) {
|
|
@@ -18242,11 +19221,11 @@ function reconcileQueued(p) {
|
|
|
18242
19221
|
}
|
|
18243
19222
|
const unqueueBusy = /* @__PURE__ */ new Set();
|
|
18244
19223
|
async function deliveryDone(paneId) {
|
|
18245
|
-
for (let i = 0; i < 60 && deliverBusy.has(paneId); i++) await sleep(100);
|
|
19224
|
+
for (let i = 0; i < 60 && deliverBusy.has(paneId); i++) await sleep$1(100);
|
|
18246
19225
|
}
|
|
18247
19226
|
async function cancelQueued(paneId, text, id) {
|
|
18248
19227
|
const p = findPane(paneId);
|
|
18249
|
-
if (!p
|
|
19228
|
+
if (!p) throw new HerdrError("not_found", "agent not found");
|
|
18250
19229
|
let mine = findQueued(queued.get(p.id) || [], text, id);
|
|
18251
19230
|
if (mine && (mine.held || mine.failed) && deliverBusy.has(p.id)) {
|
|
18252
19231
|
await deliveryDone(p.id);
|
|
@@ -18262,6 +19241,7 @@ async function cancelQueued(paneId, text, id) {
|
|
|
18262
19241
|
setTimeout(poll, 50);
|
|
18263
19242
|
return { text: mine.text };
|
|
18264
19243
|
}
|
|
19244
|
+
if (!p.agent) throw new HerdrError("not_found", "agent not found");
|
|
18265
19245
|
const pend = pendingPrompts.get(p.id);
|
|
18266
19246
|
if (pend && !pendingBusy.has(p.id) && sameMsg(msgText(pend.text), msgText(text))) {
|
|
18267
19247
|
pendingPrompts.delete(p.id);
|
|
@@ -18288,7 +19268,7 @@ async function cancelQueued(paneId, text, id) {
|
|
|
18288
19268
|
return { queue: r2.queue || [], items: r2.items || [] };
|
|
18289
19269
|
},
|
|
18290
19270
|
prompt: (t) => agentPrompt(p.id, t),
|
|
18291
|
-
sleep,
|
|
19271
|
+
sleep: sleep$1,
|
|
18292
19272
|
original: (e) => {
|
|
18293
19273
|
var _a2, _b2;
|
|
18294
19274
|
return !e.text && e.images ? ((_a2 = ownPhotos.shift()) == null ? void 0 : _a2.text) || fromHistory(e) : ((_b2 = own.find((q) => q !== mine && sameMsg(e.text, msgText(q.text)))) == null ? void 0 : _b2.text) || fromHistory(e) || e.text;
|
|
@@ -18320,6 +19300,41 @@ const pendingBusy = /* @__PURE__ */ new Set();
|
|
|
18320
19300
|
const READY = /* @__PURE__ */ new Set(["done", "idle"]);
|
|
18321
19301
|
const TURN = /* @__PURE__ */ new Set(["working", "blocked"]);
|
|
18322
19302
|
const restarts = /* @__PURE__ */ new Map();
|
|
19303
|
+
const LAST_AGENTS_FILE = path$1.join(DATA_DIR, "last-agents.json");
|
|
19304
|
+
let lastAgentsWrite = Promise.resolve();
|
|
19305
|
+
const selfUpdates = createSelfUpdates({
|
|
19306
|
+
call: herdr,
|
|
19307
|
+
sleep: sleep$1,
|
|
19308
|
+
now: Date.now,
|
|
19309
|
+
chat: (p) => transcripts.chat(p, { fresh: true }),
|
|
19310
|
+
// Only called when a record changed (see agentSelfUpdate.ts).
|
|
19311
|
+
save: (records, sync) => {
|
|
19312
|
+
const s = JSON.stringify(records) + "\n";
|
|
19313
|
+
if (sync) {
|
|
19314
|
+
try {
|
|
19315
|
+
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
19316
|
+
fs.writeFileSync(LAST_AGENTS_FILE, s, { mode: 384 });
|
|
19317
|
+
} catch {
|
|
19318
|
+
}
|
|
19319
|
+
return;
|
|
19320
|
+
}
|
|
19321
|
+
lastAgentsWrite = lastAgentsWrite.then(async () => {
|
|
19322
|
+
await fsp$1.mkdir(DATA_DIR, { recursive: true });
|
|
19323
|
+
await fsp$1.writeFile(LAST_AGENTS_FILE, s, { mode: 384 });
|
|
19324
|
+
}).catch(() => {
|
|
19325
|
+
});
|
|
19326
|
+
}
|
|
19327
|
+
}, (() => {
|
|
19328
|
+
try {
|
|
19329
|
+
return JSON.parse(fs.readFileSync(LAST_AGENTS_FILE, "utf8"));
|
|
19330
|
+
} catch {
|
|
19331
|
+
return [];
|
|
19332
|
+
}
|
|
19333
|
+
})());
|
|
19334
|
+
function dismissStopped(paneId) {
|
|
19335
|
+
selfUpdates.clear(paneId);
|
|
19336
|
+
giveBackHeld(queued.get(paneId));
|
|
19337
|
+
}
|
|
18323
19338
|
function flushPending(p) {
|
|
18324
19339
|
const pend = pendingPrompts.get(p.id);
|
|
18325
19340
|
if (!pend || pendingBusy.has(p.id)) return;
|
|
@@ -18421,6 +19436,7 @@ async function enrich(next, snap, machine) {
|
|
|
18421
19436
|
const firstSnapshot = !seenMachines.has(machine);
|
|
18422
19437
|
const prevStatus = new Map((((_a2 = mstates.get(machine)) == null ? void 0 : _a2.panes) || []).map((p) => [p.id, p.status || void 0]));
|
|
18423
19438
|
const live = new Set(next.panes.map((p) => p.id));
|
|
19439
|
+
selfUpdates.prune(live, (id) => machineOf(id) === machine);
|
|
18424
19440
|
for (const id of commands.keys()) if (machineOf(id) === machine && !live.has(id)) commands.delete(id);
|
|
18425
19441
|
for (const p of next.panes) {
|
|
18426
19442
|
const b = agentBorn.get(p.id);
|
|
@@ -18445,7 +19461,14 @@ async function enrich(next, snap, machine) {
|
|
|
18445
19461
|
seenMachines.add(machine);
|
|
18446
19462
|
(_b2 = getMachine(machine)) == null ? void 0 : _b2.transcripts.observe(next.panes);
|
|
18447
19463
|
const revs = new Map((snap.panes || []).map((p) => [joinId(machine, p.pane_id), p.revision]));
|
|
18448
|
-
|
|
19464
|
+
const exited = await Promise.all(next.panes.map((p) => selfUpdates.observe(p, restarts.has(p.id)).catch(() => false)));
|
|
19465
|
+
for (const [i, p] of next.panes.entries()) {
|
|
19466
|
+
if (exited[i]) {
|
|
19467
|
+
p.interaction = "managed";
|
|
19468
|
+
if (queued.has(p.id)) p.queued = queued.get(p.id).map((q) => queuedPasted(publicEntry(q)));
|
|
19469
|
+
continue;
|
|
19470
|
+
}
|
|
19471
|
+
if (leftForGood(p, findPane(p.id))) giveBackHeld(queued.get(p.id));
|
|
18449
19472
|
if (pendingPrompts.has(p.id)) {
|
|
18450
19473
|
p.pendingPrompt = true;
|
|
18451
19474
|
flushPending(p);
|
|
@@ -18460,13 +19483,13 @@ async function enrich(next, snap, machine) {
|
|
|
18460
19483
|
else queued.delete(p.id);
|
|
18461
19484
|
}
|
|
18462
19485
|
const list = queued.get(p.id);
|
|
18463
|
-
const menu = Boolean(before && (before.menu || before.prompt || before.screen)) || restarting(p.id);
|
|
19486
|
+
const menu = Boolean(before && (before.menu || before.prompt || before.screen || before.interaction === "terminal")) || restarting(p.id);
|
|
18464
19487
|
if (list && TURN.has(p.status || "")) {
|
|
18465
19488
|
for (const q of list) if (!q.held && !q.failed) q.turnSeen = true;
|
|
18466
19489
|
}
|
|
18467
19490
|
if (list && checkQueue(list, (before == null ? void 0 : before.ompShell) ? "working" : p.status, Date.now(), menu)) log(`message not sent on ${p.id}`);
|
|
18468
19491
|
if (list) deliverHeld(p);
|
|
18469
|
-
if (list && list.length) p.queued = list.map(publicEntry);
|
|
19492
|
+
if (list && list.length) p.queued = list.map((q) => queuedPasted(publicEntry(q)));
|
|
18470
19493
|
}
|
|
18471
19494
|
const rs = restarts.get(p.id);
|
|
18472
19495
|
if (rs && rs.phase === "failed" && (p.agent && rs.stopped || Date.now() - rs.at > 6e5)) restarts.delete(p.id);
|
|
@@ -18484,10 +19507,16 @@ async function enrich(next, snap, machine) {
|
|
|
18484
19507
|
if (p.status !== "working") {
|
|
18485
19508
|
const young = !p.agentSession || p.bornAt && Date.now() - p.bornAt < 12e4;
|
|
18486
19509
|
const c = await choicesFor(p, revs.get(p.id), p.status !== "blocked", Boolean(young));
|
|
19510
|
+
p.interaction = c.interaction;
|
|
19511
|
+
if (c.usageLimit) {
|
|
19512
|
+
p.usageLimit = c.usageLimit;
|
|
19513
|
+
p.status = "idle";
|
|
19514
|
+
}
|
|
18487
19515
|
if (c.choices) p.prompt = c.choices;
|
|
18488
19516
|
if (c.screen) p.screen = c.screen;
|
|
18489
19517
|
if (c.menu) p.menu = c.menu;
|
|
18490
19518
|
} else {
|
|
19519
|
+
p.interaction = "working";
|
|
18491
19520
|
choicesCache.delete(p.id);
|
|
18492
19521
|
askChecks.delete(p.id);
|
|
18493
19522
|
}
|
|
@@ -18672,14 +19701,17 @@ const localMachineOf = (ms) => ms.find((m) => m.local);
|
|
|
18672
19701
|
function stopPolling() {
|
|
18673
19702
|
stopped = true;
|
|
18674
19703
|
if (pollTimer) clearTimeout(pollTimer);
|
|
19704
|
+
selfUpdates.flush();
|
|
18675
19705
|
}
|
|
18676
19706
|
const notifyTimers = /* @__PURE__ */ new Map();
|
|
18677
19707
|
function watchTransitions(prev, next) {
|
|
19708
|
+
var _a2;
|
|
18678
19709
|
const before = new Map(prev.panes.map((p) => [p.id, p.status]));
|
|
18679
19710
|
for (const p of next.panes) {
|
|
18680
19711
|
const was = before.get(p.id);
|
|
18681
|
-
|
|
18682
|
-
if (
|
|
19712
|
+
const updated = p.stopped && !((_a2 = prev.panes.find((x) => x.id === p.id)) == null ? void 0 : _a2.stopped);
|
|
19713
|
+
if (!p.agent || was === p.status && !updated) continue;
|
|
19714
|
+
if (updated || was === "working" && (p.status === "blocked" || READY.has(p.status || ""))) {
|
|
18683
19715
|
clearTimeout(notifyTimers.get(p.id));
|
|
18684
19716
|
const target = p.status;
|
|
18685
19717
|
notifyTimers.set(p.id, setTimeout(() => {
|
|
@@ -19170,1059 +20202,1598 @@ function stopNotices() {
|
|
|
19170
20202
|
listeners.clear();
|
|
19171
20203
|
}
|
|
19172
20204
|
|
|
19173
|
-
|
|
19174
|
-
|
|
19175
|
-
|
|
19176
|
-
|
|
19177
|
-
|
|
19178
|
-
|
|
19179
|
-
|
|
19180
|
-
|
|
19181
|
-
|
|
19182
|
-
|
|
19183
|
-
|
|
20205
|
+
// The phone address: wherdr published on the tailnet by `tailscale serve`
|
|
20206
|
+
// (private, only your devices). Reads Tailscale's state, publishes and
|
|
20207
|
+
// unpublishes wherdr's port, and checks that the address really answers.
|
|
20208
|
+
// Used by the server (Settings › Phone, server/utils/phone.ts) and by
|
|
20209
|
+
// `wherdr phone` / `wherdr panel`. No dependency on the rest of bin/lib nor
|
|
20210
|
+
// on packages: the server bundles this file.
|
|
20211
|
+
|
|
20212
|
+
const exec = promisify(execFile);
|
|
20213
|
+
|
|
20214
|
+
const LINKS = {
|
|
20215
|
+
download: 'https://tailscale.com/download',
|
|
20216
|
+
https: 'https://login.tailscale.com/admin/dns',
|
|
20217
|
+
operator: 'https://tailscale.com/kb/1242/tailscale-serve',
|
|
19184
20218
|
};
|
|
19185
|
-
|
|
19186
|
-
|
|
19187
|
-
|
|
19188
|
-
|
|
19189
|
-
const
|
|
19190
|
-
|
|
19191
|
-
|
|
19192
|
-
|
|
19193
|
-
|
|
19194
|
-
|
|
19195
|
-
|
|
19196
|
-
|
|
19197
|
-
|
|
19198
|
-
|
|
19199
|
-
|
|
19200
|
-
|
|
19201
|
-
|
|
19202
|
-
|
|
20219
|
+
|
|
20220
|
+
// The tailscale CLI: the macOS app's binary, Homebrew, system folders, then the PATH.
|
|
20221
|
+
function tailscaleBin(env = process.env, exists = existsSync) {
|
|
20222
|
+
const dirs = ['/Applications/Tailscale.app/Contents/MacOS/Tailscale', '/opt/homebrew/bin/tailscale', '/usr/local/bin/tailscale', '/usr/bin/tailscale'];
|
|
20223
|
+
for (const dir of (env.PATH || '').split(path$1.delimiter)) if (dir) dirs.push(path$1.join(dir, 'tailscale'));
|
|
20224
|
+
return dirs.find(p => exists(p)) || null
|
|
20225
|
+
}
|
|
20226
|
+
|
|
20227
|
+
// Tailscale's own socket on Linux. Its local API answers the same JSON as the
|
|
20228
|
+
// CLI: a Docker container has no `tailscale` command, but docker-compose.yml
|
|
20229
|
+
// mounts this folder, so wherdr reads the state there. Anyone may read; the
|
|
20230
|
+
// API also accepts changes from Tailscale's operator, but wherdr only ever
|
|
20231
|
+
// sends GET requests to it (localApi below).
|
|
20232
|
+
const TAILSCALED_SOCKET = '/var/run/tailscale/tailscaled.sock';
|
|
20233
|
+
function tailscaledSocket(env = process.env, exists = existsSync) {
|
|
20234
|
+
const file = env.WHERDR_TAILSCALED_SOCKET || TAILSCALED_SOCKET;
|
|
20235
|
+
return exists(file) ? file : null
|
|
20236
|
+
}
|
|
20237
|
+
|
|
20238
|
+
// GET on Tailscale's local API → the body, '' on any failure.
|
|
20239
|
+
function localApi(socketPath, route, timeoutMs = 4000) {
|
|
20240
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
20241
|
+
const req = http.get({ socketPath, path: `/localapi/v0/${route}`, headers: { host: 'local-tailscaled.sock' }, timeout: timeoutMs }, (res) => {
|
|
20242
|
+
let text = '';
|
|
20243
|
+
res.setEncoding('utf8');
|
|
20244
|
+
res.on('data', (c) => { if (text.length < 1 << 20) text += c; });
|
|
20245
|
+
res.on('end', () => resolve(res.statusCode === 200 ? text : ''));
|
|
20246
|
+
res.on('error', () => resolve(''));
|
|
20247
|
+
});
|
|
20248
|
+
req.on('timeout', () => req.destroy());
|
|
20249
|
+
req.on('error', () => resolve(''));
|
|
20250
|
+
return promise
|
|
20251
|
+
}
|
|
20252
|
+
|
|
20253
|
+
// `tailscale status --json` → { name, connected, https }: this machine's
|
|
20254
|
+
// tailnet name (machine.tailnet.ts.net) or null, whether Tailscale is up,
|
|
20255
|
+
// and whether the tailnet issues HTTPS certificates.
|
|
20256
|
+
function tailnetStatus(statusJson) {
|
|
20257
|
+
let s = null;
|
|
20258
|
+
try { s = JSON.parse(statusJson); } catch {}
|
|
20259
|
+
const dns = s?.Self?.DNSName;
|
|
20260
|
+
return {
|
|
20261
|
+
name: typeof dns === 'string' && dns ? dns.replace(/\.$/, '') : null,
|
|
20262
|
+
connected: s?.BackendState === 'Running',
|
|
20263
|
+
https: Array.isArray(s?.CertDomains) && s.CertDomains.length > 0,
|
|
20264
|
+
}
|
|
20265
|
+
}
|
|
20266
|
+
|
|
20267
|
+
// The HTTPS entries of a serve configuration that proxy to wherdr's local
|
|
20268
|
+
// port on this machine's own name: [{ hostPort, httpsPort, funnel }], or null
|
|
20269
|
+
// when the configuration could not be read (Tailscale stopped, not JSON).
|
|
20270
|
+
// Background entries (`--bg`) and those of a `tailscale serve` still running
|
|
20271
|
+
// in a terminal (Foreground) both count. `funnel`: also open to the Internet.
|
|
20272
|
+
function wherdrEntries(name, port, serveJson) {
|
|
20273
|
+
let config = null;
|
|
20274
|
+
try { config = JSON.parse(serveJson); } catch {}
|
|
20275
|
+
if (!name || !config || typeof config !== 'object' || Array.isArray(config)) return null
|
|
20276
|
+
const target = new RegExp(`^(https?://)?(127\\.0\\.0\\.1|localhost):${port}/?$`);
|
|
20277
|
+
const out = [];
|
|
20278
|
+
for (const part of [config, ...Object.values(config.Foreground || {})]) {
|
|
20279
|
+
for (const [hostPort, entry] of Object.entries(part?.Web || {})) {
|
|
20280
|
+
const colon = hostPort.lastIndexOf(':');
|
|
20281
|
+
if (hostPort.slice(0, colon).toLowerCase() !== name.toLowerCase()) continue
|
|
20282
|
+
if (!Object.values(entry?.Handlers || {}).some(h => target.test(h?.Proxy || ''))) continue
|
|
20283
|
+
out.push({ hostPort, httpsPort: hostPort.slice(colon + 1), funnel: Boolean(part.AllowFunnel?.[hostPort]) });
|
|
20284
|
+
}
|
|
19203
20285
|
}
|
|
19204
|
-
|
|
19205
|
-
return { path: file, name: stored, kind: check.kind, size: data.length, ref: attachmentRef(pane.agent, check.kind, file) };
|
|
20286
|
+
return out
|
|
19206
20287
|
}
|
|
19207
|
-
|
|
20288
|
+
|
|
20289
|
+
const addressOf = (name, httpsPort) => `https://${name}${httpsPort === '443' ? '' : `:${httpsPort}`}/`;
|
|
20290
|
+
|
|
20291
|
+
// Phone address: the `tailscale serve` entry whose proxy targets wherdr's
|
|
20292
|
+
// local port (served on any HTTPS port), else the address publishing would
|
|
20293
|
+
// give (same port) with served: false. Null without a tailnet name.
|
|
20294
|
+
function phoneAddress(name, port, serveJson) {
|
|
20295
|
+
if (!name) return null
|
|
20296
|
+
const entries = wherdrEntries(name, port, serveJson) || [];
|
|
20297
|
+
const hit = entries.find(e => !e.funnel) || entries[0];
|
|
20298
|
+
if (hit) return { url: addressOf(name, hit.httpsPort), served: true, httpsPort: hit.httpsPort, ...(hit.funnel ? { funnel: true } : {}) }
|
|
20299
|
+
return { url: `https://${name}:${port}/`, served: false, httpsPort: String(port) }
|
|
20300
|
+
}
|
|
20301
|
+
|
|
20302
|
+
// The addresses wherdr adopts by itself (server/utils/phone.ts): what
|
|
20303
|
+
// `tailscale serve` publishes for its port on this machine's name, read from
|
|
20304
|
+
// Tailscale, which only the owner of the machine configures. null: unknown
|
|
20305
|
+
// (nothing is changed then); []: nothing is published. An address that
|
|
20306
|
+
// `tailscale funnel` also opens to the Internet is never adopted.
|
|
20307
|
+
function servedAddresses(name, port, serveJson) {
|
|
20308
|
+
const entries = wherdrEntries(name, port, serveJson);
|
|
20309
|
+
return entries && entries.filter(e => !e.funnel).map(e => addressOf(name, e.httpsPort))
|
|
20310
|
+
}
|
|
20311
|
+
|
|
20312
|
+
// HTTPS ports already published by `tailscale serve` (for anything).
|
|
20313
|
+
function servedPorts(serveJson) {
|
|
19208
20314
|
try {
|
|
19209
|
-
|
|
19210
|
-
|
|
19211
|
-
|
|
19212
|
-
|
|
19213
|
-
|
|
20315
|
+
const web = JSON.parse(serveJson || '{}')?.Web || {};
|
|
20316
|
+
return Object.keys(web).map(k => k.slice(k.lastIndexOf(':') + 1))
|
|
20317
|
+
} catch { return [] }
|
|
20318
|
+
}
|
|
20319
|
+
|
|
20320
|
+
// The command that publishes wherdr's port, as typed by hand (Docker mode).
|
|
20321
|
+
// `httpsPort`: the tailnet port, wherdr's own by default.
|
|
20322
|
+
const publishCommand = (port, httpsPort = port) => `tailscale serve --bg --https=${httpsPort} http://127.0.0.1:${port}`;
|
|
20323
|
+
const publishArgs = port => ['serve', '--bg', `--https=${port}`, `http://127.0.0.1:${port}`];
|
|
20324
|
+
const unpublishArgs = httpsPort => ['serve', `--https=${httpsPort}`, 'off'];
|
|
20325
|
+
|
|
20326
|
+
// A tailnet address typed by hand: https://machine.tailnet.ts.net[:port]/ → its
|
|
20327
|
+
// normalized form, or null.
|
|
20328
|
+
function tailnetUrl(text) {
|
|
20329
|
+
try {
|
|
20330
|
+
const u = new URL(String(text || '').trim());
|
|
20331
|
+
if (u.protocol !== 'https:' || !/^[a-z0-9-]+(\.[a-z0-9-]+)*\.ts\.net$/i.test(u.hostname) || u.username || u.password) return null
|
|
20332
|
+
return `https://${u.host.toLowerCase()}/`
|
|
20333
|
+
} catch { return null }
|
|
20334
|
+
}
|
|
20335
|
+
|
|
20336
|
+
// A failed `tailscale serve` → { code, link } for the known causes, else null.
|
|
20337
|
+
// code: 'operator' (Linux: the user may not change Tailscale's settings),
|
|
20338
|
+
// 'https' (no HTTPS certificates on the tailnet), 'offline' (not connected).
|
|
20339
|
+
function serveError(output) {
|
|
20340
|
+
const text = String(output || '');
|
|
20341
|
+
const link = /https:\/\/login\.tailscale\.com\/\S+/.exec(text)?.[0];
|
|
20342
|
+
if (/access denied|permission denied|--operator|not permitted/i.test(text)) return { code: 'operator', link: LINKS.operator }
|
|
20343
|
+
if (/https?\b.*(not enabled|disabled)|serve.*not enabled|enable https|certificates?.*(not enabled|disabled)|\/f\/serve/i.test(text)) return { code: 'https', link: link || LINKS.https }
|
|
20344
|
+
if (/needslogin|not logged in|logged out|tailscale is stopped|is tailscale running|not running|failed to connect to local tailscale/i.test(text)) return { code: 'offline', link: LINKS.download }
|
|
20345
|
+
return null
|
|
20346
|
+
}
|
|
20347
|
+
|
|
20348
|
+
// Tailscale's state for wherdr's port: { installed, bin, socket, connected,
|
|
20349
|
+
// https, name, phone, taken, served }. Read with the CLI, or without one
|
|
20350
|
+
// (Docker) from Tailscale's socket.
|
|
20351
|
+
async function inspect(port, bin = tailscaleBin(), socket = bin ? null : tailscaledSocket()) {
|
|
20352
|
+
if (!bin && !socket) return { installed: false }
|
|
20353
|
+
const run = args => exec(bin, args, { encoding: 'utf8', timeout: 8000 }).then(r => r.stdout, () => '');
|
|
20354
|
+
const status = tailnetStatus(bin ? await run(['status', '--json']) : await localApi(socket, 'status?peers=false'));
|
|
20355
|
+
const serve = !status.name ? '' : bin ? await run(['serve', 'status', '--json']) : await localApi(socket, 'serve-config');
|
|
20356
|
+
const phone = phoneAddress(status.name, port, serve);
|
|
20357
|
+
// Publishing on wherdr's port would replace whatever else is served there.
|
|
20358
|
+
const taken = Boolean(phone && !phone.served && servedPorts(serve).includes(String(port)));
|
|
20359
|
+
return { installed: true, bin, socket, ...status, phone, taken, served: servedAddresses(status.name, port, serve) }
|
|
20360
|
+
}
|
|
20361
|
+
|
|
20362
|
+
// The tailnet's DNS suffix (tailnet.ts.net) from /etc/resolv.conf's search
|
|
20363
|
+
// line: a Docker container copies it from the host when MagicDNS is on.
|
|
20364
|
+
function tailnetDomain(resolvConf) {
|
|
20365
|
+
for (const line of String(resolvConf || '').split('\n')) {
|
|
20366
|
+
const m = /^\s*search\s+(.+)$/.exec(line);
|
|
20367
|
+
const hit = m?.[1].split(/\s+/).find(d => /^[a-z0-9-]+\.ts\.net$/i.test(d));
|
|
20368
|
+
if (hit) return hit.toLowerCase()
|
|
20369
|
+
}
|
|
20370
|
+
return null
|
|
20371
|
+
}
|
|
20372
|
+
|
|
20373
|
+
// Tailscale's DNS (MagicDNS): a Docker container does not use it, but reaches it.
|
|
20374
|
+
const MAGIC_DNS = '100.100.100.100';
|
|
20375
|
+
|
|
20376
|
+
// The system's lookup, then MagicDNS for *.ts.net names.
|
|
20377
|
+
function lookup(host, opts, cb) {
|
|
20378
|
+
dns.lookup(host, opts, (err, address, family) => {
|
|
20379
|
+
if (!err || !/\.ts\.net$/i.test(host)) return cb(err, address, family)
|
|
20380
|
+
const resolver = new dns.Resolver({ timeout: 3000, tries: 1 });
|
|
20381
|
+
resolver.setServers([MAGIC_DNS]);
|
|
20382
|
+
resolver.resolve4(host, (e2, list) => {
|
|
20383
|
+
if (e2 || !list?.length) return cb(err)
|
|
20384
|
+
if (opts?.all) cb(null, list.map(a => ({ address: a, family: 4 })));
|
|
20385
|
+
else cb(null, list[0], 4);
|
|
20386
|
+
});
|
|
20387
|
+
});
|
|
20388
|
+
}
|
|
20389
|
+
|
|
20390
|
+
// Why a request failed: 'dns' (name not found), 'refused', 'timeout', 'cert'
|
|
20391
|
+
// (invalid certificate), 'tls' (no HTTPS on that port), or 'network'.
|
|
20392
|
+
function failureCause(err) {
|
|
20393
|
+
const code = String(err?.code || '');
|
|
20394
|
+
const msg = String(err?.message || '');
|
|
20395
|
+
if (code === 'ENOTFOUND' || code === 'EAI_AGAIN' || code === 'EAI_NONAME') return 'dns'
|
|
20396
|
+
if (code === 'ECONNREFUSED') return 'refused'
|
|
20397
|
+
if (code === 'ETIMEDOUT' || msg === 'timeout') return 'timeout'
|
|
20398
|
+
if (/CERT|SELF_SIGNED|UNABLE_TO_VERIFY|ALTNAME/.test(code)) return 'cert'
|
|
20399
|
+
if (code === 'EPROTO' || /wrong version number|ssl3_get_record|packet length/i.test(msg)) return 'tls'
|
|
20400
|
+
return 'network'
|
|
20401
|
+
}
|
|
20402
|
+
|
|
20403
|
+
// GET of `route` on the address → { status, text }, or { cause } when it failed.
|
|
20404
|
+
function fetchText(url, route, timeoutMs) {
|
|
20405
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
20406
|
+
let u;
|
|
20407
|
+
try { u = new URL(route, url); } catch { return Promise.resolve({ cause: 'network' }) }
|
|
20408
|
+
// Accept JSON: a refused host then answers { code: 'host' }, not the HTML page.
|
|
20409
|
+
const req = https.get(u, { lookup, timeout: timeoutMs, headers: { accept: 'application/json' } }, (res) => {
|
|
20410
|
+
let text = '';
|
|
20411
|
+
res.setEncoding('utf8');
|
|
20412
|
+
res.on('data', (c) => { if (text.length < 65536) text += c; });
|
|
20413
|
+
res.on('end', () => resolve({ status: res.statusCode || 0, text }));
|
|
20414
|
+
res.on('error', e => resolve({ cause: failureCause(e) }));
|
|
20415
|
+
});
|
|
20416
|
+
req.on('timeout', () => req.destroy(new Error('timeout')));
|
|
20417
|
+
req.on('error', e => resolve({ cause: failureCause(e) }));
|
|
20418
|
+
return promise
|
|
20419
|
+
}
|
|
20420
|
+
|
|
20421
|
+
// What an answer of /api/health means: 'ok' (wherdr, and it accepts this
|
|
20422
|
+
// address), 'host' (wherdr's host check refused it), or null: not a wherdr
|
|
20423
|
+
// that has this route (versions up to 1.3.1, or another program).
|
|
20424
|
+
function healthReach(status, text) {
|
|
20425
|
+
if (status >= 200 && status < 300 && /"name"\s*:\s*"wherdr"/.test(text)) return 'ok'
|
|
20426
|
+
if (status === 403 && /"code"\s*:\s*"host"/.test(text)) return 'host'
|
|
20427
|
+
return null
|
|
20428
|
+
}
|
|
20429
|
+
|
|
20430
|
+
// reachable() with the details: { reach, cause?, status? }: `cause` when
|
|
20431
|
+
// unreachable (failureCause), `status` the HTTP code when something answered.
|
|
20432
|
+
// /api/health is asked first: it goes through wherdr's host check, which the
|
|
20433
|
+
// static manifest does not (it answers for any host name). The manifest only
|
|
20434
|
+
// recognizes an older wherdr without that route.
|
|
20435
|
+
async function probe(url, timeoutMs = 8000) {
|
|
20436
|
+
const health = await fetchText(url, 'api/health', timeoutMs);
|
|
20437
|
+
if (health.cause) return { reach: 'unreachable', cause: health.cause }
|
|
20438
|
+
const reach = healthReach(health.status, health.text);
|
|
20439
|
+
if (reach) return { reach, status: health.status }
|
|
20440
|
+
const manifest = await fetchText(url, 'manifest.webmanifest', timeoutMs);
|
|
20441
|
+
if (manifest.cause) return { reach: 'unreachable', cause: manifest.cause }
|
|
20442
|
+
const ok = manifest.status >= 200 && manifest.status < 300 && manifest.text.includes('wherdr');
|
|
20443
|
+
return { reach: ok ? 'ok' : 'other', status: ok ? manifest.status : health.status }
|
|
20444
|
+
}
|
|
20445
|
+
|
|
20446
|
+
// Runs `tailscale <args>` without a terminal → { code, output }. On a tailnet
|
|
20447
|
+
// without HTTPS, `tailscale serve` prints a link and waits: the timeout ends it.
|
|
20448
|
+
function runTailscale(bin, args, timeoutMs = 30_000) {
|
|
20449
|
+
return exec(bin, args, { encoding: 'utf8', timeout: timeoutMs }).then(
|
|
20450
|
+
r => ({ code: 0, output: `${r.stdout}${r.stderr}` }),
|
|
20451
|
+
e => ({ code: typeof e.code === 'number' ? e.code : -1, output: `${e.stdout || ''}${e.stderr || ''}${e.message || ''}` }),
|
|
20452
|
+
)
|
|
20453
|
+
}
|
|
20454
|
+
|
|
20455
|
+
const PHONE_GRACE_MS = 9e4;
|
|
20456
|
+
const TRANSIENT = { timeout: true, tls: true, refused: true, cert: true, network: true };
|
|
20457
|
+
function phoneReach(probe, since, now = Date.now()) {
|
|
20458
|
+
if (probe.reach !== "unreachable") return probe.reach;
|
|
20459
|
+
return probe.cause && TRANSIENT[probe.cause] && now - since < PHONE_GRACE_MS ? "pending" : "unreachable";
|
|
20460
|
+
}
|
|
20461
|
+
|
|
20462
|
+
function qrSvg(text) {
|
|
20463
|
+
const qr = new QRCode(-1, QRErrorCorrectLevel.M);
|
|
20464
|
+
qr.addData(text);
|
|
20465
|
+
qr.make();
|
|
20466
|
+
const n = qr.getModuleCount();
|
|
20467
|
+
let d = "";
|
|
20468
|
+
for (let r = 0; r < n; r++) {
|
|
20469
|
+
for (let col = 0; col < n; col++) if (qr.isDark(r, col)) d += `M${col + 2} ${r + 2}h1v1h-1z`;
|
|
20470
|
+
}
|
|
20471
|
+
return { size: n + 4, path: d };
|
|
20472
|
+
}
|
|
20473
|
+
|
|
20474
|
+
function hostname(value) {
|
|
20475
|
+
try {
|
|
20476
|
+
if (!value || /[\s/@?#]/.test(value)) return null;
|
|
20477
|
+
const url = new URL(`http://${value}`);
|
|
20478
|
+
if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) return null;
|
|
20479
|
+
return url.hostname.replace(/^\[|\]$/g, "").toLowerCase().replace(/\.$/, "");
|
|
19214
20480
|
} catch {
|
|
20481
|
+
return null;
|
|
19215
20482
|
}
|
|
19216
20483
|
}
|
|
19217
|
-
|
|
19218
|
-
|
|
19219
|
-
|
|
19220
|
-
|
|
19221
|
-
|
|
19222
|
-
|
|
19223
|
-
|
|
19224
|
-
|
|
19225
|
-
cleanAttachments();
|
|
19226
|
-
const purge = setInterval(() => {
|
|
19227
|
-
cleanUploads();
|
|
19228
|
-
cleanAttachments();
|
|
19229
|
-
}, 6 * 3600 * 1e3);
|
|
19230
|
-
log(`herdr-web \u2014 socket ${HERDR_SOCK}${HERDR_SESSION ? ` (session ${HERDR_SESSION})` : ""}`);
|
|
19231
|
-
nitroApp.hooks.hook("close", () => {
|
|
19232
|
-
stopPolling();
|
|
19233
|
-
stopNotices();
|
|
19234
|
-
stopMachines();
|
|
19235
|
-
clearInterval(purge);
|
|
20484
|
+
let servedHosts = [];
|
|
20485
|
+
function setServedHosts(urls) {
|
|
20486
|
+
servedHosts = urls.flatMap((url) => {
|
|
20487
|
+
try {
|
|
20488
|
+
return [new URL(url).hostname.toLowerCase()];
|
|
20489
|
+
} catch {
|
|
20490
|
+
return [];
|
|
20491
|
+
}
|
|
19236
20492
|
});
|
|
19237
|
-
}
|
|
20493
|
+
}
|
|
20494
|
+
function allowedHosts(appUrl2 = process.env.APP_URL || "", extra = process.env.HERDR_WEB_ALLOWED_HOSTS || "", interfaces = os.networkInterfaces(), served = servedHosts) {
|
|
20495
|
+
const names = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", os.hostname().toLowerCase(), ...served]);
|
|
20496
|
+
try {
|
|
20497
|
+
if (appUrl2) names.add(new URL(appUrl2).hostname.toLowerCase().replace(/^\[|\]$/g, ""));
|
|
20498
|
+
} catch {
|
|
20499
|
+
}
|
|
20500
|
+
for (const addresses of Object.values(interfaces)) {
|
|
20501
|
+
for (const address of addresses || []) names.add(address.address.toLowerCase().replace(/%.+$/, ""));
|
|
20502
|
+
}
|
|
20503
|
+
for (const item of extra.split(",")) {
|
|
20504
|
+
const name = hostname(item.trim());
|
|
20505
|
+
if (name) names.add(name);
|
|
20506
|
+
}
|
|
20507
|
+
return names;
|
|
20508
|
+
}
|
|
20509
|
+
function hostAllowed(host, names = allowedHosts()) {
|
|
20510
|
+
const name = hostname(host || "");
|
|
20511
|
+
return Boolean(name && names.has(name));
|
|
20512
|
+
}
|
|
20513
|
+
function hostRefusalIsHtml(path, accept) {
|
|
20514
|
+
return !path.startsWith("/api/") && /\btext\/html\b/i.test(Array.isArray(accept) ? accept.join(",") : accept || "");
|
|
20515
|
+
}
|
|
20516
|
+
const CLI_DEFAULT_PORT = "7683";
|
|
20517
|
+
function phoneFixCommand(install = process.env.WHERDR_INSTALL || "", port = process.env.NITRO_PORT || process.env.PORT || "") {
|
|
20518
|
+
if (install === "docker" || install === "docker-build" || install === "plugin") return "curl -fsSL https://wherdr.dev/install | sh";
|
|
20519
|
+
const command = install === "brew" || install === "npm-global" ? "wherdr phone" : "npx wherdr phone";
|
|
20520
|
+
return /^\d+$/.test(port) && port !== CLI_DEFAULT_PORT ? `${command} --port ${port}` : command;
|
|
20521
|
+
}
|
|
20522
|
+
function tailnetName(host) {
|
|
20523
|
+
return /\.ts\.net$/.test(hostname(host || "") || "");
|
|
20524
|
+
}
|
|
20525
|
+
function hostRefusedPage(install, host, port) {
|
|
20526
|
+
var _a;
|
|
20527
|
+
const kind = (_a = process.env.WHERDR_INSTALL) != null ? _a : "";
|
|
20528
|
+
const command = phoneFixCommand(kind, port);
|
|
20529
|
+
const docker = kind === "docker" || kind === "docker-build";
|
|
20530
|
+
const where = docker ? "in the container's environment" : "in <code>~/wherdr/wherdr.env</code>";
|
|
20531
|
+
const whereFr = docker ? "dans l\u2019environnement du conteneur" : "dans <code>~/wherdr/wherdr.env</code>";
|
|
20532
|
+
const other = !tailnetName(host);
|
|
20533
|
+
const serve = other ? "If you reach wherdr through <code>tailscale serve</code>, wherdr enables that address by itself." : "wherdr enables by itself the address <code>tailscale serve</code> publishes for it.";
|
|
20534
|
+
const serveFr = other ? "Si tu passes par <code>tailscale serve</code>, wherdr active cette adresse tout seul." : "wherdr active tout seul l\u2019adresse que <code>tailscale serve</code> publie pour lui.";
|
|
20535
|
+
return `<!doctype html>
|
|
20536
|
+
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
|
20537
|
+
<meta name="robots" content="noindex"><title>wherdr \xB7 address not enabled</title>
|
|
20538
|
+
<style>
|
|
20539
|
+
:root{color-scheme:dark}*{box-sizing:border-box}
|
|
20540
|
+
body{margin:0;min-height:100vh;display:grid;place-items:center;background:#0d0f12;color:#e8eaed;font:15px/1.5 Inter,system-ui,-apple-system,sans-serif;padding:24px max(16px,env(safe-area-inset-right)) 24px max(16px,env(safe-area-inset-left))}
|
|
20541
|
+
main{max-width:480px;border:1px solid #3a3f47;padding:24px}
|
|
20542
|
+
.tag{font:11px/1 "JetBrains Mono",ui-monospace,monospace;letter-spacing:.08em;text-transform:uppercase;color:#9aa0a8;margin:0 0 16px}
|
|
20543
|
+
h1{font:600 20px/1.25 Archivo,system-ui,sans-serif;margin:0 0 8px}
|
|
20544
|
+
p{margin:0 0 12px}pre+p{margin-top:16px}p:last-child{margin-bottom:0}hr{border:0;border-top:1px solid #3a3f47;margin:20px 0}
|
|
20545
|
+
code{font:13px/1.45 "JetBrains Mono",ui-monospace,monospace;overflow-wrap:anywhere}
|
|
20546
|
+
pre{margin:0;padding:10px 12px;border:1px solid #3a3f47;background:#15181d;white-space:pre-wrap;overflow-wrap:anywhere;user-select:all;-webkit-user-select:all}
|
|
20547
|
+
</style></head><body><main>
|
|
20548
|
+
<p class="tag">wherdr \xB7 403</p>
|
|
20549
|
+
<h1>This address isn't enabled yet.</h1>
|
|
20550
|
+
<p>${serve} On the machine that runs wherdr, run this, then reload:</p>
|
|
20551
|
+
<pre><code>${command}</code></pre>${other ? `
|
|
20552
|
+
<p>For any other private name or IP address, add it to <code>HERDR_WEB_ALLOWED_HOSTS</code> (comma-separated) ${where}, then restart wherdr.</p>` : ""}
|
|
20553
|
+
<hr>
|
|
20554
|
+
<h1 lang="fr">Cette adresse n\u2019est pas encore activ\xE9e.</h1>
|
|
20555
|
+
<p lang="fr">${serveFr} Sur la machine qui fait tourner wherdr, lance ceci, puis recharge :</p>
|
|
20556
|
+
<pre><code>${command}</code></pre>${other ? `
|
|
20557
|
+
<p lang="fr">Pour tout autre nom priv\xE9 ou adresse IP, ajoute-le \xE0 <code>HERDR_WEB_ALLOWED_HOSTS</code> (s\xE9par\xE9s par des virgules) ${whereFr}, puis red\xE9marre wherdr.</p>` : ""}
|
|
20558
|
+
</main></body></html>
|
|
20559
|
+
`;
|
|
20560
|
+
}
|
|
20561
|
+
function crossSiteRequest(headers) {
|
|
20562
|
+
const site = headers["sec-fetch-site"];
|
|
20563
|
+
return typeof site === "string" && site !== "same-origin" && site !== "none";
|
|
20564
|
+
}
|
|
19238
20565
|
|
|
19239
|
-
const
|
|
19240
|
-
|
|
19241
|
-
|
|
19242
|
-
|
|
20566
|
+
const MAX_SESSION_DAYS = [1, 7, 30, 90, 365];
|
|
20567
|
+
const DEFAULT_MAX_SESSION_DAYS = 7;
|
|
20568
|
+
const DAY_MS = 24 * 3600 * 1e3;
|
|
20569
|
+
function isMaxSessionDays(v) {
|
|
20570
|
+
return MAX_SESSION_DAYS.includes(v);
|
|
20571
|
+
}
|
|
19243
20572
|
|
|
19244
|
-
|
|
19245
|
-
|
|
19246
|
-
|
|
19247
|
-
|
|
19248
|
-
|
|
19249
|
-
|
|
19250
|
-
|
|
19251
|
-
|
|
19252
|
-
|
|
19253
|
-
|
|
19254
|
-
|
|
19255
|
-
|
|
19256
|
-
|
|
19257
|
-
|
|
19258
|
-
|
|
19259
|
-
|
|
19260
|
-
|
|
19261
|
-
|
|
19262
|
-
|
|
19263
|
-
|
|
19264
|
-
"
|
|
19265
|
-
}
|
|
19266
|
-
|
|
19267
|
-
|
|
19268
|
-
|
|
19269
|
-
"
|
|
19270
|
-
|
|
19271
|
-
|
|
19272
|
-
|
|
19273
|
-
|
|
19274
|
-
|
|
19275
|
-
|
|
19276
|
-
|
|
19277
|
-
|
|
19278
|
-
|
|
19279
|
-
|
|
19280
|
-
|
|
19281
|
-
|
|
19282
|
-
|
|
19283
|
-
|
|
19284
|
-
|
|
19285
|
-
"
|
|
19286
|
-
|
|
19287
|
-
|
|
19288
|
-
|
|
19289
|
-
|
|
19290
|
-
|
|
19291
|
-
|
|
19292
|
-
|
|
19293
|
-
|
|
19294
|
-
|
|
19295
|
-
|
|
19296
|
-
|
|
19297
|
-
|
|
19298
|
-
"
|
|
19299
|
-
|
|
19300
|
-
|
|
19301
|
-
|
|
19302
|
-
|
|
19303
|
-
|
|
19304
|
-
|
|
19305
|
-
|
|
19306
|
-
|
|
19307
|
-
|
|
19308
|
-
|
|
19309
|
-
|
|
19310
|
-
|
|
19311
|
-
|
|
19312
|
-
"
|
|
19313
|
-
|
|
19314
|
-
|
|
19315
|
-
|
|
19316
|
-
|
|
19317
|
-
|
|
19318
|
-
|
|
19319
|
-
|
|
19320
|
-
|
|
19321
|
-
}
|
|
19322
|
-
"
|
|
19323
|
-
|
|
19324
|
-
|
|
19325
|
-
|
|
19326
|
-
|
|
19327
|
-
|
|
19328
|
-
}
|
|
19329
|
-
|
|
19330
|
-
|
|
19331
|
-
|
|
19332
|
-
|
|
19333
|
-
|
|
19334
|
-
|
|
19335
|
-
|
|
19336
|
-
|
|
19337
|
-
|
|
19338
|
-
|
|
19339
|
-
|
|
19340
|
-
|
|
19341
|
-
|
|
19342
|
-
|
|
19343
|
-
|
|
19344
|
-
|
|
19345
|
-
|
|
19346
|
-
|
|
19347
|
-
|
|
19348
|
-
|
|
19349
|
-
|
|
19350
|
-
|
|
19351
|
-
|
|
19352
|
-
|
|
19353
|
-
"
|
|
19354
|
-
|
|
19355
|
-
|
|
19356
|
-
}
|
|
19357
|
-
|
|
19358
|
-
|
|
19359
|
-
|
|
19360
|
-
|
|
19361
|
-
|
|
19362
|
-
|
|
19363
|
-
|
|
19364
|
-
|
|
19365
|
-
|
|
19366
|
-
|
|
19367
|
-
|
|
19368
|
-
|
|
19369
|
-
"
|
|
19370
|
-
}
|
|
19371
|
-
|
|
19372
|
-
"
|
|
19373
|
-
|
|
19374
|
-
|
|
19375
|
-
|
|
19376
|
-
|
|
19377
|
-
|
|
19378
|
-
|
|
19379
|
-
|
|
19380
|
-
|
|
19381
|
-
|
|
19382
|
-
"
|
|
19383
|
-
|
|
19384
|
-
|
|
19385
|
-
|
|
19386
|
-
|
|
19387
|
-
|
|
19388
|
-
|
|
19389
|
-
|
|
19390
|
-
|
|
19391
|
-
|
|
19392
|
-
|
|
19393
|
-
|
|
19394
|
-
|
|
19395
|
-
|
|
19396
|
-
|
|
19397
|
-
|
|
19398
|
-
|
|
19399
|
-
|
|
19400
|
-
|
|
19401
|
-
|
|
19402
|
-
|
|
19403
|
-
"
|
|
19404
|
-
|
|
19405
|
-
|
|
19406
|
-
|
|
19407
|
-
|
|
19408
|
-
"
|
|
19409
|
-
|
|
19410
|
-
|
|
19411
|
-
|
|
19412
|
-
|
|
19413
|
-
|
|
19414
|
-
|
|
19415
|
-
|
|
19416
|
-
|
|
19417
|
-
"
|
|
19418
|
-
|
|
19419
|
-
|
|
19420
|
-
|
|
19421
|
-
|
|
19422
|
-
|
|
19423
|
-
|
|
19424
|
-
"
|
|
19425
|
-
|
|
19426
|
-
|
|
19427
|
-
|
|
19428
|
-
"
|
|
19429
|
-
|
|
19430
|
-
|
|
19431
|
-
|
|
19432
|
-
"
|
|
19433
|
-
|
|
19434
|
-
|
|
19435
|
-
"
|
|
19436
|
-
|
|
19437
|
-
|
|
19438
|
-
|
|
19439
|
-
"
|
|
19440
|
-
|
|
19441
|
-
|
|
19442
|
-
|
|
19443
|
-
|
|
19444
|
-
|
|
19445
|
-
|
|
19446
|
-
|
|
19447
|
-
|
|
19448
|
-
|
|
19449
|
-
|
|
19450
|
-
|
|
19451
|
-
|
|
19452
|
-
|
|
19453
|
-
|
|
19454
|
-
|
|
19455
|
-
|
|
19456
|
-
|
|
19457
|
-
|
|
19458
|
-
|
|
19459
|
-
|
|
19460
|
-
|
|
19461
|
-
|
|
19462
|
-
|
|
19463
|
-
|
|
19464
|
-
|
|
19465
|
-
|
|
19466
|
-
|
|
19467
|
-
|
|
19468
|
-
|
|
19469
|
-
|
|
19470
|
-
|
|
19471
|
-
|
|
19472
|
-
|
|
19473
|
-
|
|
19474
|
-
|
|
20573
|
+
var __defProp = Object.defineProperty;
|
|
20574
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
20575
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
|
|
20576
|
+
const SESSION_MS = 12 * 3600 * 1e3;
|
|
20577
|
+
const RENEW_MS = 10 * 60 * 1e3;
|
|
20578
|
+
const CHALLENGE_MS = 5 * 60 * 1e3;
|
|
20579
|
+
const AUTH_COOKIE = "hw_session";
|
|
20580
|
+
const CHALLENGE_COOKIE = "hw_challenge";
|
|
20581
|
+
class AuthError extends Error {
|
|
20582
|
+
constructor(code, message) {
|
|
20583
|
+
super(message);
|
|
20584
|
+
__publicField(this, "code");
|
|
20585
|
+
this.code = code;
|
|
20586
|
+
}
|
|
20587
|
+
}
|
|
20588
|
+
function createAuth({ dataDir, log: log2 = () => {
|
|
20589
|
+
}, passkeyUser = "herdr" }) {
|
|
20590
|
+
const FILE = path$1.join(dataDir, "auth.json");
|
|
20591
|
+
let db = { secret: null, generation: 0, credentials: [] };
|
|
20592
|
+
try {
|
|
20593
|
+
db = { ...db, ...JSON.parse(fs.readFileSync(FILE, "utf8")) };
|
|
20594
|
+
} catch {
|
|
20595
|
+
}
|
|
20596
|
+
const save = () => {
|
|
20597
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
20598
|
+
fs.writeFileSync(FILE, JSON.stringify(db, null, 2) + "\n", { mode: 384 });
|
|
20599
|
+
};
|
|
20600
|
+
if (!db.secret) {
|
|
20601
|
+
db.secret = crypto.randomBytes(32).toString("hex");
|
|
20602
|
+
save();
|
|
20603
|
+
}
|
|
20604
|
+
const bootstrapToken = crypto.randomBytes(24).toString("base64url");
|
|
20605
|
+
if (!db.credentials.length) log2(`First passkey: bootstrap token ${bootstrapToken}`);
|
|
20606
|
+
const enabled = () => db.credentials.length > 0;
|
|
20607
|
+
const sign = (v) => crypto.createHmac("sha256", db.secret).update(v).digest("base64url");
|
|
20608
|
+
const safeEq = (a, b) => {
|
|
20609
|
+
const x = Buffer.from(String(a));
|
|
20610
|
+
const y = Buffer.from(String(b));
|
|
20611
|
+
return x.length === y.length && crypto.timingSafeEqual(x, y);
|
|
20612
|
+
};
|
|
20613
|
+
const readCookie = (req) => {
|
|
20614
|
+
const c = String(req.headers.cookie || "").split(/;\s*/).find((s) => s.startsWith(`${AUTH_COOKIE}=`));
|
|
20615
|
+
if (!c) return null;
|
|
20616
|
+
try {
|
|
20617
|
+
return decodeURIComponent(c.slice(AUTH_COOKIE.length + 1));
|
|
20618
|
+
} catch {
|
|
20619
|
+
return null;
|
|
20620
|
+
}
|
|
20621
|
+
};
|
|
20622
|
+
const challengeClient = (req) => {
|
|
20623
|
+
const c = String(req.headers.cookie || "").split(/;\s*/).find((s) => s.startsWith(`${CHALLENGE_COOKIE}=`));
|
|
20624
|
+
return (c == null ? void 0 : c.slice(CHALLENGE_COOKIE.length + 1)) || null;
|
|
20625
|
+
};
|
|
20626
|
+
const checkBootstrap = (body) => {
|
|
20627
|
+
if (!enabled() && !safeEq(String((body == null ? void 0 : body.bootstrapToken) || ""), bootstrapToken)) {
|
|
20628
|
+
throw new AuthError("bootstrap", "Invalid bootstrap token (see the server logs)");
|
|
20629
|
+
}
|
|
20630
|
+
};
|
|
20631
|
+
const maxSessionMs = () => {
|
|
20632
|
+
var _a;
|
|
20633
|
+
return ((_a = db.maxSessionDays) != null ? _a : DEFAULT_MAX_SESSION_DAYS) * DAY_MS;
|
|
20634
|
+
};
|
|
20635
|
+
function session(req, now = Date.now()) {
|
|
20636
|
+
const v = readCookie(req);
|
|
20637
|
+
if (!v) return null;
|
|
20638
|
+
const parts = v.split(".");
|
|
20639
|
+
if (parts.length !== 3 && parts.length !== 4) return null;
|
|
20640
|
+
const mac = parts.pop();
|
|
20641
|
+
if (!mac || parts[1] !== String(db.generation) || !safeEq(mac, sign(parts.join(".")))) return null;
|
|
20642
|
+
const exp = Number(parts[0]);
|
|
20643
|
+
const since = parts.length === 3 ? Number(parts[2]) : exp - SESSION_MS;
|
|
20644
|
+
if (!(exp >= now) || !Number.isFinite(since)) return null;
|
|
20645
|
+
if (now > since + maxSessionMs() + SESSION_MS) return null;
|
|
20646
|
+
return { exp, since };
|
|
20647
|
+
}
|
|
20648
|
+
function isUnlocked(req) {
|
|
20649
|
+
return !enabled() || session(req) !== null;
|
|
20650
|
+
}
|
|
20651
|
+
const secure = (req) => req.headers["x-forwarded-proto"] === "https" || String(req.headers.origin || "").startsWith("https://") ? "; Secure" : "";
|
|
20652
|
+
function sessionCookie(req, now = Date.now(), since = now) {
|
|
20653
|
+
const exp = now + SESSION_MS;
|
|
20654
|
+
const payload = `${exp}.${db.generation}.${since}`;
|
|
20655
|
+
return `${AUTH_COOKIE}=${payload}.${sign(payload)}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${SESSION_MS / 1e3}${secure(req)}`;
|
|
20656
|
+
}
|
|
20657
|
+
const clearCookie = (req) => `${AUTH_COOKIE}=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0${secure(req)}`;
|
|
20658
|
+
function renew(req, now = Date.now()) {
|
|
20659
|
+
if (!enabled()) return null;
|
|
20660
|
+
const s = session(req, now);
|
|
20661
|
+
if (s === null || s.exp - now > SESSION_MS - RENEW_MS) return null;
|
|
20662
|
+
return { cookie: sessionCookie(req, now, s.since), expiresAt: now + SESSION_MS };
|
|
20663
|
+
}
|
|
20664
|
+
function relying(req) {
|
|
20665
|
+
const origin = String(req.headers.origin || "");
|
|
20666
|
+
const { hostname } = new URL(origin);
|
|
20667
|
+
return { origin, rpID: hostname };
|
|
20668
|
+
}
|
|
20669
|
+
const challenges = /* @__PURE__ */ new Map();
|
|
20670
|
+
const challengeKey = (kind, client) => `${kind}:${client}`;
|
|
20671
|
+
const issueChallenge = (req, kind, value) => {
|
|
20672
|
+
const client = crypto.randomBytes(24).toString("base64url");
|
|
20673
|
+
for (const [key, challenge] of challenges) if (challenge.exp < Date.now()) challenges.delete(key);
|
|
20674
|
+
challenges.set(challengeKey(kind, client), { value, exp: Date.now() + CHALLENGE_MS });
|
|
20675
|
+
return `${CHALLENGE_COOKIE}=${client}; Path=/api/auth/; HttpOnly; SameSite=Strict; Max-Age=${CHALLENGE_MS / 1e3}${secure(req)}`;
|
|
20676
|
+
};
|
|
20677
|
+
const takeChallenge = (req, kind) => {
|
|
20678
|
+
const client = challengeClient(req);
|
|
20679
|
+
const key = challengeKey(kind, client || "");
|
|
20680
|
+
const c = challenges.get(key);
|
|
20681
|
+
challenges.delete(key);
|
|
20682
|
+
if (!c || c.exp < Date.now()) throw new AuthError("challenge", "Challenge expired, try again");
|
|
20683
|
+
return c.value;
|
|
20684
|
+
};
|
|
20685
|
+
async function registerOptions(req, body = {}) {
|
|
20686
|
+
if (enabled() && !isUnlocked(req)) throw new AuthError("locked", "Unlock first");
|
|
20687
|
+
checkBootstrap(body);
|
|
20688
|
+
const { rpID } = relying(req);
|
|
20689
|
+
const opts = await generateRegistrationOptions({
|
|
20690
|
+
rpName: "wherdr",
|
|
20691
|
+
rpID,
|
|
20692
|
+
userName: passkeyUser,
|
|
20693
|
+
userDisplayName: passkeyUser,
|
|
20694
|
+
attestationType: "none",
|
|
20695
|
+
excludeCredentials: db.credentials.map((c) => ({ id: c.id, transports: c.transports })),
|
|
20696
|
+
authenticatorSelection: { residentKey: "preferred", userVerification: "required" }
|
|
20697
|
+
});
|
|
20698
|
+
return { ...opts, __cookie: issueChallenge(req, "reg", opts.challenge) };
|
|
20699
|
+
}
|
|
20700
|
+
async function registerVerify(req, body) {
|
|
20701
|
+
if (enabled() && !isUnlocked(req)) throw new AuthError("locked", "Unlock first");
|
|
20702
|
+
checkBootstrap(body);
|
|
20703
|
+
const { origin, rpID } = relying(req);
|
|
20704
|
+
const v = await verifyRegistrationResponse({
|
|
20705
|
+
response: body.response,
|
|
20706
|
+
expectedChallenge: takeChallenge(req, "reg"),
|
|
20707
|
+
expectedOrigin: origin,
|
|
20708
|
+
expectedRPID: rpID,
|
|
20709
|
+
requireUserVerification: true
|
|
20710
|
+
});
|
|
20711
|
+
if (!v.verified || !v.registrationInfo) throw new AuthError("rejected", "Key rejected");
|
|
20712
|
+
const { credential } = v.registrationInfo;
|
|
20713
|
+
db.credentials.push({
|
|
20714
|
+
id: credential.id,
|
|
20715
|
+
publicKey: Buffer.from(credential.publicKey).toString("base64url"),
|
|
20716
|
+
counter: credential.counter,
|
|
20717
|
+
transports: credential.transports || [],
|
|
20718
|
+
name: String(body.name || "Device").slice(0, 40),
|
|
20719
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
20720
|
+
});
|
|
20721
|
+
save();
|
|
20722
|
+
log2(`lock: key registered (${db.credentials.length})`);
|
|
20723
|
+
return { ok: true, __cookie: sessionCookie(req) };
|
|
20724
|
+
}
|
|
20725
|
+
async function loginOptions(req) {
|
|
20726
|
+
const { rpID } = relying(req);
|
|
20727
|
+
const opts = await generateAuthenticationOptions({
|
|
20728
|
+
rpID,
|
|
20729
|
+
userVerification: "required",
|
|
20730
|
+
allowCredentials: db.credentials.map((c) => ({ id: c.id, transports: c.transports }))
|
|
20731
|
+
});
|
|
20732
|
+
return { ...opts, __cookie: issueChallenge(req, "auth", opts.challenge) };
|
|
20733
|
+
}
|
|
20734
|
+
async function loginVerify(req, body) {
|
|
20735
|
+
const { origin, rpID } = relying(req);
|
|
20736
|
+
const cred = db.credentials.find((c) => c.id === (body.response && body.response.id));
|
|
20737
|
+
if (!cred) throw new AuthError("unknown_key", "Key unknown on this server");
|
|
20738
|
+
const v = await verifyAuthenticationResponse({
|
|
20739
|
+
response: body.response,
|
|
20740
|
+
expectedChallenge: takeChallenge(req, "auth"),
|
|
20741
|
+
expectedOrigin: origin,
|
|
20742
|
+
expectedRPID: rpID,
|
|
20743
|
+
requireUserVerification: true,
|
|
20744
|
+
credential: { id: cred.id, publicKey: Buffer.from(cred.publicKey, "base64url"), counter: cred.counter, transports: cred.transports }
|
|
20745
|
+
});
|
|
20746
|
+
if (!v.verified) throw new AuthError("rejected", "Unlock rejected");
|
|
20747
|
+
cred.counter = v.authenticationInfo.newCounter;
|
|
20748
|
+
cred.lastUsed = (/* @__PURE__ */ new Date()).toISOString();
|
|
20749
|
+
save();
|
|
20750
|
+
return { ok: true, __cookie: sessionCookie(req) };
|
|
20751
|
+
}
|
|
20752
|
+
function disable(req) {
|
|
20753
|
+
if (!isUnlocked(req)) throw new AuthError("locked", "Unlock first");
|
|
20754
|
+
db.credentials = [];
|
|
20755
|
+
db.generation += 1;
|
|
20756
|
+
save();
|
|
20757
|
+
log2("lock disabled");
|
|
20758
|
+
return { ok: true, __cookie: clearCookie(req) };
|
|
20759
|
+
}
|
|
20760
|
+
function lockAll(req) {
|
|
20761
|
+
if (!enabled() || !isUnlocked(req)) throw new AuthError("locked", "Unlock first");
|
|
20762
|
+
db.generation += 1;
|
|
20763
|
+
save();
|
|
20764
|
+
log2("lock: all devices locked");
|
|
20765
|
+
return { ok: true, __cookie: clearCookie(req) };
|
|
20766
|
+
}
|
|
20767
|
+
function setMaxSession(req, days) {
|
|
20768
|
+
if (!enabled() || !isUnlocked(req)) throw new AuthError("locked", "Unlock first");
|
|
20769
|
+
if (!isMaxSessionDays(days)) throw new AuthError("invalid", "Invalid duration");
|
|
20770
|
+
db.maxSessionDays = days;
|
|
20771
|
+
save();
|
|
20772
|
+
return { ok: true, maxSessionDays: days };
|
|
20773
|
+
}
|
|
20774
|
+
function status(req, { resume = false, now = Date.now() } = {}) {
|
|
20775
|
+
var _a, _b;
|
|
20776
|
+
const devices = db.credentials.map((c) => ({ name: c.name, createdAt: c.createdAt, lastUsed: c.lastUsed || null }));
|
|
20777
|
+
if (!enabled()) return { enabled: false, unlocked: true, devices };
|
|
20778
|
+
const s = session(req, now);
|
|
20779
|
+
const deadline = s ? s.since + maxSessionMs() : 0;
|
|
20780
|
+
if (!s || resume && now >= deadline) {
|
|
20781
|
+
return { enabled: true, unlocked: false, expiresAt: null, now, devices, ...s ? { __cookie: clearCookie(req) } : {} };
|
|
20782
|
+
}
|
|
20783
|
+
const renewed = renew(req, now);
|
|
20784
|
+
return {
|
|
20785
|
+
enabled: true,
|
|
20786
|
+
unlocked: true,
|
|
20787
|
+
expiresAt: (_a = renewed == null ? void 0 : renewed.expiresAt) != null ? _a : s.exp,
|
|
20788
|
+
deadline,
|
|
20789
|
+
now,
|
|
20790
|
+
maxSessionDays: (_b = db.maxSessionDays) != null ? _b : DEFAULT_MAX_SESSION_DAYS,
|
|
20791
|
+
devices,
|
|
20792
|
+
...renewed ? { __cookie: renewed.cookie } : {}
|
|
20793
|
+
};
|
|
20794
|
+
}
|
|
20795
|
+
return {
|
|
20796
|
+
isUnlocked,
|
|
20797
|
+
status,
|
|
20798
|
+
renew,
|
|
20799
|
+
registerOptions,
|
|
20800
|
+
registerVerify,
|
|
20801
|
+
loginOptions,
|
|
20802
|
+
loginVerify,
|
|
20803
|
+
disable,
|
|
20804
|
+
lockAll,
|
|
20805
|
+
setMaxSession,
|
|
20806
|
+
lock: (req) => ({ ok: true, __cookie: clearCookie(req) }),
|
|
20807
|
+
// For tests.
|
|
20808
|
+
_sessionCookie: sessionCookie,
|
|
20809
|
+
_db: () => db,
|
|
20810
|
+
_bootstrapToken: () => bootstrapToken,
|
|
20811
|
+
_takeChallenge: takeChallenge
|
|
20812
|
+
};
|
|
20813
|
+
}
|
|
20814
|
+
|
|
20815
|
+
const auth = createAuth({ dataDir: DATA_DIR, log, passkeyUser: PASSKEY_USER });
|
|
20816
|
+
const reqOf = (event) => ({ headers: getRequestHeaders(event) });
|
|
20817
|
+
function sameOrigin(headers) {
|
|
20818
|
+
const o = headers.origin;
|
|
20819
|
+
if (!o) return false;
|
|
20820
|
+
try {
|
|
20821
|
+
return new URL(o).host === headers.host;
|
|
20822
|
+
} catch {
|
|
20823
|
+
return false;
|
|
20824
|
+
}
|
|
20825
|
+
}
|
|
20826
|
+
const BODY_MAX = 256 * 1024;
|
|
20827
|
+
function sendError(event, status, body) {
|
|
20828
|
+
setResponseStatus(event, status);
|
|
20829
|
+
setResponseHeader(event, "cache-control", "no-store");
|
|
20830
|
+
return body;
|
|
20831
|
+
}
|
|
20832
|
+
function defineApi(fn, opts = {}) {
|
|
20833
|
+
return defineEventHandler(async (event) => {
|
|
20834
|
+
const url = getRequestURL(event);
|
|
20835
|
+
try {
|
|
20836
|
+
let body = {};
|
|
20837
|
+
if (event.method === "POST") {
|
|
20838
|
+
const headers = getRequestHeaders(event);
|
|
20839
|
+
const ctype = String(headers["content-type"] || "");
|
|
20840
|
+
const typeOk = opts.raw ? opts.raw.test(ctype) : ctype.startsWith("application/json");
|
|
20841
|
+
if (!sameOrigin(headers) || !typeOk) return sendError(event, 403, { error: "Origin refused" });
|
|
20842
|
+
const max = opts.raw ? opts.max || 20 * 1024 * 1024 : BODY_MAX;
|
|
20843
|
+
const tooLarge = opts.raw ? opts.tooLarge || "Image too large (20 MB max)" : "Request too large";
|
|
20844
|
+
if (Number(headers["content-length"] || 0) > max) throw new HerdrError("too_large", tooLarge);
|
|
20845
|
+
const raw = await readRawBody(event, false);
|
|
20846
|
+
if (raw && raw.length > max) throw new HerdrError("too_large", tooLarge);
|
|
20847
|
+
if (opts.raw) body = { data: raw || Buffer.alloc(0), ctype };
|
|
20848
|
+
else {
|
|
20849
|
+
try {
|
|
20850
|
+
body = raw && raw.length ? JSON.parse(raw.toString("utf8")) : {};
|
|
20851
|
+
} catch {
|
|
20852
|
+
throw new HerdrError("bad_json", "Invalid JSON");
|
|
20853
|
+
}
|
|
20854
|
+
}
|
|
20855
|
+
}
|
|
20856
|
+
const out = await fn(event, body);
|
|
20857
|
+
setResponseHeader(event, "cache-control", "no-store");
|
|
20858
|
+
if (out && typeof out === "object" && "__cookie" in out) {
|
|
20859
|
+
const o = out;
|
|
20860
|
+
setResponseHeader(event, "set-cookie", String(o.__cookie));
|
|
20861
|
+
delete o.__cookie;
|
|
20862
|
+
}
|
|
20863
|
+
return out;
|
|
20864
|
+
} catch (e) {
|
|
20865
|
+
const err = e;
|
|
20866
|
+
const known = e instanceof HerdrError || url.pathname.startsWith("/api/auth/") && Boolean(err.code);
|
|
20867
|
+
const code = known ? err.code : "internal";
|
|
20868
|
+
if (!known) log("error", event.method, url.pathname, e);
|
|
20869
|
+
const status = err.code === "unreachable" ? 503 : err.code === "locked" ? 401 : 400;
|
|
20870
|
+
return sendError(event, status, { error: err.message, code });
|
|
20871
|
+
}
|
|
20872
|
+
});
|
|
20873
|
+
}
|
|
20874
|
+
function sendImage(event, img) {
|
|
20875
|
+
setResponseHeader(event, "content-type", img.type);
|
|
20876
|
+
setResponseHeader(event, "cache-control", "private, max-age=86400");
|
|
20877
|
+
setResponseHeader(event, "x-content-type-options", "nosniff");
|
|
20878
|
+
setResponseHeader(event, "content-security-policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox");
|
|
20879
|
+
return img.body;
|
|
20880
|
+
}
|
|
20881
|
+
|
|
20882
|
+
const PORT = String(process.env.NITRO_PORT || process.env.PORT || 3e3);
|
|
20883
|
+
const LOOPBACK = { "localhost": true, "127.0.0.1": true, "::1": true };
|
|
20884
|
+
function phoneAccess(event) {
|
|
20885
|
+
const req = reqOf(event);
|
|
20886
|
+
const lock = auth.status(req);
|
|
20887
|
+
if (lock.enabled && lock.unlocked) return true;
|
|
20888
|
+
const headers = getRequestHeaders(event);
|
|
20889
|
+
let host = "";
|
|
20890
|
+
try {
|
|
20891
|
+
host = new URL(`http://${headers.host || ""}`).hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
20892
|
+
} catch {
|
|
20893
|
+
return false;
|
|
20894
|
+
}
|
|
20895
|
+
const proxied = Object.keys(headers).some((k) => k === "x-forwarded-for" || k === "forwarded" || k.startsWith("tailscale-"));
|
|
20896
|
+
const peer = (event.node.req.socket.remoteAddress || "").replace(/^::ffff:/, "");
|
|
20897
|
+
const localPeer = /^127\./.test(peer) || peer === "::1" || IN_DOCKER && peer === dockerGateway();
|
|
20898
|
+
return Boolean(LOOPBACK[host]) && !proxied && localPeer;
|
|
20899
|
+
}
|
|
20900
|
+
function dockerGateway() {
|
|
20901
|
+
try {
|
|
20902
|
+
const row = fs.readFileSync("/proc/net/route", "utf8").split("\n").map((l) => l.trim().split(/\s+/)).find((f) => f[1] === "00000000");
|
|
20903
|
+
const hex = row == null ? void 0 : row[2];
|
|
20904
|
+
return hex ? [3, 2, 1, 0].map((i) => String(Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16))).join(".") : "";
|
|
20905
|
+
} catch {
|
|
20906
|
+
return "";
|
|
20907
|
+
}
|
|
20908
|
+
}
|
|
20909
|
+
let adopted = (() => {
|
|
20910
|
+
var _a;
|
|
20911
|
+
try {
|
|
20912
|
+
return ((_a = JSON.parse(fs.readFileSync(APP_URL_FILE, "utf8"))) == null ? void 0 : _a.source) === "serve";
|
|
20913
|
+
} catch {
|
|
20914
|
+
return false;
|
|
20915
|
+
}
|
|
20916
|
+
})();
|
|
20917
|
+
function saveAppUrl(url, fromServe = false) {
|
|
20918
|
+
if (process.env.APP_URL === url && (adopted || !fromServe)) return;
|
|
20919
|
+
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
20920
|
+
fs.writeFileSync(APP_URL_FILE, `${JSON.stringify(fromServe ? { url, source: "serve" } : { url }, null, 2)}
|
|
20921
|
+
`);
|
|
20922
|
+
adopted = fromServe;
|
|
20923
|
+
if (process.env.APP_URL === url) return;
|
|
20924
|
+
process.env.APP_URL = url;
|
|
20925
|
+
initVapid();
|
|
20926
|
+
log(`APP_URL set to the phone address ${url}`);
|
|
20927
|
+
}
|
|
20928
|
+
function forgetAppUrl() {
|
|
20929
|
+
fs.rmSync(APP_URL_FILE, { force: true });
|
|
20930
|
+
const was = process.env.APP_URL;
|
|
20931
|
+
if (BOOT_APP_URL) process.env.APP_URL = BOOT_APP_URL;
|
|
20932
|
+
else delete process.env.APP_URL;
|
|
20933
|
+
initVapid();
|
|
20934
|
+
log(`phone address ${was} is no longer published by tailscale serve: forgotten`);
|
|
20935
|
+
}
|
|
20936
|
+
function applyServed(served) {
|
|
20937
|
+
if (!served) return;
|
|
20938
|
+
setServedHosts(served);
|
|
20939
|
+
if (ENV_APP_URL) return;
|
|
20940
|
+
const current = process.env.APP_URL || "";
|
|
20941
|
+
if (served.length) saveAppUrl(served.includes(current) ? current : served[0], true);
|
|
20942
|
+
else if (adopted) {
|
|
20943
|
+
if (current !== BOOT_APP_URL) forgetAppUrl();
|
|
20944
|
+
adopted = false;
|
|
20945
|
+
}
|
|
20946
|
+
}
|
|
20947
|
+
const nameListeners = [];
|
|
20948
|
+
function onTailnetName(f) {
|
|
20949
|
+
nameListeners.push(f);
|
|
20950
|
+
}
|
|
20951
|
+
function tellName(name) {
|
|
20952
|
+
if (name) for (const f of nameListeners) f(name);
|
|
20953
|
+
}
|
|
20954
|
+
let adoptedAt = 0;
|
|
20955
|
+
let adopting = null;
|
|
20956
|
+
function adoptServed(maxAgeMs = 0) {
|
|
20957
|
+
if (adopting) return adopting;
|
|
20958
|
+
if (maxAgeMs && Date.now() - adoptedAt < maxAgeMs) return Promise.resolve();
|
|
20959
|
+
const bin = tailscaleBin();
|
|
20960
|
+
adopting = inspect(PORT, bin, bin ? null : tailscaledSocket()).then((net) => {
|
|
20961
|
+
applyServed(net.served);
|
|
20962
|
+
tellName(net.name);
|
|
20963
|
+
}, (e) => {
|
|
20964
|
+
log(`tailscale serve could not be read: ${(e == null ? void 0 : e.message) || e}`);
|
|
20965
|
+
}).finally(() => {
|
|
20966
|
+
adoptedAt = Date.now();
|
|
20967
|
+
adopting = null;
|
|
20968
|
+
});
|
|
20969
|
+
return adopting;
|
|
20970
|
+
}
|
|
20971
|
+
let watch = null;
|
|
20972
|
+
function startTailnetWatch() {
|
|
20973
|
+
void adoptServed();
|
|
20974
|
+
watch = setInterval(() => {
|
|
20975
|
+
void adoptServed();
|
|
20976
|
+
}, TAILNET_REFRESH_MS);
|
|
20977
|
+
}
|
|
20978
|
+
function stopTailnetWatch() {
|
|
20979
|
+
if (watch) clearInterval(watch);
|
|
20980
|
+
watch = null;
|
|
20981
|
+
}
|
|
20982
|
+
let typed = "";
|
|
20983
|
+
const waitSince = /* @__PURE__ */ new Map();
|
|
20984
|
+
async function phoneStatus() {
|
|
20985
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
20986
|
+
const bin = tailscaleBin();
|
|
20987
|
+
const socket = bin ? null : tailscaledSocket();
|
|
20988
|
+
const out = {
|
|
20989
|
+
mode: bin ? "native" : IN_DOCKER ? "docker" : "missing",
|
|
20990
|
+
platform: process.platform,
|
|
20991
|
+
port: PORT,
|
|
20992
|
+
connected: false,
|
|
20993
|
+
https: false,
|
|
20994
|
+
url: null,
|
|
20995
|
+
served: false,
|
|
20996
|
+
funnel: false,
|
|
20997
|
+
taken: false,
|
|
20998
|
+
suggested: null,
|
|
20999
|
+
command: publishCommand(PORT),
|
|
21000
|
+
reach: null,
|
|
21001
|
+
reachCause: null,
|
|
21002
|
+
reachStatus: null,
|
|
21003
|
+
checkedAt: null,
|
|
21004
|
+
appUrl: "",
|
|
21005
|
+
appUrlFromEnv: Boolean(ENV_APP_URL),
|
|
21006
|
+
qr: null
|
|
21007
|
+
};
|
|
21008
|
+
if (bin || socket) {
|
|
21009
|
+
const net = await inspect(PORT, bin, socket);
|
|
21010
|
+
applyServed(net.served);
|
|
21011
|
+
tellName(net.name);
|
|
21012
|
+
out.connected = Boolean(net.connected && net.name);
|
|
21013
|
+
out.https = Boolean(net.https);
|
|
21014
|
+
out.served = Boolean((_a = net.phone) == null ? void 0 : _a.served);
|
|
21015
|
+
out.funnel = Boolean((_b = net.phone) == null ? void 0 : _b.funnel);
|
|
21016
|
+
out.taken = Boolean(net.taken);
|
|
21017
|
+
out.suggested = (_d = (_c = net.phone) == null ? void 0 : _c.url) != null ? _d : null;
|
|
21018
|
+
out.url = out.served ? net.phone.url : null;
|
|
21019
|
+
}
|
|
21020
|
+
if (out.mode === "docker") {
|
|
21021
|
+
if (!out.suggested) {
|
|
21022
|
+
let resolv = "";
|
|
21023
|
+
try {
|
|
21024
|
+
resolv = fs.readFileSync("/etc/resolv.conf", "utf8");
|
|
21025
|
+
} catch {
|
|
21026
|
+
}
|
|
21027
|
+
const domain = tailnetDomain(resolv);
|
|
21028
|
+
const machine = (process.env.HOST_LABEL || "").trim().toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
21029
|
+
out.suggested = domain && machine ? `https://${machine}.${domain}:${PORT}/` : null;
|
|
21030
|
+
}
|
|
21031
|
+
if (!out.url) out.url = typed || tailnetUrl(process.env.APP_URL);
|
|
21032
|
+
const httpsPort = httpsPortOf(out.url || out.suggested);
|
|
21033
|
+
if (httpsPort) out.command = publishCommand(PORT, httpsPort);
|
|
21034
|
+
}
|
|
21035
|
+
if (out.url) {
|
|
21036
|
+
let p = await probe(out.url, 5e3);
|
|
21037
|
+
if (!out.served && (p.reach === "ok" || p.reach === "host") && !ENV_APP_URL) {
|
|
21038
|
+
saveAppUrl(out.url);
|
|
21039
|
+
if (p.reach === "host") p = await probe(out.url, 5e3);
|
|
21040
|
+
}
|
|
21041
|
+
const since = (_e = waitSince.get(out.url)) != null ? _e : Date.now();
|
|
21042
|
+
if (p.reach === "ok") waitSince.delete(out.url);
|
|
21043
|
+
else waitSince.set(out.url, since);
|
|
21044
|
+
out.reach = phoneReach(p, since);
|
|
21045
|
+
out.reachCause = (_f = p.cause) != null ? _f : null;
|
|
21046
|
+
out.reachStatus = (_g = p.status) != null ? _g : null;
|
|
21047
|
+
out.checkedAt = Date.now();
|
|
21048
|
+
}
|
|
21049
|
+
out.appUrl = process.env.APP_URL || "";
|
|
21050
|
+
if (out.reach === "ok" && out.url) out.qr = qrSvg(out.url);
|
|
21051
|
+
return out;
|
|
21052
|
+
}
|
|
21053
|
+
function httpsPortOf(url) {
|
|
21054
|
+
try {
|
|
21055
|
+
return url ? new URL(url).port : "";
|
|
21056
|
+
} catch {
|
|
21057
|
+
return "";
|
|
21058
|
+
}
|
|
21059
|
+
}
|
|
21060
|
+
const fail = async (error, more = {}) => ({ ok: false, error, ...more, status: await phoneStatus() });
|
|
21061
|
+
async function phoneAction(body) {
|
|
21062
|
+
var _a;
|
|
21063
|
+
if (body.action === "address") {
|
|
21064
|
+
const url = tailnetUrl(body.url);
|
|
21065
|
+
if (!url) return fail("address");
|
|
21066
|
+
typed = url;
|
|
21067
|
+
return { ok: true, status: await phoneStatus() };
|
|
21068
|
+
}
|
|
21069
|
+
if (body.action !== "publish" && body.action !== "unpublish") return fail("failed", { detail: "Unknown action" });
|
|
21070
|
+
const bin = tailscaleBin();
|
|
21071
|
+
if (!bin) return fail("offline", { link: "https://tailscale.com/download" });
|
|
21072
|
+
const net = await inspect(PORT, bin);
|
|
21073
|
+
if (!net.connected || !net.phone) return fail("offline", { link: "https://tailscale.com/download" });
|
|
21074
|
+
let args;
|
|
21075
|
+
if (body.action === "publish") {
|
|
21076
|
+
if (net.phone.served) return { ok: true, status: await phoneStatus() };
|
|
21077
|
+
if (net.taken) return fail("taken");
|
|
21078
|
+
args = publishArgs(PORT);
|
|
21079
|
+
} else {
|
|
21080
|
+
if (!net.phone.served) return { ok: true, status: await phoneStatus() };
|
|
21081
|
+
args = unpublishArgs(net.phone.httpsPort);
|
|
21082
|
+
}
|
|
21083
|
+
const r = await runTailscale(bin, args);
|
|
21084
|
+
waitSince.clear();
|
|
21085
|
+
log(`tailscale ${args.join(" ")} \u2192 ${r.code}`);
|
|
21086
|
+
if (r.code !== 0) {
|
|
21087
|
+
const known = serveError(r.output);
|
|
21088
|
+
return fail((_a = known == null ? void 0 : known.code) != null ? _a : "failed", { link: known == null ? void 0 : known.link, detail: r.output.trim().slice(-600) });
|
|
21089
|
+
}
|
|
21090
|
+
return { ok: true, status: await phoneStatus() };
|
|
21091
|
+
}
|
|
21092
|
+
|
|
21093
|
+
const fsp = fs.promises;
|
|
21094
|
+
const REFUSED = {
|
|
21095
|
+
archive: "Archives cannot be read by the agent: extract the files first",
|
|
21096
|
+
video: "Videos cannot be read by the agent",
|
|
21097
|
+
audio: "Audio files cannot be read by the agent",
|
|
21098
|
+
office: "Office documents cannot be read by the agent: export to PDF or text first",
|
|
21099
|
+
executable: "Programs and binary files cannot be read by the agent",
|
|
21100
|
+
binary: "Binary files cannot be read by the agent",
|
|
21101
|
+
pdf: "This agent cannot read PDFs: only Claude Code can",
|
|
21102
|
+
too_large: "File too large for the agent",
|
|
21103
|
+
empty: "Empty file"
|
|
21104
|
+
};
|
|
21105
|
+
async function saveAttachment(data, originalName, paneId) {
|
|
21106
|
+
const pane = findPane(paneId);
|
|
21107
|
+
const m = machineOfPane(paneId);
|
|
21108
|
+
if (!pane || !m) throw new HerdrError("bad_pane", "Pane not found");
|
|
21109
|
+
const check = checkAttachment(pane.agent, { name: originalName, type: "", size: data.length }, data.subarray(0, SNIFF_BYTES));
|
|
21110
|
+
if (check.ok && check.kind === "image") throw new HerdrError("unsupported", "Images go through the photo upload");
|
|
21111
|
+
if (!check.ok) throw new HerdrError(check.reason === "too_large" ? "too_large" : "unsupported", REFUSED[check.reason]);
|
|
21112
|
+
const stored = storedAttachmentName(originalName, /* @__PURE__ */ new Date(), crypto.randomBytes(3).toString("hex"));
|
|
21113
|
+
if (!isStoredAttachmentName(stored)) throw new HerdrError("bad_name", "Invalid file name");
|
|
21114
|
+
let file;
|
|
21115
|
+
if (m instanceof RemoteMachine) {
|
|
21116
|
+
if (m.status !== "online") throw new HerdrError("unreachable", fmt("{machine} is unreachable", { machine: m.label }));
|
|
21117
|
+
file = await m.putUpload(stored, data, "file");
|
|
21118
|
+
} else {
|
|
21119
|
+
file = path$1.join(ATTACH_DIR, stored);
|
|
21120
|
+
if (path$1.dirname(file) !== path$1.resolve(ATTACH_DIR)) throw new HerdrError("bad_name", "Invalid file name");
|
|
21121
|
+
await fsp.mkdir(ATTACH_DIR, { recursive: true, mode: 448 });
|
|
21122
|
+
await fsp.writeFile(file, data, { mode: 384, flag: "wx" });
|
|
21123
|
+
}
|
|
21124
|
+
log(`file received ${stored} (${Math.round(data.length / 1024)} KB, ${check.kind})${m instanceof RemoteMachine ? ` -> ${m.label}` : ""}`);
|
|
21125
|
+
return { path: file, name: stored, kind: check.kind, size: data.length, ref: attachmentRef(pane.agent, check.kind, file) };
|
|
21126
|
+
}
|
|
21127
|
+
async function cleanAttachments() {
|
|
21128
|
+
try {
|
|
21129
|
+
for (const n of await fsp.readdir(ATTACH_DIR)) {
|
|
21130
|
+
const f = path$1.join(ATTACH_DIR, n);
|
|
21131
|
+
const st = await fsp.lstat(f);
|
|
21132
|
+
if (st.isFile() && Date.now() - st.mtimeMs > UPLOAD_TTL_MS) await fsp.unlink(f);
|
|
21133
|
+
}
|
|
21134
|
+
} catch {
|
|
21135
|
+
}
|
|
21136
|
+
}
|
|
21137
|
+
|
|
21138
|
+
const _mZt23IdRC2vLuqDtWT6mb7m9BgHXq3U5QrnQ7Z5Hc4c = defineNitroPlugin((nitroApp) => {
|
|
21139
|
+
initVapid();
|
|
21140
|
+
startMachines();
|
|
21141
|
+
startPolling();
|
|
21142
|
+
startThreadLimits();
|
|
21143
|
+
startNotices();
|
|
21144
|
+
startPaneSizes();
|
|
21145
|
+
onTailnetName(adoptTailnetName);
|
|
21146
|
+
startTailnetWatch();
|
|
21147
|
+
cleanUploads();
|
|
21148
|
+
cleanAttachments();
|
|
21149
|
+
purgePastes();
|
|
21150
|
+
const purge = setInterval(() => {
|
|
21151
|
+
cleanUploads();
|
|
21152
|
+
cleanAttachments();
|
|
21153
|
+
purgePastes();
|
|
21154
|
+
}, 6 * 3600 * 1e3);
|
|
21155
|
+
log(`herdr-web \u2014 socket ${HERDR_SOCK}${HERDR_SESSION ? ` (session ${HERDR_SESSION})` : ""}`);
|
|
21156
|
+
nitroApp.hooks.hook("close", () => {
|
|
21157
|
+
stopPolling();
|
|
21158
|
+
stopNotices();
|
|
21159
|
+
stopPaneSizes();
|
|
21160
|
+
stopTailnetWatch();
|
|
21161
|
+
stopMachines();
|
|
21162
|
+
clearInterval(purge);
|
|
21163
|
+
});
|
|
21164
|
+
});
|
|
21165
|
+
|
|
21166
|
+
const plugins = [
|
|
21167
|
+
_9as7ch5sSe9R7BrbrDE2GGZpuA6oP9uMAcL7iRtRgrw,
|
|
21168
|
+
_mZt23IdRC2vLuqDtWT6mb7m9BgHXq3U5QrnQ7Z5Hc4c
|
|
21169
|
+
];
|
|
21170
|
+
|
|
21171
|
+
const assets = {
|
|
21172
|
+
"/manifest-en.webmanifest": {
|
|
21173
|
+
"type": "application/manifest+json",
|
|
21174
|
+
"etag": "\"260-q1JnCK0/ecdKwgeTve6N4I6/mWI\"",
|
|
21175
|
+
"mtime": "2026-10-12T02:43:06.055Z",
|
|
21176
|
+
"size": 608,
|
|
21177
|
+
"path": "../public/manifest-en.webmanifest"
|
|
19475
21178
|
},
|
|
19476
|
-
"/
|
|
21179
|
+
"/manifest.webmanifest": {
|
|
21180
|
+
"type": "application/manifest+json",
|
|
21181
|
+
"etag": "\"26e-ykoYuj4V8la457yChW3oQTur8RU\"",
|
|
21182
|
+
"mtime": "2026-10-12T02:43:06.055Z",
|
|
21183
|
+
"size": 622,
|
|
21184
|
+
"path": "../public/manifest.webmanifest"
|
|
21185
|
+
},
|
|
21186
|
+
"/sw.js": {
|
|
21187
|
+
"type": "text/javascript; charset=utf-8",
|
|
21188
|
+
"etag": "\"1b75-QHSGlFHPf7wCrgMEz9tF99j/HQQ\"",
|
|
21189
|
+
"mtime": "2026-10-12T02:43:06.452Z",
|
|
21190
|
+
"size": 7029,
|
|
21191
|
+
"path": "../public/sw.js"
|
|
21192
|
+
},
|
|
21193
|
+
"/_nuxt/AgentView.DzFWBd2A.css": {
|
|
21194
|
+
"type": "text/css; charset=utf-8",
|
|
21195
|
+
"etag": "\"16c2-oz6EmjJDgIf6r38fC9pJHsZoJww\"",
|
|
21196
|
+
"mtime": "2026-10-12T02:43:06.045Z",
|
|
21197
|
+
"size": 5826,
|
|
21198
|
+
"path": "../public/_nuxt/AgentView.DzFWBd2A.css"
|
|
21199
|
+
},
|
|
21200
|
+
"/_nuxt/B4Yk2tB1.js": {
|
|
21201
|
+
"type": "text/javascript; charset=utf-8",
|
|
21202
|
+
"etag": "\"1176f-Op3w8R2G8EFcEwxudNsoKLq8Fl8\"",
|
|
21203
|
+
"mtime": "2026-10-12T02:43:06.045Z",
|
|
21204
|
+
"size": 71535,
|
|
21205
|
+
"path": "../public/_nuxt/B4Yk2tB1.js"
|
|
21206
|
+
},
|
|
21207
|
+
"/_nuxt/BBw_0a4M.js": {
|
|
21208
|
+
"type": "text/javascript; charset=utf-8",
|
|
21209
|
+
"etag": "\"17b75-3R2VJP1Oq7OKRMe/1iHRMGe6l3o\"",
|
|
21210
|
+
"mtime": "2026-10-12T02:43:06.045Z",
|
|
21211
|
+
"size": 97141,
|
|
21212
|
+
"path": "../public/_nuxt/BBw_0a4M.js"
|
|
21213
|
+
},
|
|
21214
|
+
"/_nuxt/BSy6OCIY.js": {
|
|
21215
|
+
"type": "text/javascript; charset=utf-8",
|
|
21216
|
+
"etag": "\"13f-1QDhbXhMYI+YPIbMXZLiVqG81SU\"",
|
|
21217
|
+
"mtime": "2026-10-12T02:43:06.045Z",
|
|
21218
|
+
"size": 319,
|
|
21219
|
+
"path": "../public/_nuxt/BSy6OCIY.js"
|
|
21220
|
+
},
|
|
21221
|
+
"/_nuxt/BX5PM2JQ.js": {
|
|
21222
|
+
"type": "text/javascript; charset=utf-8",
|
|
21223
|
+
"etag": "\"2905-uwamdHajAQRD2qCUB0gaMXs5+Hs\"",
|
|
21224
|
+
"mtime": "2026-10-12T02:43:06.045Z",
|
|
21225
|
+
"size": 10501,
|
|
21226
|
+
"path": "../public/_nuxt/BX5PM2JQ.js"
|
|
21227
|
+
},
|
|
21228
|
+
"/_nuxt/Bd17z0YL.js": {
|
|
21229
|
+
"type": "text/javascript; charset=utf-8",
|
|
21230
|
+
"etag": "\"1615-CPDn06/EO3pj6KudXz5uMZ0FkhI\"",
|
|
21231
|
+
"mtime": "2026-10-12T02:43:06.045Z",
|
|
21232
|
+
"size": 5653,
|
|
21233
|
+
"path": "../public/_nuxt/Bd17z0YL.js"
|
|
21234
|
+
},
|
|
21235
|
+
"/_nuxt/BgPDnf2S.js": {
|
|
21236
|
+
"type": "text/javascript; charset=utf-8",
|
|
21237
|
+
"etag": "\"14010-9tQrbfc74ogfocj+ZMU5WrK9qEM\"",
|
|
21238
|
+
"mtime": "2026-10-12T02:43:06.045Z",
|
|
21239
|
+
"size": 81936,
|
|
21240
|
+
"path": "../public/_nuxt/BgPDnf2S.js"
|
|
21241
|
+
},
|
|
21242
|
+
"/_nuxt/BkOxQzbS.js": {
|
|
21243
|
+
"type": "text/javascript; charset=utf-8",
|
|
21244
|
+
"etag": "\"91a1-Cc5iImaS93KRPJgLU4sZS6Cewjk\"",
|
|
21245
|
+
"mtime": "2026-10-12T02:43:06.045Z",
|
|
21246
|
+
"size": 37281,
|
|
21247
|
+
"path": "../public/_nuxt/BkOxQzbS.js"
|
|
21248
|
+
},
|
|
21249
|
+
"/_nuxt/CP8o925U.js": {
|
|
21250
|
+
"type": "text/javascript; charset=utf-8",
|
|
21251
|
+
"etag": "\"238-L5f/T9nwmMze/S7MlkAtOw2Agko\"",
|
|
21252
|
+
"mtime": "2026-10-12T02:43:06.045Z",
|
|
21253
|
+
"size": 568,
|
|
21254
|
+
"path": "../public/_nuxt/CP8o925U.js"
|
|
21255
|
+
},
|
|
21256
|
+
"/_nuxt/CWwyK2XK.js": {
|
|
21257
|
+
"type": "text/javascript; charset=utf-8",
|
|
21258
|
+
"etag": "\"7e5e-4BG2J9cvS3sx6WQ360LFqNaThyA\"",
|
|
21259
|
+
"mtime": "2026-10-12T02:43:06.045Z",
|
|
21260
|
+
"size": 32350,
|
|
21261
|
+
"path": "../public/_nuxt/CWwyK2XK.js"
|
|
21262
|
+
},
|
|
21263
|
+
"/_nuxt/CkbmecwS.js": {
|
|
21264
|
+
"type": "text/javascript; charset=utf-8",
|
|
21265
|
+
"etag": "\"f35-4fKoqAhIJbV/rJjyeg24lmHsFBg\"",
|
|
21266
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21267
|
+
"size": 3893,
|
|
21268
|
+
"path": "../public/_nuxt/CkbmecwS.js"
|
|
21269
|
+
},
|
|
21270
|
+
"/_nuxt/Ci8N4mWR.js": {
|
|
21271
|
+
"type": "text/javascript; charset=utf-8",
|
|
21272
|
+
"etag": "\"2426-fCrTYUyhtdHnTmu6ZJeEttvxVMo\"",
|
|
21273
|
+
"mtime": "2026-10-12T02:43:06.045Z",
|
|
21274
|
+
"size": 9254,
|
|
21275
|
+
"path": "../public/_nuxt/Ci8N4mWR.js"
|
|
21276
|
+
},
|
|
21277
|
+
"/_nuxt/Cr3SrGky.js": {
|
|
21278
|
+
"type": "text/javascript; charset=utf-8",
|
|
21279
|
+
"etag": "\"e91-L57+rnkmURnrnbQ2ojMRXvZSac4\"",
|
|
21280
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21281
|
+
"size": 3729,
|
|
21282
|
+
"path": "../public/_nuxt/Cr3SrGky.js"
|
|
21283
|
+
},
|
|
21284
|
+
"/_nuxt/DT8qZ0Wv.js": {
|
|
21285
|
+
"type": "text/javascript; charset=utf-8",
|
|
21286
|
+
"etag": "\"d34-pCkp/XrEPhSiS8s2YPYsI/DZ69I\"",
|
|
21287
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21288
|
+
"size": 3380,
|
|
21289
|
+
"path": "../public/_nuxt/DT8qZ0Wv.js"
|
|
21290
|
+
},
|
|
21291
|
+
"/_nuxt/DAg9fiVh.js": {
|
|
21292
|
+
"type": "text/javascript; charset=utf-8",
|
|
21293
|
+
"etag": "\"1b244-sPLH9gYhaVJ89ISuFCDsYuNF9Vs\"",
|
|
21294
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21295
|
+
"size": 111172,
|
|
21296
|
+
"path": "../public/_nuxt/DAg9fiVh.js"
|
|
21297
|
+
},
|
|
21298
|
+
"/_nuxt/DaaJIRB3.js": {
|
|
21299
|
+
"type": "text/javascript; charset=utf-8",
|
|
21300
|
+
"etag": "\"dd3-CbFoaT3rUHg6B+v1747rxTekQNQ\"",
|
|
21301
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21302
|
+
"size": 3539,
|
|
21303
|
+
"path": "../public/_nuxt/DaaJIRB3.js"
|
|
21304
|
+
},
|
|
21305
|
+
"/_nuxt/DcJi16XA.js": {
|
|
21306
|
+
"type": "text/javascript; charset=utf-8",
|
|
21307
|
+
"etag": "\"10a-fvmuAmno7y+Lve3V9/nvXuQHotk\"",
|
|
21308
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21309
|
+
"size": 266,
|
|
21310
|
+
"path": "../public/_nuxt/DcJi16XA.js"
|
|
21311
|
+
},
|
|
21312
|
+
"/_nuxt/SiL1FJ5s.js": {
|
|
21313
|
+
"type": "text/javascript; charset=utf-8",
|
|
21314
|
+
"etag": "\"176ad-/hlyeMkW67OV1A4G2nhIsYJX+vE\"",
|
|
21315
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21316
|
+
"size": 95917,
|
|
21317
|
+
"path": "../public/_nuxt/SiL1FJ5s.js"
|
|
21318
|
+
},
|
|
21319
|
+
"/_nuxt/VpjCgSXu.js": {
|
|
21320
|
+
"type": "text/javascript; charset=utf-8",
|
|
21321
|
+
"etag": "\"5b489-LoKHy0L+KBvwlf0JAvODFbMFL2o\"",
|
|
21322
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21323
|
+
"size": 373897,
|
|
21324
|
+
"path": "../public/_nuxt/VpjCgSXu.js"
|
|
21325
|
+
},
|
|
21326
|
+
"/_nuxt/archivo-latin-ext-wght-normal.C4zznr8T.woff2": {
|
|
19477
21327
|
"type": "font/woff2",
|
|
19478
|
-
"etag": "\"
|
|
19479
|
-
"mtime": "2026-10-
|
|
19480
|
-
"size":
|
|
19481
|
-
"path": "../public/_nuxt/
|
|
21328
|
+
"etag": "\"7f60-cwHN4xD2RhU0GVBfKzpzYZ7Al6U\"",
|
|
21329
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21330
|
+
"size": 32608,
|
|
21331
|
+
"path": "../public/_nuxt/archivo-latin-ext-wght-normal.C4zznr8T.woff2"
|
|
21332
|
+
},
|
|
21333
|
+
"/_nuxt/archivo-latin-wght-normal.E0tuGl4L.woff2": {
|
|
21334
|
+
"type": "font/woff2",
|
|
21335
|
+
"etag": "\"8870-ZUSfFgU3jYpWYbszVY1rxpynehc\"",
|
|
21336
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21337
|
+
"size": 34928,
|
|
21338
|
+
"path": "../public/_nuxt/archivo-latin-wght-normal.E0tuGl4L.woff2"
|
|
21339
|
+
},
|
|
21340
|
+
"/_nuxt/CKhgznq4.js": {
|
|
21341
|
+
"type": "text/javascript; charset=utf-8",
|
|
21342
|
+
"etag": "\"a7330-Q9O7D5hd37aKtqvr2vkiACwyZzM\"",
|
|
21343
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21344
|
+
"size": 684848,
|
|
21345
|
+
"path": "../public/_nuxt/CKhgznq4.js"
|
|
21346
|
+
},
|
|
21347
|
+
"/_nuxt/archivo-vietnamese-wght-normal.XAtsl5Q_.woff2": {
|
|
21348
|
+
"type": "font/woff2",
|
|
21349
|
+
"etag": "\"33b8-4zjQHgOy7y9EDRZs7TDAFDM4Ams\"",
|
|
21350
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21351
|
+
"size": 13240,
|
|
21352
|
+
"path": "../public/_nuxt/archivo-vietnamese-wght-normal.XAtsl5Q_.woff2"
|
|
21353
|
+
},
|
|
21354
|
+
"/_nuxt/dPxB2L94.js": {
|
|
21355
|
+
"type": "text/javascript; charset=utf-8",
|
|
21356
|
+
"etag": "\"471ad-4Pu+hTmytPNTQQ7hdBgH0D/hZVE\"",
|
|
21357
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21358
|
+
"size": 291245,
|
|
21359
|
+
"path": "../public/_nuxt/dPxB2L94.js"
|
|
21360
|
+
},
|
|
21361
|
+
"/_nuxt/error-500.Bwd7zAaE.css": {
|
|
21362
|
+
"type": "text/css; charset=utf-8",
|
|
21363
|
+
"etag": "\"772-nqXXACYtFiPK+D42BNu4uEECARA\"",
|
|
21364
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21365
|
+
"size": 1906,
|
|
21366
|
+
"path": "../public/_nuxt/error-500.Bwd7zAaE.css"
|
|
21367
|
+
},
|
|
21368
|
+
"/_nuxt/error-404.Bb87HomL.css": {
|
|
21369
|
+
"type": "text/css; charset=utf-8",
|
|
21370
|
+
"etag": "\"97d-4h9VfBznldxjqagfMldh1hDncR0\"",
|
|
21371
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21372
|
+
"size": 2429,
|
|
21373
|
+
"path": "../public/_nuxt/error-404.Bb87HomL.css"
|
|
21374
|
+
},
|
|
21375
|
+
"/_nuxt/hePW80VL.js": {
|
|
21376
|
+
"type": "text/javascript; charset=utf-8",
|
|
21377
|
+
"etag": "\"2cc-fA8td6k29UVF6JoPfhOPkceTK1M\"",
|
|
21378
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
21379
|
+
"size": 716,
|
|
21380
|
+
"path": "../public/_nuxt/hePW80VL.js"
|
|
21381
|
+
},
|
|
21382
|
+
"/_nuxt/inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2": {
|
|
21383
|
+
"type": "font/woff2",
|
|
21384
|
+
"etag": "\"6568-cF1iUGbboMFZ8TfnP5HiMgl9II0\"",
|
|
21385
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
21386
|
+
"size": 25960,
|
|
21387
|
+
"path": "../public/_nuxt/inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2"
|
|
21388
|
+
},
|
|
21389
|
+
"/_nuxt/inter-cyrillic-wght-normal.DqGufNeO.woff2": {
|
|
21390
|
+
"type": "font/woff2",
|
|
21391
|
+
"etag": "\"493c-n3Oy9D6jvzfMjpClqox+Zo7ERQQ\"",
|
|
21392
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
21393
|
+
"size": 18748,
|
|
21394
|
+
"path": "../public/_nuxt/inter-cyrillic-wght-normal.DqGufNeO.woff2"
|
|
21395
|
+
},
|
|
21396
|
+
"/_nuxt/inter-greek-ext-wght-normal.DlzME5K_.woff2": {
|
|
21397
|
+
"type": "font/woff2",
|
|
21398
|
+
"etag": "\"2be0-BP5iTzJeB8nLqYAgKpWNi5o1Zm8\"",
|
|
21399
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
21400
|
+
"size": 11232,
|
|
21401
|
+
"path": "../public/_nuxt/inter-greek-ext-wght-normal.DlzME5K_.woff2"
|
|
21402
|
+
},
|
|
21403
|
+
"/_nuxt/inter-greek-wght-normal.CkhJZR-_.woff2": {
|
|
21404
|
+
"type": "font/woff2",
|
|
21405
|
+
"etag": "\"4a34-xor/hj4YNqI52zFecXnUbzQ4Xs4\"",
|
|
21406
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
21407
|
+
"size": 18996,
|
|
21408
|
+
"path": "../public/_nuxt/inter-greek-wght-normal.CkhJZR-_.woff2"
|
|
21409
|
+
},
|
|
21410
|
+
"/_nuxt/inter-latin-ext-wght-normal.DO1Apj_S.woff2": {
|
|
21411
|
+
"type": "font/woff2",
|
|
21412
|
+
"etag": "\"14c4c-zz61D7IQFMB9QxHvTAOk/Vh4ibQ\"",
|
|
21413
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
21414
|
+
"size": 85068,
|
|
21415
|
+
"path": "../public/_nuxt/inter-latin-ext-wght-normal.DO1Apj_S.woff2"
|
|
19482
21416
|
},
|
|
19483
21417
|
"/_nuxt/inter-vietnamese-wght-normal.CBcvBZtf.woff2": {
|
|
19484
21418
|
"type": "font/woff2",
|
|
19485
21419
|
"etag": "\"280c-nBythjoDQ0+5wVAendJ6wU7Xz2M\"",
|
|
19486
|
-
"mtime": "2026-10-
|
|
21420
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
19487
21421
|
"size": 10252,
|
|
19488
21422
|
"path": "../public/_nuxt/inter-vietnamese-wght-normal.CBcvBZtf.woff2"
|
|
19489
21423
|
},
|
|
21424
|
+
"/_nuxt/inter-latin-wght-normal.Dx4kXJAl.woff2": {
|
|
21425
|
+
"type": "font/woff2",
|
|
21426
|
+
"etag": "\"bc80-8R1ym7Ck2DUNLqPQ/AYs9u8tUpg\"",
|
|
21427
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
21428
|
+
"size": 48256,
|
|
21429
|
+
"path": "../public/_nuxt/inter-latin-wght-normal.Dx4kXJAl.woff2"
|
|
21430
|
+
},
|
|
19490
21431
|
"/_nuxt/jetbrains-mono-cyrillic-wght-normal.D73BlboJ.woff2": {
|
|
19491
21432
|
"type": "font/woff2",
|
|
19492
21433
|
"etag": "\"2f4c-WiAGfn140d4QND3ayQWaCHF8rbE\"",
|
|
19493
|
-
"mtime": "2026-10-
|
|
21434
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
19494
21435
|
"size": 12108,
|
|
19495
21436
|
"path": "../public/_nuxt/jetbrains-mono-cyrillic-wght-normal.D73BlboJ.woff2"
|
|
19496
21437
|
},
|
|
21438
|
+
"/_nuxt/entry.CGWKmYU3.css": {
|
|
21439
|
+
"type": "text/css; charset=utf-8",
|
|
21440
|
+
"etag": "\"68db2-q6wQXFOalVzn1yh9EoP09Ru8lNs\"",
|
|
21441
|
+
"mtime": "2026-10-12T02:43:06.046Z",
|
|
21442
|
+
"size": 429490,
|
|
21443
|
+
"path": "../public/_nuxt/entry.CGWKmYU3.css"
|
|
21444
|
+
},
|
|
19497
21445
|
"/_nuxt/jetbrains-mono-greek-wght-normal.Bw9x6K1M.woff2": {
|
|
19498
21446
|
"type": "font/woff2",
|
|
19499
21447
|
"etag": "\"232c-Dnz9DhH4c266e6TziU1pxRkV6FY\"",
|
|
19500
|
-
"mtime": "2026-10-
|
|
21448
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
19501
21449
|
"size": 9004,
|
|
19502
21450
|
"path": "../public/_nuxt/jetbrains-mono-greek-wght-normal.Bw9x6K1M.woff2"
|
|
19503
21451
|
},
|
|
19504
21452
|
"/_nuxt/jetbrains-mono-latin-ext-wght-normal.DBQx-q_a.woff2": {
|
|
19505
21453
|
"type": "font/woff2",
|
|
19506
21454
|
"etag": "\"3b5c-HLF7Wvs2Z1IA1cPRs6jnor8OUQ4\"",
|
|
19507
|
-
"mtime": "2026-10-
|
|
21455
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
19508
21456
|
"size": 15196,
|
|
19509
21457
|
"path": "../public/_nuxt/jetbrains-mono-latin-ext-wght-normal.DBQx-q_a.woff2"
|
|
19510
21458
|
},
|
|
19511
21459
|
"/_nuxt/jetbrains-mono-latin-wght-normal.B9CIFXIH.woff2": {
|
|
19512
21460
|
"type": "font/woff2",
|
|
19513
21461
|
"etag": "\"9dd4-5yd+cUUhzrXxdMyYebUeD0qml1M\"",
|
|
19514
|
-
"mtime": "2026-10-
|
|
21462
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
19515
21463
|
"size": 40404,
|
|
19516
21464
|
"path": "../public/_nuxt/jetbrains-mono-latin-wght-normal.B9CIFXIH.woff2"
|
|
19517
21465
|
},
|
|
19518
21466
|
"/_nuxt/jetbrains-mono-vietnamese-wght-normal.Bt-aOZkq.woff2": {
|
|
19519
21467
|
"type": "font/woff2",
|
|
19520
21468
|
"etag": "\"1d50-/Re0MyD6BV8h81wBPVijGZH5GBs\"",
|
|
19521
|
-
"mtime": "2026-10-
|
|
21469
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
19522
21470
|
"size": 7504,
|
|
19523
21471
|
"path": "../public/_nuxt/jetbrains-mono-vietnamese-wght-normal.Bt-aOZkq.woff2"
|
|
19524
21472
|
},
|
|
19525
|
-
"/_nuxt/
|
|
19526
|
-
"type": "
|
|
19527
|
-
"etag": "\"
|
|
19528
|
-
"mtime": "2026-10-
|
|
19529
|
-
"size":
|
|
19530
|
-
"path": "../public/_nuxt/
|
|
19531
|
-
},
|
|
19532
|
-
"/_nuxt/entry.cKR16VqG.css": {
|
|
19533
|
-
"type": "text/css; charset=utf-8",
|
|
19534
|
-
"etag": "\"5f50e-lGjGgrLDJlABD7dGL+/Cu4UfQi8\"",
|
|
19535
|
-
"mtime": "2026-10-08T11:44:03.919Z",
|
|
19536
|
-
"size": 390414,
|
|
19537
|
-
"path": "../public/_nuxt/entry.cKR16VqG.css"
|
|
21473
|
+
"/_nuxt/wherdr-nerd-awesome.DQ3_iAfa.woff2": {
|
|
21474
|
+
"type": "font/woff2",
|
|
21475
|
+
"etag": "\"30178-02mpQKEDb74Ec8LtlhKceB1vHO8\"",
|
|
21476
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
21477
|
+
"size": 196984,
|
|
21478
|
+
"path": "../public/_nuxt/wherdr-nerd-awesome.DQ3_iAfa.woff2"
|
|
19538
21479
|
},
|
|
19539
21480
|
"/_nuxt/wherdr-nerd-codicons.CtEsfSAf.woff2": {
|
|
19540
21481
|
"type": "font/woff2",
|
|
19541
21482
|
"etag": "\"d9b0-kmO/x6KCTFRlVipOvl+5XcArGUw\"",
|
|
19542
|
-
"mtime": "2026-10-
|
|
21483
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
19543
21484
|
"size": 55728,
|
|
19544
21485
|
"path": "../public/_nuxt/wherdr-nerd-codicons.CtEsfSAf.woff2"
|
|
19545
21486
|
},
|
|
19546
|
-
"/_nuxt/wherdr-nerd-awesome.DQ3_iAfa.woff2": {
|
|
19547
|
-
"type": "font/woff2",
|
|
19548
|
-
"etag": "\"30178-02mpQKEDb74Ec8LtlhKceB1vHO8\"",
|
|
19549
|
-
"mtime": "2026-10-08T11:44:03.927Z",
|
|
19550
|
-
"size": 196984,
|
|
19551
|
-
"path": "../public/_nuxt/wherdr-nerd-awesome.DQ3_iAfa.woff2"
|
|
19552
|
-
},
|
|
19553
21487
|
"/_nuxt/wherdr-nerd-extension.Dai_ky39.woff2": {
|
|
19554
21488
|
"type": "font/woff2",
|
|
19555
21489
|
"etag": "\"f76c-CjERGtjtOrkpSXy7JyZNl1CAu0U\"",
|
|
19556
|
-
"mtime": "2026-10-
|
|
21490
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
19557
21491
|
"size": 63340,
|
|
19558
21492
|
"path": "../public/_nuxt/wherdr-nerd-extension.Dai_ky39.woff2"
|
|
19559
21493
|
},
|
|
19560
21494
|
"/_nuxt/wherdr-nerd-devicons.ZMSFX1sW.woff2": {
|
|
19561
21495
|
"type": "font/woff2",
|
|
19562
21496
|
"etag": "\"30f78-2RuGTMLnbTBdhlI9ExApyiUeD8c\"",
|
|
19563
|
-
"mtime": "2026-10-
|
|
21497
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
19564
21498
|
"size": 200568,
|
|
19565
21499
|
"path": "../public/_nuxt/wherdr-nerd-devicons.ZMSFX1sW.woff2"
|
|
19566
21500
|
},
|
|
21501
|
+
"/_nuxt/wherdr-nerd-material-1.BdvqkCqW.woff2": {
|
|
21502
|
+
"type": "font/woff2",
|
|
21503
|
+
"etag": "\"1e130-1GKpbVoJoRf3AxaKTZS8FjxkOd4\"",
|
|
21504
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
21505
|
+
"size": 123184,
|
|
21506
|
+
"path": "../public/_nuxt/wherdr-nerd-material-1.BdvqkCqW.woff2"
|
|
21507
|
+
},
|
|
19567
21508
|
"/_nuxt/wherdr-nerd-material-2.C_flbBWO.woff2": {
|
|
19568
21509
|
"type": "font/woff2",
|
|
19569
21510
|
"etag": "\"1f95c-q6dAP964z2dgEvWzAYjTAzBtzBc\"",
|
|
19570
|
-
"mtime": "2026-10-
|
|
21511
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
19571
21512
|
"size": 129372,
|
|
19572
21513
|
"path": "../public/_nuxt/wherdr-nerd-material-2.C_flbBWO.woff2"
|
|
19573
21514
|
},
|
|
19574
|
-
"/_nuxt/wherdr-nerd-material-3.8xz7iX-Y.woff2": {
|
|
19575
|
-
"type": "font/woff2",
|
|
19576
|
-
"etag": "\"2248c-3GBJ1KjkT+xYPhza9V/DKaILa7Y\"",
|
|
19577
|
-
"mtime": "2026-10-
|
|
19578
|
-
"size": 140428,
|
|
19579
|
-
"path": "../public/_nuxt/wherdr-nerd-material-3.8xz7iX-Y.woff2"
|
|
19580
|
-
},
|
|
19581
|
-
"/_nuxt/wherdr-nerd-material-1.BdvqkCqW.woff2": {
|
|
19582
|
-
"type": "font/woff2",
|
|
19583
|
-
"etag": "\"1e130-1GKpbVoJoRf3AxaKTZS8FjxkOd4\"",
|
|
19584
|
-
"mtime": "2026-10-08T11:44:03.927Z",
|
|
19585
|
-
"size": 123184,
|
|
19586
|
-
"path": "../public/_nuxt/wherdr-nerd-material-1.BdvqkCqW.woff2"
|
|
21515
|
+
"/_nuxt/wherdr-nerd-material-3.8xz7iX-Y.woff2": {
|
|
21516
|
+
"type": "font/woff2",
|
|
21517
|
+
"etag": "\"2248c-3GBJ1KjkT+xYPhza9V/DKaILa7Y\"",
|
|
21518
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
21519
|
+
"size": 140428,
|
|
21520
|
+
"path": "../public/_nuxt/wherdr-nerd-material-3.8xz7iX-Y.woff2"
|
|
19587
21521
|
},
|
|
19588
21522
|
"/_nuxt/wherdr-nerd-material-4.CO_PudDR.woff2": {
|
|
19589
21523
|
"type": "font/woff2",
|
|
19590
21524
|
"etag": "\"1fa80-YcJvpFD/M3vTC4y6SMmp7s8a9gw\"",
|
|
19591
|
-
"mtime": "2026-10-
|
|
21525
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
19592
21526
|
"size": 129664,
|
|
19593
21527
|
"path": "../public/_nuxt/wherdr-nerd-material-4.CO_PudDR.woff2"
|
|
19594
21528
|
},
|
|
19595
|
-
"/_nuxt/wherdr-nerd-powerline.B_kAMxXr.woff2": {
|
|
19596
|
-
"type": "font/woff2",
|
|
19597
|
-
"etag": "\"1bc0-iXm39wQwNbm2VjpVBOn72grfku0\"",
|
|
19598
|
-
"mtime": "2026-10-08T11:44:03.928Z",
|
|
19599
|
-
"size": 7104,
|
|
19600
|
-
"path": "../public/_nuxt/wherdr-nerd-powerline.B_kAMxXr.woff2"
|
|
19601
|
-
},
|
|
19602
21529
|
"/_nuxt/wherdr-nerd-octicons.Hl4VEx8m.woff2": {
|
|
19603
21530
|
"type": "font/woff2",
|
|
19604
21531
|
"etag": "\"f890-FkAsW3iqENK+lU5UlSze0yqnE2s\"",
|
|
19605
|
-
"mtime": "2026-10-
|
|
21532
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
19606
21533
|
"size": 63632,
|
|
19607
21534
|
"path": "../public/_nuxt/wherdr-nerd-octicons.Hl4VEx8m.woff2"
|
|
19608
21535
|
},
|
|
21536
|
+
"/_nuxt/wherdr-nerd-powerline.B_kAMxXr.woff2": {
|
|
21537
|
+
"type": "font/woff2",
|
|
21538
|
+
"etag": "\"1bc0-iXm39wQwNbm2VjpVBOn72grfku0\"",
|
|
21539
|
+
"mtime": "2026-10-12T02:43:06.047Z",
|
|
21540
|
+
"size": 7104,
|
|
21541
|
+
"path": "../public/_nuxt/wherdr-nerd-powerline.B_kAMxXr.woff2"
|
|
21542
|
+
},
|
|
19609
21543
|
"/_nuxt/wherdr-symbols.C1LsPKmg.woff2": {
|
|
19610
21544
|
"type": "font/woff2",
|
|
19611
21545
|
"etag": "\"1304-Lwyr1XiDl3FK38nmS/6Pk9wfiWc\"",
|
|
19612
|
-
"mtime": "2026-10-
|
|
21546
|
+
"mtime": "2026-10-12T02:43:06.048Z",
|
|
19613
21547
|
"size": 4868,
|
|
19614
21548
|
"path": "../public/_nuxt/wherdr-symbols.C1LsPKmg.woff2"
|
|
19615
21549
|
},
|
|
19616
21550
|
"/_nuxt/yzABg5xl.js": {
|
|
19617
21551
|
"type": "text/javascript; charset=utf-8",
|
|
19618
21552
|
"etag": "\"1c49-W7I7waHyF8BfftITNIl1xCbNFQw\"",
|
|
19619
|
-
"mtime": "2026-10-
|
|
21553
|
+
"mtime": "2026-10-12T02:43:06.048Z",
|
|
19620
21554
|
"size": 7241,
|
|
19621
21555
|
"path": "../public/_nuxt/yzABg5xl.js"
|
|
19622
21556
|
},
|
|
19623
|
-
"/_nuxt/
|
|
19624
|
-
"type": "
|
|
19625
|
-
"etag": "\"
|
|
19626
|
-
"mtime": "2026-10-
|
|
19627
|
-
"size":
|
|
19628
|
-
"path": "../public/_nuxt/
|
|
21557
|
+
"/_nuxt/xYX85SPI.js": {
|
|
21558
|
+
"type": "text/javascript; charset=utf-8",
|
|
21559
|
+
"etag": "\"374e-AaaIqO4sGKcceUeLatrSWJTAfAU\"",
|
|
21560
|
+
"mtime": "2026-10-12T02:43:06.048Z",
|
|
21561
|
+
"size": 14158,
|
|
21562
|
+
"path": "../public/_nuxt/xYX85SPI.js"
|
|
19629
21563
|
},
|
|
19630
|
-
"/_nuxt/builds/meta/
|
|
21564
|
+
"/_nuxt/builds/meta/c735b6c0-1bd0-47d7-a208-d310f0a06287.json": {
|
|
19631
21565
|
"type": "application/json",
|
|
19632
|
-
"etag": "\"58-
|
|
19633
|
-
"mtime": "2026-10-
|
|
21566
|
+
"etag": "\"58-d/XzFsF7fGOYu7cBbS83s+45DOk\"",
|
|
21567
|
+
"mtime": "2026-10-12T02:43:06.021Z",
|
|
19634
21568
|
"size": 88,
|
|
19635
|
-
"path": "../public/_nuxt/builds/meta/
|
|
19636
|
-
}
|
|
19637
|
-
|
|
19638
|
-
|
|
19639
|
-
|
|
19640
|
-
|
|
19641
|
-
|
|
19642
|
-
|
|
19643
|
-
}
|
|
19644
|
-
return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
|
|
19645
|
-
}
|
|
19646
|
-
const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
|
|
19647
|
-
const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
|
|
19648
|
-
const _ROOT_FOLDER_RE = /^\/([A-Za-z]:)?$/;
|
|
19649
|
-
function cwd() {
|
|
19650
|
-
if (typeof process !== "undefined" && typeof process.cwd === "function") {
|
|
19651
|
-
return process.cwd().replace(/\\/g, "/");
|
|
19652
|
-
}
|
|
19653
|
-
return "/";
|
|
19654
|
-
}
|
|
19655
|
-
const resolve = function(...arguments_) {
|
|
19656
|
-
arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
|
|
19657
|
-
let resolvedPath = "";
|
|
19658
|
-
let resolvedAbsolute = false;
|
|
19659
|
-
for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
|
|
19660
|
-
const path = index >= 0 ? arguments_[index] : cwd();
|
|
19661
|
-
if (!path || path.length === 0) {
|
|
19662
|
-
continue;
|
|
19663
|
-
}
|
|
19664
|
-
resolvedPath = `${path}/${resolvedPath}`;
|
|
19665
|
-
resolvedAbsolute = isAbsolute(path);
|
|
19666
|
-
}
|
|
19667
|
-
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
|
|
19668
|
-
if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
|
|
19669
|
-
return `/${resolvedPath}`;
|
|
19670
|
-
}
|
|
19671
|
-
return resolvedPath.length > 0 ? resolvedPath : ".";
|
|
19672
|
-
};
|
|
19673
|
-
function normalizeString(path, allowAboveRoot) {
|
|
19674
|
-
let res = "";
|
|
19675
|
-
let lastSegmentLength = 0;
|
|
19676
|
-
let lastSlash = -1;
|
|
19677
|
-
let dots = 0;
|
|
19678
|
-
let char = null;
|
|
19679
|
-
for (let index = 0; index <= path.length; ++index) {
|
|
19680
|
-
if (index < path.length) {
|
|
19681
|
-
char = path[index];
|
|
19682
|
-
} else if (char === "/") {
|
|
19683
|
-
break;
|
|
19684
|
-
} else {
|
|
19685
|
-
char = "/";
|
|
19686
|
-
}
|
|
19687
|
-
if (char === "/") {
|
|
19688
|
-
if (lastSlash === index - 1 || dots === 1) ; else if (dots === 2) {
|
|
19689
|
-
if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
|
|
19690
|
-
if (res.length > 2) {
|
|
19691
|
-
const lastSlashIndex = res.lastIndexOf("/");
|
|
19692
|
-
if (lastSlashIndex === -1) {
|
|
19693
|
-
res = "";
|
|
19694
|
-
lastSegmentLength = 0;
|
|
19695
|
-
} else {
|
|
19696
|
-
res = res.slice(0, lastSlashIndex);
|
|
19697
|
-
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
|
|
19698
|
-
}
|
|
19699
|
-
lastSlash = index;
|
|
19700
|
-
dots = 0;
|
|
19701
|
-
continue;
|
|
19702
|
-
} else if (res.length > 0) {
|
|
19703
|
-
res = "";
|
|
19704
|
-
lastSegmentLength = 0;
|
|
19705
|
-
lastSlash = index;
|
|
19706
|
-
dots = 0;
|
|
19707
|
-
continue;
|
|
19708
|
-
}
|
|
19709
|
-
}
|
|
19710
|
-
if (allowAboveRoot) {
|
|
19711
|
-
res += res.length > 0 ? "/.." : "..";
|
|
19712
|
-
lastSegmentLength = 2;
|
|
19713
|
-
}
|
|
19714
|
-
} else {
|
|
19715
|
-
if (res.length > 0) {
|
|
19716
|
-
res += `/${path.slice(lastSlash + 1, index)}`;
|
|
19717
|
-
} else {
|
|
19718
|
-
res = path.slice(lastSlash + 1, index);
|
|
19719
|
-
}
|
|
19720
|
-
lastSegmentLength = index - lastSlash - 1;
|
|
19721
|
-
}
|
|
19722
|
-
lastSlash = index;
|
|
19723
|
-
dots = 0;
|
|
19724
|
-
} else if (char === "." && dots !== -1) {
|
|
19725
|
-
++dots;
|
|
19726
|
-
} else {
|
|
19727
|
-
dots = -1;
|
|
19728
|
-
}
|
|
19729
|
-
}
|
|
19730
|
-
return res;
|
|
19731
|
-
}
|
|
19732
|
-
const isAbsolute = function(p) {
|
|
19733
|
-
return _IS_ABSOLUTE_RE.test(p);
|
|
19734
|
-
};
|
|
19735
|
-
const relative = function(from, to) {
|
|
19736
|
-
const _from = resolve(from).replace(_ROOT_FOLDER_RE, "$1").split("/");
|
|
19737
|
-
const _to = resolve(to).replace(_ROOT_FOLDER_RE, "$1").split("/");
|
|
19738
|
-
if (_to[0][1] === ":" && _from[0][1] === ":" && _from[0] !== _to[0]) {
|
|
19739
|
-
return _to.join("/");
|
|
19740
|
-
}
|
|
19741
|
-
const _fromCopy = [..._from];
|
|
19742
|
-
for (const segment of _fromCopy) {
|
|
19743
|
-
if (_to[0] !== segment) {
|
|
19744
|
-
break;
|
|
19745
|
-
}
|
|
19746
|
-
_from.shift();
|
|
19747
|
-
_to.shift();
|
|
19748
|
-
}
|
|
19749
|
-
return [..._from.map(() => ".."), ..._to].join("/");
|
|
19750
|
-
};
|
|
19751
|
-
const dirname = function(p) {
|
|
19752
|
-
const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
|
|
19753
|
-
if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) {
|
|
19754
|
-
segments[0] += "/";
|
|
19755
|
-
}
|
|
19756
|
-
return segments.join("/") || (isAbsolute(p) ? "/" : ".");
|
|
19757
|
-
};
|
|
19758
|
-
|
|
19759
|
-
function readAsset (id) {
|
|
19760
|
-
const serverDir = dirname(fileURLToPath(globalThis._importMeta_.url));
|
|
19761
|
-
return promises.readFile(resolve(serverDir, assets[id].path))
|
|
19762
|
-
}
|
|
19763
|
-
|
|
19764
|
-
const publicAssetBases = {"/_nuxt/builds/meta/":{"maxAge":31536000},"/_nuxt/builds/":{"maxAge":1},"/_nuxt/":{"maxAge":31536000}};
|
|
19765
|
-
|
|
19766
|
-
function isPublicAssetURL(id = '') {
|
|
19767
|
-
if (assets[id]) {
|
|
19768
|
-
return true
|
|
19769
|
-
}
|
|
19770
|
-
for (const base in publicAssetBases) {
|
|
19771
|
-
if (id.startsWith(base)) { return true }
|
|
19772
|
-
}
|
|
19773
|
-
return false
|
|
19774
|
-
}
|
|
19775
|
-
|
|
19776
|
-
function getAsset (id) {
|
|
19777
|
-
return assets[id]
|
|
19778
|
-
}
|
|
19779
|
-
|
|
19780
|
-
const METHODS = /* @__PURE__ */ new Set(["HEAD", "GET"]);
|
|
19781
|
-
const EncodingMap = { gzip: ".gz", br: ".br" };
|
|
19782
|
-
const _uN03ln = eventHandler((event) => {
|
|
19783
|
-
if (event.method && !METHODS.has(event.method)) {
|
|
19784
|
-
return;
|
|
19785
|
-
}
|
|
19786
|
-
let id = decodePath(
|
|
19787
|
-
withLeadingSlash(withoutTrailingSlash(parseURL(event.path).pathname))
|
|
19788
|
-
);
|
|
19789
|
-
let asset;
|
|
19790
|
-
const encodingHeader = String(
|
|
19791
|
-
getRequestHeader(event, "accept-encoding") || ""
|
|
19792
|
-
);
|
|
19793
|
-
const encodings = [
|
|
19794
|
-
...encodingHeader.split(",").map((e) => EncodingMap[e.trim()]).filter(Boolean).sort(),
|
|
19795
|
-
""
|
|
19796
|
-
];
|
|
19797
|
-
for (const encoding of encodings) {
|
|
19798
|
-
for (const _id of [id + encoding, joinURL(id, "index.html" + encoding)]) {
|
|
19799
|
-
const _asset = getAsset(_id);
|
|
19800
|
-
if (_asset) {
|
|
19801
|
-
asset = _asset;
|
|
19802
|
-
id = _id;
|
|
19803
|
-
break;
|
|
19804
|
-
}
|
|
19805
|
-
}
|
|
19806
|
-
}
|
|
19807
|
-
if (!asset) {
|
|
19808
|
-
if (isPublicAssetURL(id)) {
|
|
19809
|
-
removeResponseHeader(event, "Cache-Control");
|
|
19810
|
-
throw createError$1({ statusCode: 404 });
|
|
19811
|
-
}
|
|
19812
|
-
return;
|
|
19813
|
-
}
|
|
19814
|
-
if (asset.encoding !== void 0) {
|
|
19815
|
-
appendResponseHeader(event, "Vary", "Accept-Encoding");
|
|
19816
|
-
}
|
|
19817
|
-
const ifNotMatch = getRequestHeader(event, "if-none-match") === asset.etag;
|
|
19818
|
-
if (ifNotMatch) {
|
|
19819
|
-
setResponseStatus(event, 304, "Not Modified");
|
|
19820
|
-
return "";
|
|
19821
|
-
}
|
|
19822
|
-
const ifModifiedSinceH = getRequestHeader(event, "if-modified-since");
|
|
19823
|
-
const mtimeDate = new Date(asset.mtime);
|
|
19824
|
-
if (ifModifiedSinceH && asset.mtime && new Date(ifModifiedSinceH) >= mtimeDate) {
|
|
19825
|
-
setResponseStatus(event, 304, "Not Modified");
|
|
19826
|
-
return "";
|
|
19827
|
-
}
|
|
19828
|
-
if (asset.type && !getResponseHeader(event, "Content-Type")) {
|
|
19829
|
-
setResponseHeader(event, "Content-Type", asset.type);
|
|
19830
|
-
}
|
|
19831
|
-
if (asset.etag && !getResponseHeader(event, "ETag")) {
|
|
19832
|
-
setResponseHeader(event, "ETag", asset.etag);
|
|
19833
|
-
}
|
|
19834
|
-
if (asset.mtime && !getResponseHeader(event, "Last-Modified")) {
|
|
19835
|
-
setResponseHeader(event, "Last-Modified", mtimeDate.toUTCString());
|
|
19836
|
-
}
|
|
19837
|
-
if (asset.encoding && !getResponseHeader(event, "Content-Encoding")) {
|
|
19838
|
-
setResponseHeader(event, "Content-Encoding", asset.encoding);
|
|
19839
|
-
}
|
|
19840
|
-
if (asset.size > 0 && !getResponseHeader(event, "Content-Length")) {
|
|
19841
|
-
setResponseHeader(event, "Content-Length", asset.size);
|
|
19842
|
-
}
|
|
19843
|
-
return readAsset(id);
|
|
19844
|
-
});
|
|
19845
|
-
|
|
19846
|
-
const MAX_SESSION_DAYS = [1, 7, 30, 90, 365];
|
|
19847
|
-
const DEFAULT_MAX_SESSION_DAYS = 7;
|
|
19848
|
-
const DAY_MS = 24 * 3600 * 1e3;
|
|
19849
|
-
function isMaxSessionDays(v) {
|
|
19850
|
-
return MAX_SESSION_DAYS.includes(v);
|
|
19851
|
-
}
|
|
19852
|
-
|
|
19853
|
-
var __defProp = Object.defineProperty;
|
|
19854
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
19855
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
|
|
19856
|
-
const SESSION_MS = 12 * 3600 * 1e3;
|
|
19857
|
-
const RENEW_MS = 10 * 60 * 1e3;
|
|
19858
|
-
const CHALLENGE_MS = 5 * 60 * 1e3;
|
|
19859
|
-
const AUTH_COOKIE = "hw_session";
|
|
19860
|
-
const CHALLENGE_COOKIE = "hw_challenge";
|
|
19861
|
-
class AuthError extends Error {
|
|
19862
|
-
constructor(code, message) {
|
|
19863
|
-
super(message);
|
|
19864
|
-
__publicField(this, "code");
|
|
19865
|
-
this.code = code;
|
|
21569
|
+
"path": "../public/_nuxt/builds/meta/c735b6c0-1bd0-47d7-a208-d310f0a06287.json"
|
|
21570
|
+
},
|
|
21571
|
+
"/_nuxt/builds/latest.json": {
|
|
21572
|
+
"type": "application/json",
|
|
21573
|
+
"etag": "\"47-tXRY9adXBmru9/K9a4qadfDPmmc\"",
|
|
21574
|
+
"mtime": "2026-10-12T02:43:06.026Z",
|
|
21575
|
+
"size": 71,
|
|
21576
|
+
"path": "../public/_nuxt/builds/latest.json"
|
|
19866
21577
|
}
|
|
19867
|
-
}
|
|
19868
|
-
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
|
|
19872
|
-
|
|
19873
|
-
db = { ...db, ...JSON.parse(fs.readFileSync(FILE, "utf8")) };
|
|
19874
|
-
} catch {
|
|
21578
|
+
};
|
|
21579
|
+
|
|
21580
|
+
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
|
|
21581
|
+
function normalizeWindowsPath(input = "") {
|
|
21582
|
+
if (!input) {
|
|
21583
|
+
return input;
|
|
19875
21584
|
}
|
|
19876
|
-
|
|
19877
|
-
|
|
19878
|
-
|
|
19879
|
-
|
|
19880
|
-
|
|
19881
|
-
|
|
19882
|
-
|
|
21585
|
+
return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
|
|
21586
|
+
}
|
|
21587
|
+
const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
|
|
21588
|
+
const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
|
|
21589
|
+
const _ROOT_FOLDER_RE = /^\/([A-Za-z]:)?$/;
|
|
21590
|
+
function cwd() {
|
|
21591
|
+
if (typeof process !== "undefined" && typeof process.cwd === "function") {
|
|
21592
|
+
return process.cwd().replace(/\\/g, "/");
|
|
19883
21593
|
}
|
|
19884
|
-
|
|
19885
|
-
|
|
19886
|
-
|
|
19887
|
-
|
|
19888
|
-
|
|
19889
|
-
|
|
19890
|
-
|
|
19891
|
-
|
|
19892
|
-
|
|
19893
|
-
|
|
19894
|
-
const c = String(req.headers.cookie || "").split(/;\s*/).find((s) => s.startsWith(`${AUTH_COOKIE}=`));
|
|
19895
|
-
if (!c) return null;
|
|
19896
|
-
try {
|
|
19897
|
-
return decodeURIComponent(c.slice(AUTH_COOKIE.length + 1));
|
|
19898
|
-
} catch {
|
|
19899
|
-
return null;
|
|
19900
|
-
}
|
|
19901
|
-
};
|
|
19902
|
-
const challengeClient = (req) => {
|
|
19903
|
-
const c = String(req.headers.cookie || "").split(/;\s*/).find((s) => s.startsWith(`${CHALLENGE_COOKIE}=`));
|
|
19904
|
-
return (c == null ? void 0 : c.slice(CHALLENGE_COOKIE.length + 1)) || null;
|
|
19905
|
-
};
|
|
19906
|
-
const checkBootstrap = (body) => {
|
|
19907
|
-
if (!enabled() && !safeEq(String((body == null ? void 0 : body.bootstrapToken) || ""), bootstrapToken)) {
|
|
19908
|
-
throw new AuthError("bootstrap", "Invalid bootstrap token (see the server logs)");
|
|
21594
|
+
return "/";
|
|
21595
|
+
}
|
|
21596
|
+
const resolve = function(...arguments_) {
|
|
21597
|
+
arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
|
|
21598
|
+
let resolvedPath = "";
|
|
21599
|
+
let resolvedAbsolute = false;
|
|
21600
|
+
for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
|
|
21601
|
+
const path = index >= 0 ? arguments_[index] : cwd();
|
|
21602
|
+
if (!path || path.length === 0) {
|
|
21603
|
+
continue;
|
|
19909
21604
|
}
|
|
19910
|
-
|
|
19911
|
-
|
|
19912
|
-
var _a;
|
|
19913
|
-
return ((_a = db.maxSessionDays) != null ? _a : DEFAULT_MAX_SESSION_DAYS) * DAY_MS;
|
|
19914
|
-
};
|
|
19915
|
-
function session(req, now = Date.now()) {
|
|
19916
|
-
const v = readCookie(req);
|
|
19917
|
-
if (!v) return null;
|
|
19918
|
-
const parts = v.split(".");
|
|
19919
|
-
if (parts.length !== 3 && parts.length !== 4) return null;
|
|
19920
|
-
const mac = parts.pop();
|
|
19921
|
-
if (!mac || parts[1] !== String(db.generation) || !safeEq(mac, sign(parts.join(".")))) return null;
|
|
19922
|
-
const exp = Number(parts[0]);
|
|
19923
|
-
const since = parts.length === 3 ? Number(parts[2]) : exp - SESSION_MS;
|
|
19924
|
-
if (!(exp >= now) || !Number.isFinite(since)) return null;
|
|
19925
|
-
if (now > since + maxSessionMs() + SESSION_MS) return null;
|
|
19926
|
-
return { exp, since };
|
|
19927
|
-
}
|
|
19928
|
-
function isUnlocked(req) {
|
|
19929
|
-
return !enabled() || session(req) !== null;
|
|
19930
|
-
}
|
|
19931
|
-
const secure = (req) => req.headers["x-forwarded-proto"] === "https" || String(req.headers.origin || "").startsWith("https://") ? "; Secure" : "";
|
|
19932
|
-
function sessionCookie(req, now = Date.now(), since = now) {
|
|
19933
|
-
const exp = now + SESSION_MS;
|
|
19934
|
-
const payload = `${exp}.${db.generation}.${since}`;
|
|
19935
|
-
return `${AUTH_COOKIE}=${payload}.${sign(payload)}; Path=/; HttpOnly; SameSite=Strict; Max-Age=${SESSION_MS / 1e3}${secure(req)}`;
|
|
19936
|
-
}
|
|
19937
|
-
const clearCookie = (req) => `${AUTH_COOKIE}=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0${secure(req)}`;
|
|
19938
|
-
function renew(req, now = Date.now()) {
|
|
19939
|
-
if (!enabled()) return null;
|
|
19940
|
-
const s = session(req, now);
|
|
19941
|
-
if (s === null || s.exp - now > SESSION_MS - RENEW_MS) return null;
|
|
19942
|
-
return { cookie: sessionCookie(req, now, s.since), expiresAt: now + SESSION_MS };
|
|
19943
|
-
}
|
|
19944
|
-
function relying(req) {
|
|
19945
|
-
const origin = String(req.headers.origin || "");
|
|
19946
|
-
const { hostname } = new URL(origin);
|
|
19947
|
-
return { origin, rpID: hostname };
|
|
19948
|
-
}
|
|
19949
|
-
const challenges = /* @__PURE__ */ new Map();
|
|
19950
|
-
const challengeKey = (kind, client) => `${kind}:${client}`;
|
|
19951
|
-
const issueChallenge = (req, kind, value) => {
|
|
19952
|
-
const client = crypto.randomBytes(24).toString("base64url");
|
|
19953
|
-
for (const [key, challenge] of challenges) if (challenge.exp < Date.now()) challenges.delete(key);
|
|
19954
|
-
challenges.set(challengeKey(kind, client), { value, exp: Date.now() + CHALLENGE_MS });
|
|
19955
|
-
return `${CHALLENGE_COOKIE}=${client}; Path=/api/auth/; HttpOnly; SameSite=Strict; Max-Age=${CHALLENGE_MS / 1e3}${secure(req)}`;
|
|
19956
|
-
};
|
|
19957
|
-
const takeChallenge = (req, kind) => {
|
|
19958
|
-
const client = challengeClient(req);
|
|
19959
|
-
const key = challengeKey(kind, client || "");
|
|
19960
|
-
const c = challenges.get(key);
|
|
19961
|
-
challenges.delete(key);
|
|
19962
|
-
if (!c || c.exp < Date.now()) throw new AuthError("challenge", "Challenge expired, try again");
|
|
19963
|
-
return c.value;
|
|
19964
|
-
};
|
|
19965
|
-
async function registerOptions(req, body = {}) {
|
|
19966
|
-
if (enabled() && !isUnlocked(req)) throw new AuthError("locked", "Unlock first");
|
|
19967
|
-
checkBootstrap(body);
|
|
19968
|
-
const { rpID } = relying(req);
|
|
19969
|
-
const opts = await generateRegistrationOptions({
|
|
19970
|
-
rpName: "wherdr",
|
|
19971
|
-
rpID,
|
|
19972
|
-
userName: passkeyUser,
|
|
19973
|
-
userDisplayName: passkeyUser,
|
|
19974
|
-
attestationType: "none",
|
|
19975
|
-
excludeCredentials: db.credentials.map((c) => ({ id: c.id, transports: c.transports })),
|
|
19976
|
-
authenticatorSelection: { residentKey: "preferred", userVerification: "required" }
|
|
19977
|
-
});
|
|
19978
|
-
return { ...opts, __cookie: issueChallenge(req, "reg", opts.challenge) };
|
|
19979
|
-
}
|
|
19980
|
-
async function registerVerify(req, body) {
|
|
19981
|
-
if (enabled() && !isUnlocked(req)) throw new AuthError("locked", "Unlock first");
|
|
19982
|
-
checkBootstrap(body);
|
|
19983
|
-
const { origin, rpID } = relying(req);
|
|
19984
|
-
const v = await verifyRegistrationResponse({
|
|
19985
|
-
response: body.response,
|
|
19986
|
-
expectedChallenge: takeChallenge(req, "reg"),
|
|
19987
|
-
expectedOrigin: origin,
|
|
19988
|
-
expectedRPID: rpID,
|
|
19989
|
-
requireUserVerification: true
|
|
19990
|
-
});
|
|
19991
|
-
if (!v.verified || !v.registrationInfo) throw new AuthError("rejected", "Key rejected");
|
|
19992
|
-
const { credential } = v.registrationInfo;
|
|
19993
|
-
db.credentials.push({
|
|
19994
|
-
id: credential.id,
|
|
19995
|
-
publicKey: Buffer.from(credential.publicKey).toString("base64url"),
|
|
19996
|
-
counter: credential.counter,
|
|
19997
|
-
transports: credential.transports || [],
|
|
19998
|
-
name: String(body.name || "Device").slice(0, 40),
|
|
19999
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
20000
|
-
});
|
|
20001
|
-
save();
|
|
20002
|
-
log2(`lock: key registered (${db.credentials.length})`);
|
|
20003
|
-
return { ok: true, __cookie: sessionCookie(req) };
|
|
20004
|
-
}
|
|
20005
|
-
async function loginOptions(req) {
|
|
20006
|
-
const { rpID } = relying(req);
|
|
20007
|
-
const opts = await generateAuthenticationOptions({
|
|
20008
|
-
rpID,
|
|
20009
|
-
userVerification: "required",
|
|
20010
|
-
allowCredentials: db.credentials.map((c) => ({ id: c.id, transports: c.transports }))
|
|
20011
|
-
});
|
|
20012
|
-
return { ...opts, __cookie: issueChallenge(req, "auth", opts.challenge) };
|
|
20013
|
-
}
|
|
20014
|
-
async function loginVerify(req, body) {
|
|
20015
|
-
const { origin, rpID } = relying(req);
|
|
20016
|
-
const cred = db.credentials.find((c) => c.id === (body.response && body.response.id));
|
|
20017
|
-
if (!cred) throw new AuthError("unknown_key", "Key unknown on this server");
|
|
20018
|
-
const v = await verifyAuthenticationResponse({
|
|
20019
|
-
response: body.response,
|
|
20020
|
-
expectedChallenge: takeChallenge(req, "auth"),
|
|
20021
|
-
expectedOrigin: origin,
|
|
20022
|
-
expectedRPID: rpID,
|
|
20023
|
-
requireUserVerification: true,
|
|
20024
|
-
credential: { id: cred.id, publicKey: Buffer.from(cred.publicKey, "base64url"), counter: cred.counter, transports: cred.transports }
|
|
20025
|
-
});
|
|
20026
|
-
if (!v.verified) throw new AuthError("rejected", "Unlock rejected");
|
|
20027
|
-
cred.counter = v.authenticationInfo.newCounter;
|
|
20028
|
-
cred.lastUsed = (/* @__PURE__ */ new Date()).toISOString();
|
|
20029
|
-
save();
|
|
20030
|
-
return { ok: true, __cookie: sessionCookie(req) };
|
|
21605
|
+
resolvedPath = `${path}/${resolvedPath}`;
|
|
21606
|
+
resolvedAbsolute = isAbsolute(path);
|
|
20031
21607
|
}
|
|
20032
|
-
|
|
20033
|
-
|
|
20034
|
-
|
|
20035
|
-
db.generation += 1;
|
|
20036
|
-
save();
|
|
20037
|
-
log2("lock disabled");
|
|
20038
|
-
return { ok: true, __cookie: clearCookie(req) };
|
|
21608
|
+
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
|
|
21609
|
+
if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
|
|
21610
|
+
return `/${resolvedPath}`;
|
|
20039
21611
|
}
|
|
20040
|
-
|
|
20041
|
-
|
|
20042
|
-
|
|
20043
|
-
|
|
20044
|
-
|
|
20045
|
-
|
|
21612
|
+
return resolvedPath.length > 0 ? resolvedPath : ".";
|
|
21613
|
+
};
|
|
21614
|
+
function normalizeString(path, allowAboveRoot) {
|
|
21615
|
+
let res = "";
|
|
21616
|
+
let lastSegmentLength = 0;
|
|
21617
|
+
let lastSlash = -1;
|
|
21618
|
+
let dots = 0;
|
|
21619
|
+
let char = null;
|
|
21620
|
+
for (let index = 0; index <= path.length; ++index) {
|
|
21621
|
+
if (index < path.length) {
|
|
21622
|
+
char = path[index];
|
|
21623
|
+
} else if (char === "/") {
|
|
21624
|
+
break;
|
|
21625
|
+
} else {
|
|
21626
|
+
char = "/";
|
|
21627
|
+
}
|
|
21628
|
+
if (char === "/") {
|
|
21629
|
+
if (lastSlash === index - 1 || dots === 1) ; else if (dots === 2) {
|
|
21630
|
+
if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
|
|
21631
|
+
if (res.length > 2) {
|
|
21632
|
+
const lastSlashIndex = res.lastIndexOf("/");
|
|
21633
|
+
if (lastSlashIndex === -1) {
|
|
21634
|
+
res = "";
|
|
21635
|
+
lastSegmentLength = 0;
|
|
21636
|
+
} else {
|
|
21637
|
+
res = res.slice(0, lastSlashIndex);
|
|
21638
|
+
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
|
|
21639
|
+
}
|
|
21640
|
+
lastSlash = index;
|
|
21641
|
+
dots = 0;
|
|
21642
|
+
continue;
|
|
21643
|
+
} else if (res.length > 0) {
|
|
21644
|
+
res = "";
|
|
21645
|
+
lastSegmentLength = 0;
|
|
21646
|
+
lastSlash = index;
|
|
21647
|
+
dots = 0;
|
|
21648
|
+
continue;
|
|
21649
|
+
}
|
|
21650
|
+
}
|
|
21651
|
+
if (allowAboveRoot) {
|
|
21652
|
+
res += res.length > 0 ? "/.." : "..";
|
|
21653
|
+
lastSegmentLength = 2;
|
|
21654
|
+
}
|
|
21655
|
+
} else {
|
|
21656
|
+
if (res.length > 0) {
|
|
21657
|
+
res += `/${path.slice(lastSlash + 1, index)}`;
|
|
21658
|
+
} else {
|
|
21659
|
+
res = path.slice(lastSlash + 1, index);
|
|
21660
|
+
}
|
|
21661
|
+
lastSegmentLength = index - lastSlash - 1;
|
|
21662
|
+
}
|
|
21663
|
+
lastSlash = index;
|
|
21664
|
+
dots = 0;
|
|
21665
|
+
} else if (char === "." && dots !== -1) {
|
|
21666
|
+
++dots;
|
|
21667
|
+
} else {
|
|
21668
|
+
dots = -1;
|
|
21669
|
+
}
|
|
20046
21670
|
}
|
|
20047
|
-
|
|
20048
|
-
|
|
20049
|
-
|
|
20050
|
-
|
|
20051
|
-
|
|
20052
|
-
|
|
21671
|
+
return res;
|
|
21672
|
+
}
|
|
21673
|
+
const isAbsolute = function(p) {
|
|
21674
|
+
return _IS_ABSOLUTE_RE.test(p);
|
|
21675
|
+
};
|
|
21676
|
+
const relative = function(from, to) {
|
|
21677
|
+
const _from = resolve(from).replace(_ROOT_FOLDER_RE, "$1").split("/");
|
|
21678
|
+
const _to = resolve(to).replace(_ROOT_FOLDER_RE, "$1").split("/");
|
|
21679
|
+
if (_to[0][1] === ":" && _from[0][1] === ":" && _from[0] !== _to[0]) {
|
|
21680
|
+
return _to.join("/");
|
|
20053
21681
|
}
|
|
20054
|
-
|
|
20055
|
-
|
|
20056
|
-
|
|
20057
|
-
|
|
20058
|
-
const s = session(req, now);
|
|
20059
|
-
const deadline = s ? s.since + maxSessionMs() : 0;
|
|
20060
|
-
if (!s || resume && now >= deadline) {
|
|
20061
|
-
return { enabled: true, unlocked: false, expiresAt: null, now, devices, ...s ? { __cookie: clearCookie(req) } : {} };
|
|
21682
|
+
const _fromCopy = [..._from];
|
|
21683
|
+
for (const segment of _fromCopy) {
|
|
21684
|
+
if (_to[0] !== segment) {
|
|
21685
|
+
break;
|
|
20062
21686
|
}
|
|
20063
|
-
|
|
20064
|
-
|
|
20065
|
-
enabled: true,
|
|
20066
|
-
unlocked: true,
|
|
20067
|
-
expiresAt: (_a = renewed == null ? void 0 : renewed.expiresAt) != null ? _a : s.exp,
|
|
20068
|
-
deadline,
|
|
20069
|
-
now,
|
|
20070
|
-
maxSessionDays: (_b = db.maxSessionDays) != null ? _b : DEFAULT_MAX_SESSION_DAYS,
|
|
20071
|
-
devices,
|
|
20072
|
-
...renewed ? { __cookie: renewed.cookie } : {}
|
|
20073
|
-
};
|
|
21687
|
+
_from.shift();
|
|
21688
|
+
_to.shift();
|
|
20074
21689
|
}
|
|
20075
|
-
return
|
|
20076
|
-
|
|
20077
|
-
|
|
20078
|
-
|
|
20079
|
-
|
|
20080
|
-
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
|
|
20084
|
-
|
|
20085
|
-
|
|
20086
|
-
|
|
20087
|
-
|
|
20088
|
-
_sessionCookie: sessionCookie,
|
|
20089
|
-
_db: () => db,
|
|
20090
|
-
_bootstrapToken: () => bootstrapToken,
|
|
20091
|
-
_takeChallenge: takeChallenge
|
|
20092
|
-
};
|
|
21690
|
+
return [..._from.map(() => ".."), ..._to].join("/");
|
|
21691
|
+
};
|
|
21692
|
+
const dirname = function(p) {
|
|
21693
|
+
const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
|
|
21694
|
+
if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) {
|
|
21695
|
+
segments[0] += "/";
|
|
21696
|
+
}
|
|
21697
|
+
return segments.join("/") || (isAbsolute(p) ? "/" : ".");
|
|
21698
|
+
};
|
|
21699
|
+
|
|
21700
|
+
function readAsset (id) {
|
|
21701
|
+
const serverDir = dirname(fileURLToPath(globalThis._importMeta_.url));
|
|
21702
|
+
return promises.readFile(resolve(serverDir, assets[id].path))
|
|
20093
21703
|
}
|
|
20094
21704
|
|
|
20095
|
-
const
|
|
20096
|
-
|
|
20097
|
-
function
|
|
20098
|
-
|
|
20099
|
-
|
|
20100
|
-
try {
|
|
20101
|
-
return new URL(o).host === headers.host;
|
|
20102
|
-
} catch {
|
|
20103
|
-
return false;
|
|
21705
|
+
const publicAssetBases = {"/_nuxt/builds/meta/":{"maxAge":31536000},"/_nuxt/builds/":{"maxAge":1},"/_nuxt/":{"maxAge":31536000}};
|
|
21706
|
+
|
|
21707
|
+
function isPublicAssetURL(id = '') {
|
|
21708
|
+
if (assets[id]) {
|
|
21709
|
+
return true
|
|
20104
21710
|
}
|
|
21711
|
+
for (const base in publicAssetBases) {
|
|
21712
|
+
if (id.startsWith(base)) { return true }
|
|
21713
|
+
}
|
|
21714
|
+
return false
|
|
20105
21715
|
}
|
|
20106
|
-
|
|
20107
|
-
function
|
|
20108
|
-
|
|
20109
|
-
setResponseHeader(event, "cache-control", "no-store");
|
|
20110
|
-
return body;
|
|
21716
|
+
|
|
21717
|
+
function getAsset (id) {
|
|
21718
|
+
return assets[id]
|
|
20111
21719
|
}
|
|
20112
|
-
|
|
20113
|
-
|
|
20114
|
-
|
|
20115
|
-
|
|
20116
|
-
|
|
20117
|
-
|
|
20118
|
-
|
|
20119
|
-
|
|
20120
|
-
|
|
20121
|
-
|
|
20122
|
-
|
|
20123
|
-
|
|
20124
|
-
|
|
20125
|
-
|
|
20126
|
-
|
|
20127
|
-
|
|
20128
|
-
|
|
20129
|
-
|
|
20130
|
-
|
|
20131
|
-
|
|
20132
|
-
|
|
20133
|
-
|
|
20134
|
-
|
|
20135
|
-
|
|
20136
|
-
|
|
20137
|
-
setResponseHeader(event, "cache-control", "no-store");
|
|
20138
|
-
if (out && typeof out === "object" && "__cookie" in out) {
|
|
20139
|
-
const o = out;
|
|
20140
|
-
setResponseHeader(event, "set-cookie", String(o.__cookie));
|
|
20141
|
-
delete o.__cookie;
|
|
21720
|
+
|
|
21721
|
+
const METHODS = /* @__PURE__ */ new Set(["HEAD", "GET"]);
|
|
21722
|
+
const EncodingMap = { gzip: ".gz", br: ".br" };
|
|
21723
|
+
const _uN03ln = eventHandler((event) => {
|
|
21724
|
+
if (event.method && !METHODS.has(event.method)) {
|
|
21725
|
+
return;
|
|
21726
|
+
}
|
|
21727
|
+
let id = decodePath(
|
|
21728
|
+
withLeadingSlash(withoutTrailingSlash(parseURL(event.path).pathname))
|
|
21729
|
+
);
|
|
21730
|
+
let asset;
|
|
21731
|
+
const encodingHeader = String(
|
|
21732
|
+
getRequestHeader(event, "accept-encoding") || ""
|
|
21733
|
+
);
|
|
21734
|
+
const encodings = [
|
|
21735
|
+
...encodingHeader.split(",").map((e) => EncodingMap[e.trim()]).filter(Boolean).sort(),
|
|
21736
|
+
""
|
|
21737
|
+
];
|
|
21738
|
+
for (const encoding of encodings) {
|
|
21739
|
+
for (const _id of [id + encoding, joinURL(id, "index.html" + encoding)]) {
|
|
21740
|
+
const _asset = getAsset(_id);
|
|
21741
|
+
if (_asset) {
|
|
21742
|
+
asset = _asset;
|
|
21743
|
+
id = _id;
|
|
21744
|
+
break;
|
|
20142
21745
|
}
|
|
20143
|
-
return out;
|
|
20144
|
-
} catch (e) {
|
|
20145
|
-
const err = e;
|
|
20146
|
-
const known = e instanceof HerdrError || url.pathname.startsWith("/api/auth/") && Boolean(err.code);
|
|
20147
|
-
const code = known ? err.code : "internal";
|
|
20148
|
-
if (!known) log("error", event.method, url.pathname, e);
|
|
20149
|
-
const status = err.code === "unreachable" ? 503 : err.code === "locked" ? 401 : 400;
|
|
20150
|
-
return sendError(event, status, { error: err.message, code });
|
|
20151
21746
|
}
|
|
20152
|
-
});
|
|
20153
|
-
}
|
|
20154
|
-
function sendImage(event, img) {
|
|
20155
|
-
setResponseHeader(event, "content-type", img.type);
|
|
20156
|
-
setResponseHeader(event, "cache-control", "private, max-age=86400");
|
|
20157
|
-
setResponseHeader(event, "x-content-type-options", "nosniff");
|
|
20158
|
-
setResponseHeader(event, "content-security-policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox");
|
|
20159
|
-
return img.body;
|
|
20160
|
-
}
|
|
20161
|
-
|
|
20162
|
-
function hostname(value) {
|
|
20163
|
-
try {
|
|
20164
|
-
if (!value || /[\s/@?#]/.test(value)) return null;
|
|
20165
|
-
const url = new URL(`http://${value}`);
|
|
20166
|
-
if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) return null;
|
|
20167
|
-
return url.hostname.replace(/^\[|\]$/g, "").toLowerCase().replace(/\.$/, "");
|
|
20168
|
-
} catch {
|
|
20169
|
-
return null;
|
|
20170
21747
|
}
|
|
20171
|
-
|
|
20172
|
-
|
|
20173
|
-
|
|
20174
|
-
|
|
20175
|
-
|
|
20176
|
-
|
|
21748
|
+
if (!asset) {
|
|
21749
|
+
if (isPublicAssetURL(id)) {
|
|
21750
|
+
removeResponseHeader(event, "Cache-Control");
|
|
21751
|
+
throw createError$1({ statusCode: 404 });
|
|
21752
|
+
}
|
|
21753
|
+
return;
|
|
20177
21754
|
}
|
|
20178
|
-
|
|
20179
|
-
|
|
21755
|
+
if (asset.encoding !== void 0) {
|
|
21756
|
+
appendResponseHeader(event, "Vary", "Accept-Encoding");
|
|
20180
21757
|
}
|
|
20181
|
-
|
|
20182
|
-
|
|
20183
|
-
|
|
21758
|
+
const ifNotMatch = getRequestHeader(event, "if-none-match") === asset.etag;
|
|
21759
|
+
if (ifNotMatch) {
|
|
21760
|
+
setResponseStatus(event, 304, "Not Modified");
|
|
21761
|
+
return "";
|
|
20184
21762
|
}
|
|
20185
|
-
|
|
20186
|
-
|
|
20187
|
-
|
|
20188
|
-
|
|
20189
|
-
|
|
20190
|
-
}
|
|
20191
|
-
|
|
20192
|
-
|
|
20193
|
-
}
|
|
20194
|
-
|
|
20195
|
-
|
|
20196
|
-
|
|
20197
|
-
|
|
20198
|
-
|
|
20199
|
-
|
|
20200
|
-
|
|
20201
|
-
|
|
20202
|
-
|
|
20203
|
-
|
|
20204
|
-
|
|
20205
|
-
|
|
20206
|
-
|
|
20207
|
-
|
|
20208
|
-
<hr>
|
|
20209
|
-
<h1 lang="fr">Cette adresse n\u2019est pas encore activ\xE9e.</h1>
|
|
20210
|
-
<p lang="fr">Sur l\u2019ordinateur, ouvre wherdr \u2192 R\xE9glages \u203A T\xE9l\xE9phone (ou appuie sur V\xE9rifier), puis recharge.</p>
|
|
20211
|
-
</main></body></html>
|
|
20212
|
-
`;
|
|
20213
|
-
function crossSiteRequest(headers) {
|
|
20214
|
-
const site = headers["sec-fetch-site"];
|
|
20215
|
-
return typeof site === "string" && site !== "same-origin" && site !== "none";
|
|
20216
|
-
}
|
|
21763
|
+
const ifModifiedSinceH = getRequestHeader(event, "if-modified-since");
|
|
21764
|
+
const mtimeDate = new Date(asset.mtime);
|
|
21765
|
+
if (ifModifiedSinceH && asset.mtime && new Date(ifModifiedSinceH) >= mtimeDate) {
|
|
21766
|
+
setResponseStatus(event, 304, "Not Modified");
|
|
21767
|
+
return "";
|
|
21768
|
+
}
|
|
21769
|
+
if (asset.type && !getResponseHeader(event, "Content-Type")) {
|
|
21770
|
+
setResponseHeader(event, "Content-Type", asset.type);
|
|
21771
|
+
}
|
|
21772
|
+
if (asset.etag && !getResponseHeader(event, "ETag")) {
|
|
21773
|
+
setResponseHeader(event, "ETag", asset.etag);
|
|
21774
|
+
}
|
|
21775
|
+
if (asset.mtime && !getResponseHeader(event, "Last-Modified")) {
|
|
21776
|
+
setResponseHeader(event, "Last-Modified", mtimeDate.toUTCString());
|
|
21777
|
+
}
|
|
21778
|
+
if (asset.encoding && !getResponseHeader(event, "Content-Encoding")) {
|
|
21779
|
+
setResponseHeader(event, "Content-Encoding", asset.encoding);
|
|
21780
|
+
}
|
|
21781
|
+
if (asset.size > 0 && !getResponseHeader(event, "Content-Length")) {
|
|
21782
|
+
setResponseHeader(event, "Content-Length", asset.size);
|
|
21783
|
+
}
|
|
21784
|
+
return readAsset(id);
|
|
21785
|
+
});
|
|
20217
21786
|
|
|
20218
|
-
const
|
|
20219
|
-
const
|
|
21787
|
+
const HOST_RECHECK_MS = 3e3;
|
|
21788
|
+
const needsUnlock = (p) => p.startsWith("/api/") && !p.startsWith("/api/auth/") && p !== "/api/health" || p.startsWith("/uploads/");
|
|
21789
|
+
const _M8hgLp = defineEventHandler(async (event) => {
|
|
21790
|
+
if (!hostAllowed(event.node.req.headers.host)) await adoptServed(HOST_RECHECK_MS);
|
|
20220
21791
|
if (!hostAllowed(event.node.req.headers.host)) {
|
|
20221
21792
|
if (hostRefusalIsHtml(event.path.split("?")[0], event.node.req.headers.accept)) {
|
|
20222
21793
|
setResponseStatus(event, 403);
|
|
20223
21794
|
setResponseHeader(event, "content-type", "text/html; charset=utf-8");
|
|
20224
21795
|
setResponseHeader(event, "cache-control", "no-store");
|
|
20225
|
-
return
|
|
21796
|
+
return hostRefusedPage(void 0, event.node.req.headers.host);
|
|
20226
21797
|
}
|
|
20227
21798
|
return sendError(event, 403, { error: "Host not allowed", code: "host" });
|
|
20228
21799
|
}
|
|
@@ -20254,6 +21825,7 @@ const _lazy_4__oTm = () => import('../routes/api/auth/max-session.post.mjs');
|
|
|
20254
21825
|
const _lazy_7O3TsZ = () => import('../routes/api/auth/register/options.post.mjs');
|
|
20255
21826
|
const _lazy_fQByfS = () => import('../routes/api/auth/register/verify.post.mjs');
|
|
20256
21827
|
const _lazy_jcfnWB = () => import('../routes/api/auth/status.get.mjs');
|
|
21828
|
+
const _lazy_DwN9dC = () => import('../routes/api/board.get.mjs');
|
|
20257
21829
|
const _lazy_WhNb61 = () => import('../routes/api/changes.get.mjs');
|
|
20258
21830
|
const _lazy_hdSKYJ = () => import('../routes/api/chat.get.mjs');
|
|
20259
21831
|
const _lazy_WoSEa6 = () => import('../routes/api/chat/image.get.mjs');
|
|
@@ -20265,11 +21837,16 @@ const _lazy_GF2Rhb = () => import('../routes/api/codex-update.post.mjs');
|
|
|
20265
21837
|
const _lazy_onTn85 = () => import('../routes/api/commands.get.mjs');
|
|
20266
21838
|
const _lazy_XuUgLO = () => import('../routes/api/config.get.mjs');
|
|
20267
21839
|
const _lazy_y_su18 = () => import('../routes/api/dirs.get.mjs');
|
|
21840
|
+
const _lazy_l7QdCS = () => import('../routes/api/dismiss-stopped.post.mjs');
|
|
20268
21841
|
const _lazy_Z6oqjB = () => import('../routes/api/dismiss.post.mjs');
|
|
20269
21842
|
const _lazy_2uy6Z9 = () => import('../routes/api/effort.post.mjs');
|
|
20270
21843
|
const _lazy_7FhJOq = () => import('../routes/api/efforts.get.mjs');
|
|
20271
21844
|
const _lazy_Wg_MPT = () => import('../routes/api/file.post.mjs');
|
|
21845
|
+
const _lazy_vy5jzJ = () => import('../routes/api/files.get.mjs');
|
|
21846
|
+
const _lazy_iD5EkU = () => import('../routes/api/files/download.get.mjs');
|
|
21847
|
+
const _lazy_TecKdl = () => import('../routes/api/files/read.get.mjs');
|
|
20272
21848
|
const _lazy_kEWyNy = () => import('../routes/api/gitroot.get.mjs');
|
|
21849
|
+
const _lazy_1Vqvr9 = () => import('../routes/api/health.get.mjs');
|
|
20273
21850
|
const _lazy_U4OEhz = () => import('../routes/api/input.post.mjs');
|
|
20274
21851
|
const _lazy_PDyMr9 = () => import('../routes/api/interrupt.post.mjs');
|
|
20275
21852
|
const _lazy_mtPGaX = () => import('../routes/api/isgit.get.mjs');
|
|
@@ -20293,8 +21870,15 @@ const _lazy_mhIdo4 = () => import('../routes/api/plugins/projects.get.mjs');
|
|
|
20293
21870
|
const _lazy_zH9tge = () => import('../routes/api/plugins/projects.post.mjs');
|
|
20294
21871
|
const _lazy_qE4HMW = () => import('../routes/api/plugins/thread-limits.get.mjs');
|
|
20295
21872
|
const _lazy_ByJj04 = () => import('../routes/api/plugins/thread-limits.post.mjs');
|
|
21873
|
+
const _lazy_VZ7yeT = () => import('../routes/api/project/inbox.get.mjs');
|
|
21874
|
+
const _lazy_wq4Jn3 = () => import('../routes/api/project/inbox.post.mjs');
|
|
20296
21875
|
const _lazy_WLg2Uu = () => import('../routes/api/index.get.mjs');
|
|
21876
|
+
const _lazy_LkLunk = () => import('../routes/api/project/memory.get.mjs');
|
|
20297
21877
|
const _lazy_sQe2ZF = () => import('../routes/api/project/report.get.mjs');
|
|
21878
|
+
const _lazy_jP59VB = () => import('../routes/api/project/routines.get.mjs');
|
|
21879
|
+
const _lazy_do1380 = () => import('../routes/api/project/settings.get.mjs');
|
|
21880
|
+
const _lazy_6EuDYh = () => import('../routes/api/project/thread.get.mjs');
|
|
21881
|
+
const _lazy_qZGRKl = () => import('../routes/api/project/thread.post.mjs');
|
|
20298
21882
|
const _lazy_wxPObX = () => import('../routes/api/prompt.post.mjs');
|
|
20299
21883
|
const _lazy_gHqHgE = () => import('../routes/api/push/quiet.post.mjs');
|
|
20300
21884
|
const _lazy_vXOoN7 = () => import('../routes/api/push/subscribe.post.mjs');
|
|
@@ -20316,6 +21900,8 @@ const _lazy_TqhtLZ = () => import('../routes/api/space.post.mjs');
|
|
|
20316
21900
|
const _lazy_oeuOKk = () => import('../routes/api/state.get.mjs');
|
|
20317
21901
|
const _lazy_ecbrXA = () => import('../routes/api/unqueue.post.mjs');
|
|
20318
21902
|
const _lazy_AUmWqe = () => import('../routes/api/update.get.mjs');
|
|
21903
|
+
const _lazy_1DfWY6 = () => import('../routes/api/update.post.mjs');
|
|
21904
|
+
const _lazy_lxIhkr = () => import('../routes/api/update/status.get.mjs');
|
|
20319
21905
|
const _lazy_5f22vB = () => import('../routes/api/upload.post.mjs');
|
|
20320
21906
|
const _lazy_9iwHBL = () => import('../routes/api/worktrees.get.mjs');
|
|
20321
21907
|
const _lazy_IA8rJi = () => import('../routes/api/worktrees/remove.post.mjs');
|
|
@@ -20324,7 +21910,6 @@ const _lazy_2a3aHZ = () => import('../routes/icons/_name_.get.mjs');
|
|
|
20324
21910
|
const _lazy_MpH2Yl = () => import('../routes/uploads/_...name_.get.mjs');
|
|
20325
21911
|
const _lazy_hSqSXW = () => import('../routes/ws/_..._.mjs');
|
|
20326
21912
|
const _lazy_N9wZe_ = () => import('../routes/ws/events.mjs');
|
|
20327
|
-
const _lazy_CXgyQE = () => import('../routes/ws/mirror.mjs');
|
|
20328
21913
|
const _lazy_Stovcz = () => import('../routes/ws/term.mjs');
|
|
20329
21914
|
const _lazy_9gHabX = () => import('../routes/renderer.mjs');
|
|
20330
21915
|
|
|
@@ -20344,6 +21929,7 @@ const handlers = [
|
|
|
20344
21929
|
{ route: '/api/auth/register/options', handler: _lazy_7O3TsZ, lazy: true, middleware: false, method: "post" },
|
|
20345
21930
|
{ route: '/api/auth/register/verify', handler: _lazy_fQByfS, lazy: true, middleware: false, method: "post" },
|
|
20346
21931
|
{ route: '/api/auth/status', handler: _lazy_jcfnWB, lazy: true, middleware: false, method: "get" },
|
|
21932
|
+
{ route: '/api/board', handler: _lazy_DwN9dC, lazy: true, middleware: false, method: "get" },
|
|
20347
21933
|
{ route: '/api/changes', handler: _lazy_WhNb61, lazy: true, middleware: false, method: "get" },
|
|
20348
21934
|
{ route: '/api/chat', handler: _lazy_hdSKYJ, lazy: true, middleware: false, method: "get" },
|
|
20349
21935
|
{ route: '/api/chat/image', handler: _lazy_WoSEa6, lazy: true, middleware: false, method: "get" },
|
|
@@ -20355,11 +21941,16 @@ const handlers = [
|
|
|
20355
21941
|
{ route: '/api/commands', handler: _lazy_onTn85, lazy: true, middleware: false, method: "get" },
|
|
20356
21942
|
{ route: '/api/config', handler: _lazy_XuUgLO, lazy: true, middleware: false, method: "get" },
|
|
20357
21943
|
{ route: '/api/dirs', handler: _lazy_y_su18, lazy: true, middleware: false, method: "get" },
|
|
21944
|
+
{ route: '/api/dismiss-stopped', handler: _lazy_l7QdCS, lazy: true, middleware: false, method: "post" },
|
|
20358
21945
|
{ route: '/api/dismiss', handler: _lazy_Z6oqjB, lazy: true, middleware: false, method: "post" },
|
|
20359
21946
|
{ route: '/api/effort', handler: _lazy_2uy6Z9, lazy: true, middleware: false, method: "post" },
|
|
20360
21947
|
{ route: '/api/efforts', handler: _lazy_7FhJOq, lazy: true, middleware: false, method: "get" },
|
|
20361
21948
|
{ route: '/api/file', handler: _lazy_Wg_MPT, lazy: true, middleware: false, method: "post" },
|
|
21949
|
+
{ route: '/api/files', handler: _lazy_vy5jzJ, lazy: true, middleware: false, method: "get" },
|
|
21950
|
+
{ route: '/api/files/download', handler: _lazy_iD5EkU, lazy: true, middleware: false, method: "get" },
|
|
21951
|
+
{ route: '/api/files/read', handler: _lazy_TecKdl, lazy: true, middleware: false, method: "get" },
|
|
20362
21952
|
{ route: '/api/gitroot', handler: _lazy_kEWyNy, lazy: true, middleware: false, method: "get" },
|
|
21953
|
+
{ route: '/api/health', handler: _lazy_1Vqvr9, lazy: true, middleware: false, method: "get" },
|
|
20363
21954
|
{ route: '/api/input', handler: _lazy_U4OEhz, lazy: true, middleware: false, method: "post" },
|
|
20364
21955
|
{ route: '/api/interrupt', handler: _lazy_PDyMr9, lazy: true, middleware: false, method: "post" },
|
|
20365
21956
|
{ route: '/api/isgit', handler: _lazy_mtPGaX, lazy: true, middleware: false, method: "get" },
|
|
@@ -20383,8 +21974,15 @@ const handlers = [
|
|
|
20383
21974
|
{ route: '/api/plugins/projects', handler: _lazy_zH9tge, lazy: true, middleware: false, method: "post" },
|
|
20384
21975
|
{ route: '/api/plugins/thread-limits', handler: _lazy_qE4HMW, lazy: true, middleware: false, method: "get" },
|
|
20385
21976
|
{ route: '/api/plugins/thread-limits', handler: _lazy_ByJj04, lazy: true, middleware: false, method: "post" },
|
|
21977
|
+
{ route: '/api/project/inbox', handler: _lazy_VZ7yeT, lazy: true, middleware: false, method: "get" },
|
|
21978
|
+
{ route: '/api/project/inbox', handler: _lazy_wq4Jn3, lazy: true, middleware: false, method: "post" },
|
|
20386
21979
|
{ route: '/api/project', handler: _lazy_WLg2Uu, lazy: true, middleware: false, method: "get" },
|
|
21980
|
+
{ route: '/api/project/memory', handler: _lazy_LkLunk, lazy: true, middleware: false, method: "get" },
|
|
20387
21981
|
{ route: '/api/project/report', handler: _lazy_sQe2ZF, lazy: true, middleware: false, method: "get" },
|
|
21982
|
+
{ route: '/api/project/routines', handler: _lazy_jP59VB, lazy: true, middleware: false, method: "get" },
|
|
21983
|
+
{ route: '/api/project/settings', handler: _lazy_do1380, lazy: true, middleware: false, method: "get" },
|
|
21984
|
+
{ route: '/api/project/thread', handler: _lazy_6EuDYh, lazy: true, middleware: false, method: "get" },
|
|
21985
|
+
{ route: '/api/project/thread', handler: _lazy_qZGRKl, lazy: true, middleware: false, method: "post" },
|
|
20388
21986
|
{ route: '/api/prompt', handler: _lazy_wxPObX, lazy: true, middleware: false, method: "post" },
|
|
20389
21987
|
{ route: '/api/push/quiet', handler: _lazy_gHqHgE, lazy: true, middleware: false, method: "post" },
|
|
20390
21988
|
{ route: '/api/push/subscribe', handler: _lazy_vXOoN7, lazy: true, middleware: false, method: "post" },
|
|
@@ -20406,6 +22004,8 @@ const handlers = [
|
|
|
20406
22004
|
{ route: '/api/state', handler: _lazy_oeuOKk, lazy: true, middleware: false, method: "get" },
|
|
20407
22005
|
{ route: '/api/unqueue', handler: _lazy_ecbrXA, lazy: true, middleware: false, method: "post" },
|
|
20408
22006
|
{ route: '/api/update', handler: _lazy_AUmWqe, lazy: true, middleware: false, method: "get" },
|
|
22007
|
+
{ route: '/api/update', handler: _lazy_1DfWY6, lazy: true, middleware: false, method: "post" },
|
|
22008
|
+
{ route: '/api/update/status', handler: _lazy_lxIhkr, lazy: true, middleware: false, method: "get" },
|
|
20409
22009
|
{ route: '/api/upload', handler: _lazy_5f22vB, lazy: true, middleware: false, method: "post" },
|
|
20410
22010
|
{ route: '/api/worktrees', handler: _lazy_9iwHBL, lazy: true, middleware: false, method: "get" },
|
|
20411
22011
|
{ route: '/api/worktrees/remove', handler: _lazy_IA8rJi, lazy: true, middleware: false, method: "post" },
|
|
@@ -20414,7 +22014,6 @@ const handlers = [
|
|
|
20414
22014
|
{ route: '/uploads/**:name', handler: _lazy_MpH2Yl, lazy: true, middleware: false, method: "get" },
|
|
20415
22015
|
{ route: '/ws/**', handler: _lazy_hSqSXW, lazy: true, middleware: false, method: undefined },
|
|
20416
22016
|
{ route: '/ws/events', handler: _lazy_N9wZe_, lazy: true, middleware: false, method: undefined },
|
|
20417
|
-
{ route: '/ws/mirror', handler: _lazy_CXgyQE, lazy: true, middleware: false, method: undefined },
|
|
20418
22017
|
{ route: '/ws/term', handler: _lazy_Stovcz, lazy: true, middleware: false, method: undefined },
|
|
20419
22018
|
{ route: '/__nuxt_error', handler: _lazy_9gHabX, lazy: true, middleware: false, method: undefined },
|
|
20420
22019
|
{ route: '/__nuxt_island/**', handler: _SxA8c9, lazy: false, middleware: false, method: undefined },
|
|
@@ -20877,5 +22476,5 @@ setupGracefulShutdown(listener, nitroApp);
|
|
|
20877
22476
|
}
|
|
20878
22477
|
const nodeServer = {};
|
|
20879
22478
|
|
|
20880
|
-
export {
|
|
22479
|
+
export { sleep$1 as $, ATTACH_MAX as A, isProjectThread as B, uploadNames as C, machineOfPane as D, fmt as E, takenBackOf as F, transcripts as G, HerdrError as H, chatPasted as I, withoutTakenBack as J, withSentText as K, currentModel as L, sendImage as M, keysFor as N, parseChoices as O, PANE_RE as P, parseOmpField as Q, withPaneLock as R, selfUpdates as S, restarting as T, UPLOAD_DIR as U, updateChoices as V, UPDATE_LABELS as W, updateRestartPlan as X, restarts as Y, usesTerminalException as Z, sameQuestion as _, defineApi as a, validLimit as a$, MACHINE_KEY_RE as a0, getMachine as a1, installClaudeStatusline as a2, codexStatus as a3, paneForeground as a4, codexMenuOpen as a5, stopAgent as a6, terminalUpdateLine as a7, waitFor as a8, terminalUpdateDone as a9, listEfforts as aA, saveUpload as aB, setResponseHeader as aC, underHome as aD, isSlashCommand as aE, addQueued as aF, interruptAgent as aG, takeBackFromClaude as aH, takeBack as aI, isGitRepo as aJ, projectFolderKinds as aK, baseMachines as aL, DATA_DIR as aM, renameMachine as aN, readMenu as aO, searchKeys as aP, findEntry as aQ, stepToward as aR, clickMovesOnly as aS, setModel as aT, listModels as aU, TOP as aV, HOST_LABEL as aW, phoneAccess as aX, phoneStatus as aY, phoneAction as aZ, limitMachineKeys as a_, startAgent as aa, log as ab, RESTARTABLE as ac, claudeFooterMode as ad, planRestart as ae, builtinCommands as af, slashCommands as ag, HOME as ah, localMachine as ai, readSubs as aj, vapidPublicKey as ak, pushReady as al, machineConfigs as am, recentDirsWithKinds as an, installedAgentKinds as ao, IN_DOCKER as ap, HOST_OPEN_TARGET as aq, HOST_OPEN_KEY as ar, PANE_RE$1 as as, splitId as at, HOST_OPEN_USER as au, listDirs as av, dismissStopped as aw, closePanel as ax, setEffort as ay, refreshModel as az, screenChoices as b, getResponseStatusText as b$, readThreadLimits as b0, writeThreadLimits as b1, closeTrailingMention as b2, keepPastes as b3, hasHeld as b4, INPUT_STATES as b5, inputVisible as b6, shouldHold as b7, sendPrompt as b8, agentPrompt as b9, cancelQueued as bA, installMode as bB, oneTapMode as bC, updateCommand as bD, compareVersions as bE, updateRunning as bF, allMachines as bG, recentDirs as bH, joinId as bI, useStorage as bJ, getRouterParam as bK, readUpload as bL, defineWebSocketHandler as bM, eventClients as bN, getStateJson as bO, hostAllowed as bP, sameOrigin as bQ, openTerm as bR, useRuntimeConfig as bS, encodePath as bT, joinRelativeURL as bU, defineRenderHandler as bV, createError$1 as bW, destr as bX, getRouteRules as bY, relative as bZ, joinURL as b_, isBusyError as ba, HOLD_AGENTS as bb, pendingPrompts as bc, parseQuiet as bd, writeGlobalQuiet as be, writeSubs as bf, quietActive as bg, readGlobalQuiet as bh, validPushSubscription as bi, replaceSubscription as bj, pushSend as bk, readQuotas as bl, retryQueued as bm, parseWaitScreen as bn, settingsTabs as bo, tabsOfRow as bp, classifyScreen as bq, parseMenu as br, hasTranscript as bs, SEARCH_DEADLINE_MS as bt, SEARCH_MAX_RESULTS as bu, spaceTitle as bv, markSeen as bw, listSessions as bx, openSession as by, PANE_DIRECTIONS as bz, createAgent as c, getResponseStatus as c0, useNitroApp as c1, nodeServer as c2, defineEventHandler as d, watchScreen as e, findPane as f, choicesCache as g, herdr as h, getQuery as i, saveAttachment as j, auth as k, localMachineLabel as l, getState as m, projectOf as n, isCoordinator as o, poll as p, paneBaseMachine as q, reqOf as r, sendError as s, pickCoordinator as t, currentMachineSlots as u, machineFor as v, withOmpTab as w, herdrOn as x, HERDR_CHILD_ENV as y, HERDR_BIN as z };
|
|
20881
22480
|
//# sourceMappingURL=nitro.mjs.map
|