@poolzin/pool-bot 2026.2.0 → 2026.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +118 -0
- package/README-header.png +0 -0
- package/dist/agents/bash-tools.exec.js +76 -25
- package/dist/agents/cli-runner/helpers.js +9 -11
- package/dist/agents/context.js +1 -1
- package/dist/agents/identity.js +47 -7
- package/dist/agents/memory-search.js +25 -8
- package/dist/agents/model-catalog.js +1 -1
- package/dist/agents/model-selection.js +21 -0
- package/dist/agents/pi-embedded-block-chunker.js +117 -42
- package/dist/agents/pi-embedded-helpers/errors.js +183 -78
- package/dist/agents/pi-embedded-helpers.js +1 -1
- package/dist/agents/pi-embedded-runner/compact.js +8 -10
- package/dist/agents/pi-embedded-runner/model.js +62 -3
- package/dist/agents/pi-embedded-runner/run/attempt.js +21 -11
- package/dist/agents/pi-embedded-runner/run.js +199 -46
- package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
- package/dist/agents/pi-embedded-subscribe.js +118 -29
- package/dist/agents/pi-tools.js +10 -5
- package/dist/agents/poolbot-tools.js +15 -10
- package/dist/agents/sandbox-paths.js +31 -0
- package/dist/agents/session-tool-result-guard.js +94 -15
- package/dist/agents/shell-utils.js +51 -0
- package/dist/agents/skills/bundled-context.js +23 -0
- package/dist/agents/skills/bundled-dir.js +41 -7
- package/dist/agents/skills-install.js +60 -23
- package/dist/agents/subagent-announce.js +79 -34
- package/dist/agents/tool-policy.conformance.js +14 -0
- package/dist/agents/tool-policy.js +24 -0
- package/dist/agents/tools/cron-tool.js +166 -19
- package/dist/agents/tools/discord-actions-presence.js +78 -0
- package/dist/agents/tools/image-tool.js +1 -1
- package/dist/agents/tools/message-tool.js +56 -2
- package/dist/agents/tools/sessions-history-tool.js +69 -1
- package/dist/agents/tools/web-search.js +211 -42
- package/dist/agents/usage.js +23 -1
- package/dist/agents/workspace-run.js +67 -0
- package/dist/agents/workspace-templates.js +44 -0
- package/dist/auto-reply/command-auth.js +121 -6
- package/dist/auto-reply/envelope.js +74 -82
- package/dist/auto-reply/reply/commands-compact.js +1 -0
- package/dist/auto-reply/reply/commands-context-report.js +1 -0
- package/dist/auto-reply/reply/commands-context.js +1 -0
- package/dist/auto-reply/reply/commands-models.js +107 -60
- package/dist/auto-reply/reply/commands-ptt.js +171 -0
- package/dist/auto-reply/reply/get-reply-run.js +2 -1
- package/dist/auto-reply/reply/inbound-context.js +5 -1
- package/dist/auto-reply/reply/mentions.js +1 -1
- package/dist/auto-reply/reply/model-selection.js +3 -3
- package/dist/auto-reply/thinking.js +88 -43
- package/dist/browser/bridge-server.js +13 -0
- package/dist/browser/cdp.helpers.js +38 -24
- package/dist/browser/client-fetch.js +50 -7
- package/dist/browser/config.js +1 -10
- package/dist/browser/extension-relay.js +101 -40
- package/dist/browser/pw-ai.js +1 -1
- package/dist/browser/pw-session.js +143 -8
- package/dist/browser/pw-tools-core.interactions.js +125 -27
- package/dist/browser/pw-tools-core.responses.js +1 -1
- package/dist/browser/pw-tools-core.state.js +1 -1
- package/dist/browser/routes/agent.act.js +86 -41
- package/dist/browser/routes/dispatcher.js +4 -4
- package/dist/browser/screenshot.js +1 -1
- package/dist/browser/server.js +13 -0
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/index.html +28 -28
- package/dist/channels/reply-prefix.js +8 -1
- package/dist/cli/cron-cli/register.cron-add.js +61 -40
- package/dist/cli/cron-cli/register.cron-edit.js +60 -34
- package/dist/cli/cron-cli/shared.js +56 -41
- package/dist/cli/dns-cli.js +26 -14
- package/dist/cli/gateway-cli/register.js +37 -19
- package/dist/cli/memory-cli.js +5 -5
- package/dist/cli/parse-bytes.js +37 -0
- package/dist/cli/update-cli.js +173 -52
- package/dist/commands/agent.js +1 -0
- package/dist/commands/auth-choice.apply.oauth.js +1 -1
- package/dist/commands/doctor-config-flow.js +61 -5
- package/dist/commands/doctor-state-migrations.js +1 -1
- package/dist/commands/health.js +1 -1
- package/dist/commands/model-allowlist.js +29 -0
- package/dist/commands/model-picker.js +2 -1
- package/dist/commands/models/list.registry.js +1 -1
- package/dist/commands/models/list.status-command.js +43 -23
- package/dist/commands/models/shared.js +15 -0
- package/dist/commands/onboard-custom.js +384 -0
- package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
- package/dist/commands/onboard-non-interactive/local/auth-choice.js +6 -3
- package/dist/commands/onboard-skills.js +63 -38
- package/dist/commands/openai-model-default.js +41 -0
- package/dist/compat/legacy-names.js +2 -0
- package/dist/config/defaults.js +3 -2
- package/dist/config/paths.js +136 -35
- package/dist/config/plugin-auto-enable.js +21 -5
- package/dist/config/redact-snapshot.js +153 -0
- package/dist/config/schema.field-metadata.js +590 -0
- package/dist/config/schema.js +2 -2
- package/dist/config/sessions/store.js +291 -23
- package/dist/config/zod-schema.agent-defaults.js +3 -0
- package/dist/config/zod-schema.agent-runtime.js +13 -2
- package/dist/config/zod-schema.providers-core.js +142 -0
- package/dist/config/zod-schema.session.js +3 -0
- package/dist/control-ui/assets/{index-CIRDm-Lu.css → index-CSfXd2LO.css} +1 -1
- package/dist/control-ui/assets/{index-CmNMuoem.js → index-HRr1grwl.js} +446 -413
- package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -0
- package/dist/control-ui/index.html +4 -4
- package/dist/cron/delivery.js +57 -0
- package/dist/cron/isolated-agent/delivery-target.js +18 -3
- package/dist/cron/isolated-agent/helpers.js +22 -5
- package/dist/cron/isolated-agent/run.js +172 -63
- package/dist/cron/isolated-agent/session.js +2 -0
- package/dist/cron/normalize.js +356 -28
- package/dist/cron/parse.js +10 -5
- package/dist/cron/run-log.js +35 -10
- package/dist/cron/schedule.js +41 -6
- package/dist/cron/service/jobs.js +208 -35
- package/dist/cron/service/ops.js +72 -16
- package/dist/cron/service/state.js +2 -0
- package/dist/cron/service/store.js +386 -14
- package/dist/cron/service/timer.js +390 -147
- package/dist/cron/session-reaper.js +86 -0
- package/dist/cron/store.js +23 -8
- package/dist/cron/validate-timestamp.js +43 -0
- package/dist/discord/monitor/agent-components.js +438 -0
- package/dist/discord/monitor/allow-list.js +28 -5
- package/dist/discord/monitor/gateway-registry.js +29 -0
- package/dist/discord/monitor/native-command.js +44 -23
- package/dist/discord/monitor/sender-identity.js +45 -0
- package/dist/discord/pluralkit.js +27 -0
- package/dist/discord/send.outbound.js +92 -5
- package/dist/discord/send.shared.js +60 -23
- package/dist/discord/targets.js +84 -1
- package/dist/entry.js +15 -9
- package/dist/extensionAPI.js +8 -0
- package/dist/gateway/control-ui.js +8 -1
- package/dist/gateway/hooks-mapping.js +3 -0
- package/dist/gateway/hooks.js +65 -0
- package/dist/gateway/net.js +96 -31
- package/dist/gateway/node-command-policy.js +50 -15
- package/dist/gateway/origin-check.js +56 -0
- package/dist/gateway/protocol/client-info.js +9 -0
- package/dist/gateway/protocol/index.js +9 -2
- package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
- package/dist/gateway/protocol/schema/cron.js +22 -10
- package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
- package/dist/gateway/protocol/schema/sessions.js +12 -0
- package/dist/gateway/server/hooks.js +1 -1
- package/dist/gateway/server-broadcast.js +26 -9
- package/dist/gateway/server-chat.js +112 -23
- package/dist/gateway/server-discovery-runtime.js +10 -2
- package/dist/gateway/server-http.js +109 -11
- package/dist/gateway/server-methods/agent-timestamp.js +60 -0
- package/dist/gateway/server-methods/agents.js +321 -2
- package/dist/gateway/server-methods/usage.js +559 -16
- package/dist/gateway/server-runtime-state.js +22 -8
- package/dist/gateway/server-startup-memory.js +16 -0
- package/dist/gateway/server.impl.js +5 -1
- package/dist/gateway/session-utils.fs.js +23 -25
- package/dist/gateway/session-utils.js +20 -10
- package/dist/gateway/sessions-patch.js +7 -22
- package/dist/gateway/test-helpers.mocks.js +11 -7
- package/dist/gateway/test-helpers.server.js +35 -2
- package/dist/imessage/constants.js +2 -0
- package/dist/imessage/monitor/deliver.js +4 -1
- package/dist/imessage/monitor/monitor-provider.js +51 -1
- package/dist/infra/bonjour-discovery.js +131 -70
- package/dist/infra/control-ui-assets.js +134 -12
- package/dist/infra/errors.js +12 -0
- package/dist/infra/exec-approvals.js +266 -57
- package/dist/infra/format-time/format-datetime.js +79 -0
- package/dist/infra/format-time/format-duration.js +81 -0
- package/dist/infra/format-time/format-relative.js +80 -0
- package/dist/infra/heartbeat-runner.js +140 -49
- package/dist/infra/home-dir.js +54 -0
- package/dist/infra/net/fetch-guard.js +122 -0
- package/dist/infra/net/ssrf.js +65 -29
- package/dist/infra/outbound/abort.js +14 -0
- package/dist/infra/outbound/message-action-runner.js +77 -13
- package/dist/infra/outbound/outbound-session.js +143 -37
- package/dist/infra/poolbot-root.js +43 -1
- package/dist/infra/session-cost-usage.js +631 -41
- package/dist/infra/state-migrations.js +317 -47
- package/dist/infra/update-global.js +35 -0
- package/dist/infra/update-runner.js +149 -43
- package/dist/infra/warning-filter.js +65 -0
- package/dist/infra/widearea-dns.js +30 -9
- package/dist/logging/redact-identifier.js +12 -0
- package/dist/media/fetch.js +81 -58
- package/dist/media/store.js +2 -0
- package/dist/media-understanding/apply.js +403 -3
- package/dist/media-understanding/attachments.js +38 -27
- package/dist/media-understanding/defaults.js +16 -0
- package/dist/media-understanding/providers/deepgram/audio.js +22 -14
- package/dist/media-understanding/providers/google/audio.js +24 -17
- package/dist/media-understanding/providers/google/video.js +24 -17
- package/dist/media-understanding/providers/image.js +3 -3
- package/dist/media-understanding/providers/index.js +4 -1
- package/dist/media-understanding/providers/openai/audio.js +22 -14
- package/dist/media-understanding/providers/shared.js +16 -11
- package/dist/media-understanding/providers/zai/index.js +6 -0
- package/dist/media-understanding/runner.js +158 -90
- package/dist/memory/batch-voyage.js +277 -0
- package/dist/memory/embeddings-voyage.js +75 -0
- package/dist/memory/embeddings.js +28 -16
- package/dist/memory/internal.js +101 -18
- package/dist/memory/manager.js +154 -48
- package/dist/memory/search-manager.js +173 -0
- package/dist/memory/session-files.js +9 -3
- package/dist/node-host/runner.js +34 -24
- package/dist/node-host/with-timeout.js +27 -0
- package/dist/plugins/commands.js +5 -1
- package/dist/plugins/config-state.js +86 -7
- package/dist/plugins/source-display.js +51 -0
- package/dist/process/exec.js +20 -2
- package/dist/routing/resolve-route.js +12 -0
- package/dist/routing/session-key.js +15 -0
- package/dist/runtime.js +2 -0
- package/dist/security/audit-extra.async.js +601 -0
- package/dist/security/audit-extra.js +2 -830
- package/dist/security/audit-extra.sync.js +505 -0
- package/dist/security/channel-metadata.js +34 -0
- package/dist/security/external-content.js +88 -6
- package/dist/security/skill-scanner.js +330 -0
- package/dist/sessions/session-key-utils.js +7 -0
- package/dist/signal/monitor/event-handler.js +80 -1
- package/dist/slack/monitor/media.js +85 -15
- package/dist/tailscale/detect.js +1 -2
- package/dist/telegram/bot/helpers.js +109 -28
- package/dist/telegram/bot-handlers.js +144 -3
- package/dist/telegram/bot-message-context.js +37 -10
- package/dist/telegram/bot-message-dispatch.js +54 -17
- package/dist/telegram/bot-native-commands.js +86 -29
- package/dist/telegram/bot.js +30 -29
- package/dist/telegram/model-buttons.js +163 -0
- package/dist/telegram/monitor.js +110 -85
- package/dist/telegram/send.js +129 -47
- package/dist/terminal/restore.js +45 -0
- package/dist/test-helpers/state-dir-env.js +16 -0
- package/dist/tts/tts.js +12 -6
- package/dist/tui/tui-session-actions.js +166 -54
- package/dist/utils/fetch-timeout.js +20 -0
- package/dist/utils/normalize-secret-input.js +19 -0
- package/dist/utils/transcript-tools.js +58 -0
- package/dist/utils.js +45 -14
- package/dist/version.js +42 -5
- package/dist/wizard/clack-prompter.js +9 -6
- package/extensions/googlechat/node_modules/.bin/poolbot +21 -0
- package/extensions/googlechat/package.json +2 -2
- package/extensions/line/node_modules/.bin/poolbot +21 -0
- package/extensions/line/package.json +1 -1
- package/extensions/matrix/node_modules/.bin/poolbot +21 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/memory-core/node_modules/.bin/poolbot +21 -0
- package/extensions/memory-core/package.json +4 -1
- package/extensions/twitch/node_modules/.bin/poolbot +21 -0
- package/extensions/twitch/package.json +1 -1
- package/package.json +183 -24
- package/dist/control-ui/assets/index-CmNMuoem.js.map +0 -1
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { runCommandWithTimeout } from "../process/exec.js";
|
|
2
|
-
import {
|
|
2
|
+
import { resolveWideAreaDiscoveryDomain } from "./widearea-dns.js";
|
|
3
3
|
const DEFAULT_TIMEOUT_MS = 2000;
|
|
4
|
-
const
|
|
4
|
+
const GATEWAY_SERVICE_TYPE = "_poolbot-gw._tcp";
|
|
5
5
|
function decodeDnsSdEscapes(value) {
|
|
6
6
|
let decoded = false;
|
|
7
7
|
const bytes = [];
|
|
8
8
|
let pending = "";
|
|
9
9
|
const flush = () => {
|
|
10
|
-
if (!pending)
|
|
10
|
+
if (!pending) {
|
|
11
11
|
return;
|
|
12
|
+
}
|
|
12
13
|
bytes.push(...Buffer.from(pending, "utf8"));
|
|
13
14
|
pending = "";
|
|
14
15
|
};
|
|
@@ -31,18 +32,21 @@ function decodeDnsSdEscapes(value) {
|
|
|
31
32
|
}
|
|
32
33
|
pending += ch;
|
|
33
34
|
}
|
|
34
|
-
if (!decoded)
|
|
35
|
+
if (!decoded) {
|
|
35
36
|
return value;
|
|
37
|
+
}
|
|
36
38
|
flush();
|
|
37
39
|
return Buffer.from(bytes).toString("utf8");
|
|
38
40
|
}
|
|
39
41
|
function isTailnetIPv4(address) {
|
|
40
42
|
const parts = address.split(".");
|
|
41
|
-
if (parts.length !== 4)
|
|
43
|
+
if (parts.length !== 4) {
|
|
42
44
|
return false;
|
|
45
|
+
}
|
|
43
46
|
const octets = parts.map((p) => Number.parseInt(p, 10));
|
|
44
|
-
if (octets.some((n) => !Number.isFinite(n) || n < 0 || n > 255))
|
|
47
|
+
if (octets.some((n) => !Number.isFinite(n) || n < 0 || n > 255)) {
|
|
45
48
|
return false;
|
|
49
|
+
}
|
|
46
50
|
// Tailscale IPv4 range: 100.64.0.0/10
|
|
47
51
|
const [a, b] = octets;
|
|
48
52
|
return a === 100 && b >= 64 && b <= 127;
|
|
@@ -59,8 +63,9 @@ function parseDigTxt(stdout) {
|
|
|
59
63
|
const tokens = [];
|
|
60
64
|
for (const raw of stdout.split("\n")) {
|
|
61
65
|
const line = raw.trim();
|
|
62
|
-
if (!line)
|
|
66
|
+
if (!line) {
|
|
63
67
|
continue;
|
|
68
|
+
}
|
|
64
69
|
const matches = Array.from(line.matchAll(/"([^"]*)"/g), (m) => m[1] ?? "");
|
|
65
70
|
for (const m of matches) {
|
|
66
71
|
const unescaped = m.replaceAll("\\\\", "\\").replaceAll('\\"', '"').replaceAll("\\n", "\n");
|
|
@@ -75,35 +80,43 @@ function parseDigSrv(stdout) {
|
|
|
75
80
|
.split("\n")
|
|
76
81
|
.map((l) => l.trim())
|
|
77
82
|
.find(Boolean);
|
|
78
|
-
if (!line)
|
|
83
|
+
if (!line) {
|
|
79
84
|
return null;
|
|
85
|
+
}
|
|
80
86
|
const parts = line.split(/\s+/).filter(Boolean);
|
|
81
|
-
if (parts.length < 4)
|
|
87
|
+
if (parts.length < 4) {
|
|
82
88
|
return null;
|
|
89
|
+
}
|
|
83
90
|
const port = Number.parseInt(parts[2] ?? "", 10);
|
|
84
91
|
const hostRaw = parts[3] ?? "";
|
|
85
|
-
if (!Number.isFinite(port) || port <= 0)
|
|
92
|
+
if (!Number.isFinite(port) || port <= 0) {
|
|
86
93
|
return null;
|
|
94
|
+
}
|
|
87
95
|
const host = hostRaw.replace(/\.$/, "");
|
|
88
|
-
if (!host)
|
|
96
|
+
if (!host) {
|
|
89
97
|
return null;
|
|
98
|
+
}
|
|
90
99
|
return { host, port };
|
|
91
100
|
}
|
|
92
101
|
function parseTailscaleStatusIPv4s(stdout) {
|
|
93
102
|
const parsed = stdout ? JSON.parse(stdout) : {};
|
|
94
103
|
const out = [];
|
|
95
104
|
const addIps = (value) => {
|
|
96
|
-
if (!value || typeof value !== "object")
|
|
105
|
+
if (!value || typeof value !== "object") {
|
|
97
106
|
return;
|
|
107
|
+
}
|
|
98
108
|
const ips = value.TailscaleIPs;
|
|
99
|
-
if (!Array.isArray(ips))
|
|
109
|
+
if (!Array.isArray(ips)) {
|
|
100
110
|
return;
|
|
111
|
+
}
|
|
101
112
|
for (const ip of ips) {
|
|
102
|
-
if (typeof ip !== "string")
|
|
113
|
+
if (typeof ip !== "string") {
|
|
103
114
|
continue;
|
|
115
|
+
}
|
|
104
116
|
const trimmed = ip.trim();
|
|
105
|
-
if (trimmed && isTailnetIPv4(trimmed))
|
|
117
|
+
if (trimmed && isTailnetIPv4(trimmed)) {
|
|
106
118
|
out.push(trimmed);
|
|
119
|
+
}
|
|
107
120
|
}
|
|
108
121
|
};
|
|
109
122
|
addIps(parsed.Self);
|
|
@@ -116,8 +129,9 @@ function parseTailscaleStatusIPv4s(stdout) {
|
|
|
116
129
|
return [...new Set(out)];
|
|
117
130
|
}
|
|
118
131
|
function parseIntOrNull(value) {
|
|
119
|
-
if (!value)
|
|
132
|
+
if (!value) {
|
|
120
133
|
return undefined;
|
|
134
|
+
}
|
|
121
135
|
const parsed = Number.parseInt(value, 10);
|
|
122
136
|
return Number.isFinite(parsed) ? parsed : undefined;
|
|
123
137
|
}
|
|
@@ -125,12 +139,14 @@ function parseTxtTokens(tokens) {
|
|
|
125
139
|
const txt = {};
|
|
126
140
|
for (const token of tokens) {
|
|
127
141
|
const idx = token.indexOf("=");
|
|
128
|
-
if (idx <= 0)
|
|
142
|
+
if (idx <= 0) {
|
|
129
143
|
continue;
|
|
144
|
+
}
|
|
130
145
|
const key = token.slice(0, idx).trim();
|
|
131
146
|
const value = decodeDnsSdEscapes(token.slice(idx + 1).trim());
|
|
132
|
-
if (!key)
|
|
147
|
+
if (!key) {
|
|
133
148
|
continue;
|
|
149
|
+
}
|
|
134
150
|
txt[key] = value;
|
|
135
151
|
}
|
|
136
152
|
return txt;
|
|
@@ -139,10 +155,12 @@ function parseDnsSdBrowse(stdout) {
|
|
|
139
155
|
const instances = new Set();
|
|
140
156
|
for (const raw of stdout.split("\n")) {
|
|
141
157
|
const line = raw.trim();
|
|
142
|
-
if (!line || !line.includes(
|
|
158
|
+
if (!line || !line.includes(GATEWAY_SERVICE_TYPE)) {
|
|
143
159
|
continue;
|
|
144
|
-
|
|
160
|
+
}
|
|
161
|
+
if (!line.includes("Add")) {
|
|
145
162
|
continue;
|
|
163
|
+
}
|
|
146
164
|
const match = line.match(/_poolbot-gw\._tcp\.?\s+(.+)$/);
|
|
147
165
|
if (match?.[1]) {
|
|
148
166
|
instances.add(decodeDnsSdEscapes(match[1].trim()));
|
|
@@ -156,8 +174,9 @@ function parseDnsSdResolve(stdout, instanceName) {
|
|
|
156
174
|
let txt = {};
|
|
157
175
|
for (const raw of stdout.split("\n")) {
|
|
158
176
|
const line = raw.trim();
|
|
159
|
-
if (!line)
|
|
177
|
+
if (!line) {
|
|
160
178
|
continue;
|
|
179
|
+
}
|
|
161
180
|
if (line.includes("can be reached at")) {
|
|
162
181
|
const match = line.match(/can be reached at\s+([^\s:]+):(\d+)/i);
|
|
163
182
|
if (match?.[1]) {
|
|
@@ -174,49 +193,59 @@ function parseDnsSdResolve(stdout, instanceName) {
|
|
|
174
193
|
}
|
|
175
194
|
}
|
|
176
195
|
beacon.txt = Object.keys(txt).length ? txt : undefined;
|
|
177
|
-
if (txt.displayName)
|
|
196
|
+
if (txt.displayName) {
|
|
178
197
|
beacon.displayName = decodeDnsSdEscapes(txt.displayName);
|
|
179
|
-
|
|
198
|
+
}
|
|
199
|
+
if (txt.lanHost) {
|
|
180
200
|
beacon.lanHost = txt.lanHost;
|
|
181
|
-
|
|
201
|
+
}
|
|
202
|
+
if (txt.tailnetDns) {
|
|
182
203
|
beacon.tailnetDns = txt.tailnetDns;
|
|
183
|
-
|
|
204
|
+
}
|
|
205
|
+
if (txt.cliPath) {
|
|
184
206
|
beacon.cliPath = txt.cliPath;
|
|
207
|
+
}
|
|
185
208
|
beacon.gatewayPort = parseIntOrNull(txt.gatewayPort);
|
|
186
209
|
beacon.sshPort = parseIntOrNull(txt.sshPort);
|
|
187
210
|
if (txt.gatewayTls) {
|
|
188
211
|
const raw = txt.gatewayTls.trim().toLowerCase();
|
|
189
212
|
beacon.gatewayTls = raw === "1" || raw === "true" || raw === "yes";
|
|
190
213
|
}
|
|
191
|
-
if (txt.gatewayTlsSha256)
|
|
214
|
+
if (txt.gatewayTlsSha256) {
|
|
192
215
|
beacon.gatewayTlsFingerprintSha256 = txt.gatewayTlsSha256;
|
|
193
|
-
|
|
216
|
+
}
|
|
217
|
+
if (txt.role) {
|
|
194
218
|
beacon.role = txt.role;
|
|
195
|
-
|
|
219
|
+
}
|
|
220
|
+
if (txt.transport) {
|
|
196
221
|
beacon.transport = txt.transport;
|
|
197
|
-
|
|
222
|
+
}
|
|
223
|
+
if (!beacon.displayName) {
|
|
198
224
|
beacon.displayName = decodedInstanceName;
|
|
225
|
+
}
|
|
199
226
|
return beacon;
|
|
200
227
|
}
|
|
201
228
|
async function discoverViaDnsSd(domain, timeoutMs, run) {
|
|
202
|
-
const browse = await run(["dns-sd", "-B",
|
|
229
|
+
const browse = await run(["dns-sd", "-B", GATEWAY_SERVICE_TYPE, domain], {
|
|
203
230
|
timeoutMs,
|
|
204
231
|
});
|
|
205
232
|
const instances = parseDnsSdBrowse(browse.stdout);
|
|
206
233
|
const results = [];
|
|
207
234
|
for (const instance of instances) {
|
|
208
|
-
const resolved = await run(["dns-sd", "-L", instance,
|
|
235
|
+
const resolved = await run(["dns-sd", "-L", instance, GATEWAY_SERVICE_TYPE, domain], {
|
|
209
236
|
timeoutMs,
|
|
210
237
|
});
|
|
211
238
|
const parsed = parseDnsSdResolve(resolved.stdout, instance);
|
|
212
|
-
if (parsed)
|
|
239
|
+
if (parsed) {
|
|
213
240
|
results.push({ ...parsed, domain });
|
|
241
|
+
}
|
|
214
242
|
}
|
|
215
243
|
return results;
|
|
216
244
|
}
|
|
217
245
|
async function discoverWideAreaViaTailnetDns(domain, timeoutMs, run) {
|
|
218
|
-
if (domain
|
|
246
|
+
if (!domain || domain === "local.") {
|
|
219
247
|
return [];
|
|
248
|
+
}
|
|
220
249
|
const startedAt = Date.now();
|
|
221
250
|
const remainingMs = () => timeoutMs - (Date.now() - startedAt);
|
|
222
251
|
const tailscaleCandidates = ["tailscale", "/Applications/Tailscale.app/Contents/MacOS/Tailscale"];
|
|
@@ -227,20 +256,23 @@ async function discoverWideAreaViaTailnetDns(domain, timeoutMs, run) {
|
|
|
227
256
|
timeoutMs: Math.max(1, Math.min(700, remainingMs())),
|
|
228
257
|
});
|
|
229
258
|
ips = parseTailscaleStatusIPv4s(res.stdout);
|
|
230
|
-
if (ips.length > 0)
|
|
259
|
+
if (ips.length > 0) {
|
|
231
260
|
break;
|
|
261
|
+
}
|
|
232
262
|
}
|
|
233
263
|
catch {
|
|
234
264
|
// ignore
|
|
235
265
|
}
|
|
236
266
|
}
|
|
237
|
-
if (ips.length === 0)
|
|
267
|
+
if (ips.length === 0) {
|
|
238
268
|
return [];
|
|
239
|
-
|
|
269
|
+
}
|
|
270
|
+
if (remainingMs() <= 0) {
|
|
240
271
|
return [];
|
|
272
|
+
}
|
|
241
273
|
// Keep scans bounded: this is a fallback and should not block long.
|
|
242
274
|
ips = ips.slice(0, 40);
|
|
243
|
-
const probeName =
|
|
275
|
+
const probeName = `${GATEWAY_SERVICE_TYPE}.${domain.replace(/\.$/, "")}`;
|
|
244
276
|
const concurrency = 6;
|
|
245
277
|
let nextIndex = 0;
|
|
246
278
|
let nameserver = null;
|
|
@@ -248,20 +280,24 @@ async function discoverWideAreaViaTailnetDns(domain, timeoutMs, run) {
|
|
|
248
280
|
const worker = async () => {
|
|
249
281
|
while (nameserver === null) {
|
|
250
282
|
const budget = remainingMs();
|
|
251
|
-
if (budget <= 0)
|
|
283
|
+
if (budget <= 0) {
|
|
252
284
|
return;
|
|
285
|
+
}
|
|
253
286
|
const i = nextIndex;
|
|
254
287
|
nextIndex += 1;
|
|
255
|
-
if (i >= ips.length)
|
|
288
|
+
if (i >= ips.length) {
|
|
256
289
|
return;
|
|
290
|
+
}
|
|
257
291
|
const ip = ips[i] ?? "";
|
|
258
|
-
if (!ip)
|
|
292
|
+
if (!ip) {
|
|
259
293
|
continue;
|
|
294
|
+
}
|
|
260
295
|
try {
|
|
261
296
|
const probe = await run(["dig", "+short", "+time=1", "+tries=1", `@${ip}`, probeName, "PTR"], { timeoutMs: Math.max(1, Math.min(250, budget)) });
|
|
262
297
|
const lines = parseDigShortLines(probe.stdout);
|
|
263
|
-
if (lines.length === 0)
|
|
298
|
+
if (lines.length === 0) {
|
|
264
299
|
continue;
|
|
300
|
+
}
|
|
265
301
|
nameserver = ip;
|
|
266
302
|
ptrs = lines;
|
|
267
303
|
return;
|
|
@@ -272,26 +308,31 @@ async function discoverWideAreaViaTailnetDns(domain, timeoutMs, run) {
|
|
|
272
308
|
}
|
|
273
309
|
};
|
|
274
310
|
await Promise.all(Array.from({ length: Math.min(concurrency, ips.length) }, () => worker()));
|
|
275
|
-
if (!nameserver || ptrs.length === 0)
|
|
311
|
+
if (!nameserver || ptrs.length === 0) {
|
|
276
312
|
return [];
|
|
277
|
-
|
|
313
|
+
}
|
|
314
|
+
if (remainingMs() <= 0) {
|
|
278
315
|
return [];
|
|
316
|
+
}
|
|
279
317
|
const nameserverArg = `@${String(nameserver)}`;
|
|
280
318
|
const results = [];
|
|
281
319
|
for (const ptr of ptrs) {
|
|
282
320
|
const budget = remainingMs();
|
|
283
|
-
if (budget <= 0)
|
|
321
|
+
if (budget <= 0) {
|
|
284
322
|
break;
|
|
323
|
+
}
|
|
285
324
|
const ptrName = ptr.trim().replace(/\.$/, "");
|
|
286
|
-
if (!ptrName)
|
|
325
|
+
if (!ptrName) {
|
|
287
326
|
continue;
|
|
327
|
+
}
|
|
288
328
|
const instanceName = ptrName.replace(/\.?_poolbot-gw\._tcp\..*$/, "");
|
|
289
329
|
const srv = await run(["dig", "+short", "+time=1", "+tries=1", nameserverArg, ptrName, "SRV"], {
|
|
290
330
|
timeoutMs: Math.max(1, Math.min(350, budget)),
|
|
291
331
|
}).catch(() => null);
|
|
292
332
|
const srvParsed = srv ? parseDigSrv(srv.stdout) : null;
|
|
293
|
-
if (!srvParsed)
|
|
333
|
+
if (!srvParsed) {
|
|
294
334
|
continue;
|
|
335
|
+
}
|
|
295
336
|
const txtBudget = remainingMs();
|
|
296
337
|
if (txtBudget <= 0) {
|
|
297
338
|
results.push({
|
|
@@ -324,12 +365,15 @@ async function discoverWideAreaViaTailnetDns(domain, timeoutMs, run) {
|
|
|
324
365
|
const raw = txtMap.gatewayTls.trim().toLowerCase();
|
|
325
366
|
beacon.gatewayTls = raw === "1" || raw === "true" || raw === "yes";
|
|
326
367
|
}
|
|
327
|
-
if (txtMap.gatewayTlsSha256)
|
|
368
|
+
if (txtMap.gatewayTlsSha256) {
|
|
328
369
|
beacon.gatewayTlsFingerprintSha256 = txtMap.gatewayTlsSha256;
|
|
329
|
-
|
|
370
|
+
}
|
|
371
|
+
if (txtMap.role) {
|
|
330
372
|
beacon.role = txtMap.role;
|
|
331
|
-
|
|
373
|
+
}
|
|
374
|
+
if (txtMap.transport) {
|
|
332
375
|
beacon.transport = txtMap.transport;
|
|
376
|
+
}
|
|
333
377
|
results.push(beacon);
|
|
334
378
|
}
|
|
335
379
|
return results;
|
|
@@ -339,12 +383,14 @@ function parseAvahiBrowse(stdout) {
|
|
|
339
383
|
let current = null;
|
|
340
384
|
for (const raw of stdout.split("\n")) {
|
|
341
385
|
const line = raw.trimEnd();
|
|
342
|
-
if (!line)
|
|
386
|
+
if (!line) {
|
|
343
387
|
continue;
|
|
344
|
-
|
|
345
|
-
|
|
388
|
+
}
|
|
389
|
+
if (line.startsWith("=") && line.includes(GATEWAY_SERVICE_TYPE)) {
|
|
390
|
+
if (current) {
|
|
346
391
|
results.push(current);
|
|
347
|
-
|
|
392
|
+
}
|
|
393
|
+
const marker = ` ${GATEWAY_SERVICE_TYPE}`;
|
|
348
394
|
const idx = line.indexOf(marker);
|
|
349
395
|
const left = idx >= 0 ? line.slice(0, idx).trim() : line;
|
|
350
396
|
const parts = left.split(/\s+/);
|
|
@@ -355,53 +401,64 @@ function parseAvahiBrowse(stdout) {
|
|
|
355
401
|
};
|
|
356
402
|
continue;
|
|
357
403
|
}
|
|
358
|
-
if (!current)
|
|
404
|
+
if (!current) {
|
|
359
405
|
continue;
|
|
406
|
+
}
|
|
360
407
|
const trimmed = line.trim();
|
|
361
408
|
if (trimmed.startsWith("hostname =")) {
|
|
362
409
|
const match = trimmed.match(/hostname\s*=\s*\[([^\]]+)\]/);
|
|
363
|
-
if (match?.[1])
|
|
410
|
+
if (match?.[1]) {
|
|
364
411
|
current.host = match[1];
|
|
412
|
+
}
|
|
365
413
|
continue;
|
|
366
414
|
}
|
|
367
415
|
if (trimmed.startsWith("port =")) {
|
|
368
416
|
const match = trimmed.match(/port\s*=\s*\[(\d+)\]/);
|
|
369
|
-
if (match?.[1])
|
|
417
|
+
if (match?.[1]) {
|
|
370
418
|
current.port = parseIntOrNull(match[1]);
|
|
419
|
+
}
|
|
371
420
|
continue;
|
|
372
421
|
}
|
|
373
422
|
if (trimmed.startsWith("txt =")) {
|
|
374
423
|
const tokens = Array.from(trimmed.matchAll(/"([^"]*)"/g), (m) => m[1]);
|
|
375
424
|
const txt = parseTxtTokens(tokens);
|
|
376
425
|
current.txt = Object.keys(txt).length ? txt : undefined;
|
|
377
|
-
if (txt.displayName)
|
|
426
|
+
if (txt.displayName) {
|
|
378
427
|
current.displayName = txt.displayName;
|
|
379
|
-
|
|
428
|
+
}
|
|
429
|
+
if (txt.lanHost) {
|
|
380
430
|
current.lanHost = txt.lanHost;
|
|
381
|
-
|
|
431
|
+
}
|
|
432
|
+
if (txt.tailnetDns) {
|
|
382
433
|
current.tailnetDns = txt.tailnetDns;
|
|
383
|
-
|
|
434
|
+
}
|
|
435
|
+
if (txt.cliPath) {
|
|
384
436
|
current.cliPath = txt.cliPath;
|
|
437
|
+
}
|
|
385
438
|
current.gatewayPort = parseIntOrNull(txt.gatewayPort);
|
|
386
439
|
current.sshPort = parseIntOrNull(txt.sshPort);
|
|
387
440
|
if (txt.gatewayTls) {
|
|
388
441
|
const raw = txt.gatewayTls.trim().toLowerCase();
|
|
389
442
|
current.gatewayTls = raw === "1" || raw === "true" || raw === "yes";
|
|
390
443
|
}
|
|
391
|
-
if (txt.gatewayTlsSha256)
|
|
444
|
+
if (txt.gatewayTlsSha256) {
|
|
392
445
|
current.gatewayTlsFingerprintSha256 = txt.gatewayTlsSha256;
|
|
393
|
-
|
|
446
|
+
}
|
|
447
|
+
if (txt.role) {
|
|
394
448
|
current.role = txt.role;
|
|
395
|
-
|
|
449
|
+
}
|
|
450
|
+
if (txt.transport) {
|
|
396
451
|
current.transport = txt.transport;
|
|
452
|
+
}
|
|
397
453
|
}
|
|
398
454
|
}
|
|
399
|
-
if (current)
|
|
455
|
+
if (current) {
|
|
400
456
|
results.push(current);
|
|
457
|
+
}
|
|
401
458
|
return results;
|
|
402
459
|
}
|
|
403
460
|
async function discoverViaAvahi(domain, timeoutMs, run) {
|
|
404
|
-
const args = ["avahi-browse", "-rt",
|
|
461
|
+
const args = ["avahi-browse", "-rt", GATEWAY_SERVICE_TYPE];
|
|
405
462
|
if (domain && domain !== "local.") {
|
|
406
463
|
// avahi-browse wants a plain domain (no trailing dot)
|
|
407
464
|
args.push("-d", domain.replace(/\.$/, ""));
|
|
@@ -416,8 +473,10 @@ export async function discoverGatewayBeacons(opts = {}) {
|
|
|
416
473
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
417
474
|
const platform = opts.platform ?? process.platform;
|
|
418
475
|
const run = opts.run ?? runCommandWithTimeout;
|
|
476
|
+
const wideAreaDomain = resolveWideAreaDiscoveryDomain({ configDomain: opts.wideAreaDomain });
|
|
419
477
|
const domainsRaw = Array.isArray(opts.domains) ? opts.domains : [];
|
|
420
|
-
const
|
|
478
|
+
const defaultDomains = ["local.", ...(wideAreaDomain ? [wideAreaDomain] : [])];
|
|
479
|
+
const domains = (domainsRaw.length > 0 ? domainsRaw : defaultDomains)
|
|
421
480
|
.map((d) => String(d).trim())
|
|
422
481
|
.filter(Boolean)
|
|
423
482
|
.map((d) => (d.endsWith(".") ? d : `${d}.`));
|
|
@@ -425,10 +484,12 @@ export async function discoverGatewayBeacons(opts = {}) {
|
|
|
425
484
|
if (platform === "darwin") {
|
|
426
485
|
const perDomain = await Promise.allSettled(domains.map(async (domain) => await discoverViaDnsSd(domain, timeoutMs, run)));
|
|
427
486
|
const discovered = perDomain.flatMap((r) => (r.status === "fulfilled" ? r.value : []));
|
|
428
|
-
const wantsWideArea = domains.includes(
|
|
429
|
-
const hasWideArea =
|
|
430
|
-
|
|
431
|
-
|
|
487
|
+
const wantsWideArea = wideAreaDomain ? domains.includes(wideAreaDomain) : false;
|
|
488
|
+
const hasWideArea = wideAreaDomain
|
|
489
|
+
? discovered.some((b) => b.domain === wideAreaDomain)
|
|
490
|
+
: false;
|
|
491
|
+
if (wantsWideArea && !hasWideArea && wideAreaDomain) {
|
|
492
|
+
const fallback = await discoverWideAreaViaTailnetDns(wideAreaDomain, timeoutMs, run).catch(() => []);
|
|
432
493
|
return [...discovered, ...fallback];
|
|
433
494
|
}
|
|
434
495
|
return discovered;
|
|
@@ -1,17 +1,34 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import { runCommandWithTimeout } from "../process/exec.js";
|
|
4
5
|
import { defaultRuntime } from "../runtime.js";
|
|
6
|
+
import { resolvePoolBotPackageRoot, resolvePoolBotPackageRootSync } from "./poolbot-root.js";
|
|
7
|
+
const CONTROL_UI_DIST_PATH_SEGMENTS = ["dist", "control-ui", "index.html"];
|
|
8
|
+
export function resolveControlUiDistIndexPathForRoot(root) {
|
|
9
|
+
return path.join(root, ...CONTROL_UI_DIST_PATH_SEGMENTS);
|
|
10
|
+
}
|
|
11
|
+
export async function resolveControlUiDistIndexHealth(opts = {}) {
|
|
12
|
+
const indexPath = opts.root
|
|
13
|
+
? resolveControlUiDistIndexPathForRoot(opts.root)
|
|
14
|
+
: await resolveControlUiDistIndexPath(opts.argv1 ?? process.argv[1]);
|
|
15
|
+
return {
|
|
16
|
+
indexPath,
|
|
17
|
+
exists: Boolean(indexPath && fs.existsSync(indexPath)),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
5
20
|
export function resolveControlUiRepoRoot(argv1 = process.argv[1]) {
|
|
6
|
-
if (!argv1)
|
|
21
|
+
if (!argv1) {
|
|
7
22
|
return null;
|
|
23
|
+
}
|
|
8
24
|
const normalized = path.resolve(argv1);
|
|
9
25
|
const parts = normalized.split(path.sep);
|
|
10
26
|
const srcIndex = parts.lastIndexOf("src");
|
|
11
27
|
if (srcIndex !== -1) {
|
|
12
28
|
const root = parts.slice(0, srcIndex).join(path.sep);
|
|
13
|
-
if (fs.existsSync(path.join(root, "ui", "vite.config.ts")))
|
|
29
|
+
if (fs.existsSync(path.join(root, "ui", "vite.config.ts"))) {
|
|
14
30
|
return root;
|
|
31
|
+
}
|
|
15
32
|
}
|
|
16
33
|
let dir = path.dirname(normalized);
|
|
17
34
|
for (let i = 0; i < 8; i++) {
|
|
@@ -20,36 +37,141 @@ export function resolveControlUiRepoRoot(argv1 = process.argv[1]) {
|
|
|
20
37
|
return dir;
|
|
21
38
|
}
|
|
22
39
|
const parent = path.dirname(dir);
|
|
23
|
-
if (parent === dir)
|
|
40
|
+
if (parent === dir) {
|
|
24
41
|
break;
|
|
42
|
+
}
|
|
25
43
|
dir = parent;
|
|
26
44
|
}
|
|
27
45
|
return null;
|
|
28
46
|
}
|
|
29
|
-
export function resolveControlUiDistIndexPath(argv1 = process.argv[1]) {
|
|
30
|
-
if (!argv1)
|
|
47
|
+
export async function resolveControlUiDistIndexPath(argv1 = process.argv[1]) {
|
|
48
|
+
if (!argv1) {
|
|
31
49
|
return null;
|
|
50
|
+
}
|
|
32
51
|
const normalized = path.resolve(argv1);
|
|
52
|
+
// Case 1: entrypoint is directly inside dist/ (e.g., dist/entry.js)
|
|
33
53
|
const distDir = path.dirname(normalized);
|
|
34
|
-
if (path.basename(distDir)
|
|
54
|
+
if (path.basename(distDir) === "dist") {
|
|
55
|
+
return path.join(distDir, "control-ui", "index.html");
|
|
56
|
+
}
|
|
57
|
+
const packageRoot = await resolvePoolBotPackageRoot({ argv1: normalized });
|
|
58
|
+
if (packageRoot) {
|
|
59
|
+
return path.join(packageRoot, "dist", "control-ui", "index.html");
|
|
60
|
+
}
|
|
61
|
+
// Fallback: traverse up and find package.json with name "poolbot" + dist/control-ui/index.html
|
|
62
|
+
// This handles global installs where path-based resolution might fail.
|
|
63
|
+
let dir = path.dirname(normalized);
|
|
64
|
+
for (let i = 0; i < 8; i++) {
|
|
65
|
+
const pkgJsonPath = path.join(dir, "package.json");
|
|
66
|
+
const indexPath = path.join(dir, "dist", "control-ui", "index.html");
|
|
67
|
+
if (fs.existsSync(pkgJsonPath) && fs.existsSync(indexPath)) {
|
|
68
|
+
try {
|
|
69
|
+
const raw = fs.readFileSync(pkgJsonPath, "utf-8");
|
|
70
|
+
const parsed = JSON.parse(raw);
|
|
71
|
+
if (parsed.name === "poolbot") {
|
|
72
|
+
return indexPath;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Invalid package.json, continue searching
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const parent = path.dirname(dir);
|
|
80
|
+
if (parent === dir) {
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
dir = parent;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
function addCandidate(candidates, value) {
|
|
88
|
+
if (!value) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
candidates.add(path.resolve(value));
|
|
92
|
+
}
|
|
93
|
+
export function resolveControlUiRootOverrideSync(rootOverride) {
|
|
94
|
+
const resolved = path.resolve(rootOverride);
|
|
95
|
+
try {
|
|
96
|
+
const stats = fs.statSync(resolved);
|
|
97
|
+
if (stats.isFile()) {
|
|
98
|
+
return path.basename(resolved) === "index.html" ? path.dirname(resolved) : null;
|
|
99
|
+
}
|
|
100
|
+
if (stats.isDirectory()) {
|
|
101
|
+
const indexPath = path.join(resolved, "index.html");
|
|
102
|
+
return fs.existsSync(indexPath) ? resolved : null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
35
106
|
return null;
|
|
36
|
-
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
export function resolveControlUiRootSync(opts = {}) {
|
|
111
|
+
const candidates = new Set();
|
|
112
|
+
const argv1 = opts.argv1 ?? process.argv[1];
|
|
113
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
114
|
+
const moduleDir = opts.moduleUrl ? path.dirname(fileURLToPath(opts.moduleUrl)) : null;
|
|
115
|
+
const argv1Dir = argv1 ? path.dirname(path.resolve(argv1)) : null;
|
|
116
|
+
const execDir = (() => {
|
|
117
|
+
try {
|
|
118
|
+
const execPath = opts.execPath ?? process.execPath;
|
|
119
|
+
return path.dirname(fs.realpathSync(execPath));
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
})();
|
|
125
|
+
const packageRoot = resolvePoolBotPackageRootSync({
|
|
126
|
+
argv1,
|
|
127
|
+
moduleUrl: opts.moduleUrl,
|
|
128
|
+
cwd,
|
|
129
|
+
});
|
|
130
|
+
// Packaged app: control-ui lives alongside the executable.
|
|
131
|
+
addCandidate(candidates, execDir ? path.join(execDir, "control-ui") : null);
|
|
132
|
+
if (moduleDir) {
|
|
133
|
+
// dist/<bundle>.js -> dist/control-ui
|
|
134
|
+
addCandidate(candidates, path.join(moduleDir, "control-ui"));
|
|
135
|
+
// dist/gateway/control-ui.js -> dist/control-ui
|
|
136
|
+
addCandidate(candidates, path.join(moduleDir, "../control-ui"));
|
|
137
|
+
// src/gateway/control-ui.ts -> dist/control-ui
|
|
138
|
+
addCandidate(candidates, path.join(moduleDir, "../../dist/control-ui"));
|
|
139
|
+
}
|
|
140
|
+
if (argv1Dir) {
|
|
141
|
+
// poolbot.mjs or dist/<bundle>.js
|
|
142
|
+
addCandidate(candidates, path.join(argv1Dir, "dist", "control-ui"));
|
|
143
|
+
addCandidate(candidates, path.join(argv1Dir, "control-ui"));
|
|
144
|
+
}
|
|
145
|
+
if (packageRoot) {
|
|
146
|
+
addCandidate(candidates, path.join(packageRoot, "dist", "control-ui"));
|
|
147
|
+
}
|
|
148
|
+
addCandidate(candidates, path.join(cwd, "dist", "control-ui"));
|
|
149
|
+
for (const dir of candidates) {
|
|
150
|
+
const indexPath = path.join(dir, "index.html");
|
|
151
|
+
if (fs.existsSync(indexPath)) {
|
|
152
|
+
return dir;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
37
156
|
}
|
|
38
157
|
function summarizeCommandOutput(text) {
|
|
39
158
|
const lines = text
|
|
40
159
|
.split(/\r?\n/g)
|
|
41
160
|
.map((l) => l.trim())
|
|
42
161
|
.filter(Boolean);
|
|
43
|
-
if (!lines.length)
|
|
162
|
+
if (!lines.length) {
|
|
44
163
|
return undefined;
|
|
164
|
+
}
|
|
45
165
|
const last = lines.at(-1);
|
|
46
|
-
if (!last)
|
|
166
|
+
if (!last) {
|
|
47
167
|
return undefined;
|
|
168
|
+
}
|
|
48
169
|
return last.length > 240 ? `${last.slice(0, 239)}…` : last;
|
|
49
170
|
}
|
|
50
171
|
export async function ensureControlUiAssetsBuilt(runtime = defaultRuntime, opts) {
|
|
51
|
-
const
|
|
52
|
-
|
|
172
|
+
const health = await resolveControlUiDistIndexHealth({ argv1: process.argv[1] });
|
|
173
|
+
const indexFromDist = health.indexPath;
|
|
174
|
+
if (health.exists) {
|
|
53
175
|
return { ok: true, built: false };
|
|
54
176
|
}
|
|
55
177
|
const repoRoot = resolveControlUiRepoRoot(process.argv[1]);
|
|
@@ -63,7 +185,7 @@ export async function ensureControlUiAssetsBuilt(runtime = defaultRuntime, opts)
|
|
|
63
185
|
message: `${hint}. Build them with \`pnpm ui:build\` (auto-installs UI deps).`,
|
|
64
186
|
};
|
|
65
187
|
}
|
|
66
|
-
const indexPath =
|
|
188
|
+
const indexPath = resolveControlUiDistIndexPathForRoot(repoRoot);
|
|
67
189
|
if (fs.existsSync(indexPath)) {
|
|
68
190
|
return { ok: true, built: false };
|
|
69
191
|
}
|
package/dist/infra/errors.js
CHANGED
|
@@ -8,6 +8,18 @@ export function extractErrorCode(err) {
|
|
|
8
8
|
return String(code);
|
|
9
9
|
return undefined;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Type guard for NodeJS.ErrnoException (any error with a `code` property).
|
|
13
|
+
*/
|
|
14
|
+
export function isErrno(err) {
|
|
15
|
+
return Boolean(err && typeof err === "object" && "code" in err);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Check if an error has a specific errno code.
|
|
19
|
+
*/
|
|
20
|
+
export function hasErrnoCode(err, code) {
|
|
21
|
+
return isErrno(err) && err.code === code;
|
|
22
|
+
}
|
|
11
23
|
export function formatErrorMessage(err) {
|
|
12
24
|
if (err instanceof Error) {
|
|
13
25
|
return err.message || err.name || "Error";
|