@clearlysh/beehaven 0.8.16
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/LICENSE +21 -0
- package/README.md +201 -0
- package/dist/__tests__/_cli-switch.js +105 -0
- package/dist/__tests__/agent-wrap-probe.js +136 -0
- package/dist/agent-attribution.js +192 -0
- package/dist/agent-identity-daemon.js +135 -0
- package/dist/agent-onboard.js +156 -0
- package/dist/agent-scope.js +79 -0
- package/dist/agent-sessions.js +370 -0
- package/dist/agent-state.js +482 -0
- package/dist/app-manifest.js +221 -0
- package/dist/app-signer.js +54 -0
- package/dist/app-signing.js +127 -0
- package/dist/audio-play.js +98 -0
- package/dist/avatar-protocol.js +25 -0
- package/dist/avatar-route.js +79 -0
- package/dist/bee/cloud-proxy.js +130 -0
- package/dist/blocked-actions.js +120 -0
- package/dist/build-channel.js +101 -0
- package/dist/buildings/daemon-workspace.js +475 -0
- package/dist/claude-bridge.js +310 -0
- package/dist/claude-credential-store.js +633 -0
- package/dist/clearly-sync.js +518 -0
- package/dist/clearly.js +616 -0
- package/dist/cli-state.js +334 -0
- package/dist/cli.js +6973 -0
- package/dist/commands/agent-skills.js +302 -0
- package/dist/commands/agent.js +1310 -0
- package/dist/commands/app.js +1123 -0
- package/dist/commands/auth.js +171 -0
- package/dist/commands/building-shell.js +392 -0
- package/dist/commands/canvas.js +932 -0
- package/dist/commands/claude.js +218 -0
- package/dist/commands/compliance.js +387 -0
- package/dist/commands/compose.js +507 -0
- package/dist/commands/composition.js +95 -0
- package/dist/commands/connect-session.js +409 -0
- package/dist/commands/conversations.js +160 -0
- package/dist/commands/costs.js +443 -0
- package/dist/commands/d1.js +253 -0
- package/dist/commands/daemon-cmd.js +162 -0
- package/dist/commands/deploy.js +164 -0
- package/dist/commands/design.js +617 -0
- package/dist/commands/dev-generate.js +125 -0
- package/dist/commands/dev.js +109 -0
- package/dist/commands/devices.js +135 -0
- package/dist/commands/explain-router.js +24 -0
- package/dist/commands/explain.js +882 -0
- package/dist/commands/explore.js +264 -0
- package/dist/commands/help.js +2418 -0
- package/dist/commands/init.js +408 -0
- package/dist/commands/install.js +129 -0
- package/dist/commands/library.js +229 -0
- package/dist/commands/marketplace.js +138 -0
- package/dist/commands/mcp-install.js +476 -0
- package/dist/commands/mention.js +257 -0
- package/dist/commands/omni.js +529 -0
- package/dist/commands/secrets.js +323 -0
- package/dist/commands/self-update.js +206 -0
- package/dist/commands/sheet.js +264 -0
- package/dist/commands/shell-output.js +309 -0
- package/dist/commands/shell.js +1048 -0
- package/dist/commands/shop.js +368 -0
- package/dist/commands/skills.js +311 -0
- package/dist/commands/slides.js +418 -0
- package/dist/commands/swarm.js +341 -0
- package/dist/commands/ticket.js +1301 -0
- package/dist/commands/tool.js +312 -0
- package/dist/commands/trash.js +106 -0
- package/dist/commands/treehouse.js +120 -0
- package/dist/commands/user.js +240 -0
- package/dist/commands/voice-tts.js +319 -0
- package/dist/commands/voice.js +166 -0
- package/dist/commands/watch-git.js +401 -0
- package/dist/commands/webhook.js +159 -0
- package/dist/commands/workspace.js +3354 -0
- package/dist/composition-repos.js +91 -0
- package/dist/daemon-avatar.js +130 -0
- package/dist/daemon-claude-code-auto-install.js +52 -0
- package/dist/daemon-client.js +363 -0
- package/dist/daemon-codex.js +349 -0
- package/dist/daemon-entry.js +101 -0
- package/dist/daemon-fetch-allowlist.js +194 -0
- package/dist/daemon-gemini.js +224 -0
- package/dist/daemon-mcp-clearly.js +353 -0
- package/dist/daemon-mcp-sandbox.js +127 -0
- package/dist/daemon-omni-company.js +338 -0
- package/dist/daemon-omni-hooks.js +375 -0
- package/dist/daemon-omni-identity.js +229 -0
- package/dist/daemon-omni-pipeline.js +251 -0
- package/dist/daemon-omni-policies.js +346 -0
- package/dist/daemon-omni-principal.js +580 -0
- package/dist/daemon-omni-tool-gate.js +116 -0
- package/dist/daemon-omni.js +1442 -0
- package/dist/daemon-path-transform.js +120 -0
- package/dist/daemon-write-sandbox.js +191 -0
- package/dist/daemon.js +4471 -0
- package/dist/dashboard.js +846 -0
- package/dist/dep-installer.js +286 -0
- package/dist/dev-mode.js +67 -0
- package/dist/doc-indexer.js +169 -0
- package/dist/e2e-crypto.js +94 -0
- package/dist/feature-flags.js +154 -0
- package/dist/file-browser.js +88 -0
- package/dist/first-party-hives.js +211 -0
- package/dist/folder-sync.js +226 -0
- package/dist/health.js +56 -0
- package/dist/ipc-auth.js +299 -0
- package/dist/ipc-client.js +495 -0
- package/dist/ipc-server.js +684 -0
- package/dist/keychain.js +224 -0
- package/dist/lib/admin-client.js +73 -0
- package/dist/lib/mobile-build-parser.js +156 -0
- package/dist/local-app-manager.js +660 -0
- package/dist/log-redaction.js +89 -0
- package/dist/logger.js +108 -0
- package/dist/manifest.js +143 -0
- package/dist/marketplace.js +793 -0
- package/dist/min-version-copy.js +51 -0
- package/dist/min-version.js +214 -0
- package/dist/public-surface.js +23 -0
- package/dist/relay-presets.js +89 -0
- package/dist/relay.js +2723 -0
- package/dist/repl.js +619 -0
- package/dist/setup-relay.js +363 -0
- package/dist/setup-service.js +536 -0
- package/dist/shared/bee-memory/index.js +6 -0
- package/dist/shared/bee-memory/types.js +682 -0
- package/dist/shared/claude-code/daemon-types.js +112 -0
- package/dist/shared/claude-code/index.js +2 -0
- package/dist/shared/claude-code/types.js +1 -0
- package/dist/shared/status/schema.js +133 -0
- package/dist/shell-session.js +198 -0
- package/dist/speak-lock.js +97 -0
- package/dist/spinner.js +57 -0
- package/dist/telemetry.js +166 -0
- package/dist/terminal/block-parser.js +207 -0
- package/dist/terminal/shell-integration.js +108 -0
- package/dist/transcript-tailer.js +215 -0
- package/dist/tts-qwen3.js +547 -0
- package/dist/types.js +4 -0
- package/dist/utm.js +38 -0
- package/dist/verb-surface.js +331 -0
- package/dist/version-utils.js +85 -0
- package/dist/watcher.js +207 -0
- package/package.json +88 -0
- package/python/qwen3_tts.py +230 -0
- package/scripts/stamp-build-channel.mjs +115 -0
- package/scripts/sync-installed.mjs +159 -0
- package/scripts/sync-skills.mjs +112 -0
- package/skills/README.md +24 -0
- package/skills/agent-doodle-space/SKILL.md +118 -0
- package/skills/agent-home/SKILL.md +98 -0
- package/skills/beehaven-cli/SKILL.md +144 -0
- package/skills/claude-shopper/README.md +90 -0
- package/skills/claude-shopper/SKILL.md +151 -0
- package/skills/clearly-agent/SKILL.md +183 -0
- package/skills/clearly-canvas/SKILL.md +222 -0
- package/skills/clearly-canvas/authoring.md +160 -0
- package/skills/clearly-canvas/communications.md +32 -0
- package/skills/clearly-canvas/composition-patterns.md +84 -0
- package/skills/clearly-canvas/example.mjs +78 -0
- package/skills/clearly-canvas/project-plans.md +156 -0
- package/skills/clearly-canvas/review.mjs +68 -0
- package/skills/clearly-workspace/SKILL.md +79 -0
- package/skills/clearly-workspace/survey.mjs +101 -0
- package/skills/codebase-map/SKILL.md +142 -0
- package/skills/design-craft/SKILL.md +300 -0
- package/skills/index.json +79 -0
- package/skills/layout-systems/SKILL.md +184 -0
- package/skills/pair-on-canvas/SKILL.md +92 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHY a minimum was published — a closed set, with the wording written in advance.
|
|
3
|
+
*
|
|
4
|
+
* ⚠⚠ THE PUBLISHED FILE CARRIES A KEY, NEVER A SENTENCE. Two reasons, and the second is the one
|
|
5
|
+
* that decides it:
|
|
6
|
+
*
|
|
7
|
+
* · `minimum.json` is served from a PUBLIC CDN. A free-text reason typed while arming a lockout
|
|
8
|
+
* during a security incident publishes the vulnerability to everyone who has not yet updated —
|
|
9
|
+
* the exact population the block exists to protect. A category cannot leak what it does not
|
|
10
|
+
* contain.
|
|
11
|
+
* · The copy is then written calmly, once, and can be improved without republishing a minimum.
|
|
12
|
+
* The alternative is composing user-facing prose at 2am while people are locked out.
|
|
13
|
+
*
|
|
14
|
+
* ⚠ THE FLOOR IS WHAT THIS RAISES. `reason` was optional, so the realistic blocked message was the
|
|
15
|
+
* bare "no longer supported" — true, and the least actionable sentence available. A category is
|
|
16
|
+
* REQUIRED when arming, so there is always a second line saying what kind of thing happened.
|
|
17
|
+
*
|
|
18
|
+
* ⚠⚠ AND IT MUST NOT BECOME A LABEL PEOPLE STOP BELIEVING. "Security update" on a protocol change
|
|
19
|
+
* is a false claim, and two of those teach everyone to discount the word on the day it is true.
|
|
20
|
+
* Each key below means only what it says; if a block does not fit one, that is a signal the block
|
|
21
|
+
* needs more thought, not a new synonym for `security`.
|
|
22
|
+
*
|
|
23
|
+
* ⚠ AN UNKNOWN KEY RENDERS NOTHING, never the raw key. A client older than a category still shows
|
|
24
|
+
* the base message and the upgrade command, which is the whole cure — `security` printed as a bare
|
|
25
|
+
* word would read like a bug at the worst possible moment.
|
|
26
|
+
*/
|
|
27
|
+
export const MIN_VERSION_CATEGORIES = ['security', 'compatibility', 'data-safety'];
|
|
28
|
+
/**
|
|
29
|
+
* One sentence per category, addressed to the person who is blocked.
|
|
30
|
+
*
|
|
31
|
+
* ⚠ IT SAYS WHAT TO DO OR WHY IT IS URGENT — never what the flaw was. See the leak note above.
|
|
32
|
+
*/
|
|
33
|
+
const COPY = {
|
|
34
|
+
security: 'This is a security update. Please upgrade now.',
|
|
35
|
+
compatibility: 'This version can no longer talk to the service.',
|
|
36
|
+
'data-safety': 'This version can corrupt or lose your work.',
|
|
37
|
+
};
|
|
38
|
+
/** Is this a category we publish? Used by the arming tool to refuse anything else. */
|
|
39
|
+
export function isMinVersionCategory(v) {
|
|
40
|
+
return typeof v === 'string' && MIN_VERSION_CATEGORIES.includes(v);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The line to show, or `''` when there is nothing trustworthy to say.
|
|
44
|
+
*
|
|
45
|
+
* ⚠ TOTAL AND SILENT ON ANYTHING UNRECOGNISED — an absent key, a key from a newer publisher, junk
|
|
46
|
+
* in the field. Every one of those means "this client cannot explain the block", and the honest
|
|
47
|
+
* response is to say nothing extra rather than to guess or to echo.
|
|
48
|
+
*/
|
|
49
|
+
export function minVersionMessage(category) {
|
|
50
|
+
return isMinVersionCategory(category) ? COPY[category] : '';
|
|
51
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* min-version — refuse to run a beehaven CLI below the minimum the server names.
|
|
3
|
+
*
|
|
4
|
+
* The CLI half of the mechanism documented in `docs/FORCE_UPDATE.md`. Same contract as the Mac
|
|
5
|
+
* app — a small JSON beside the published artifact — and the same governing rule:
|
|
6
|
+
*
|
|
7
|
+
* BLOCK ONLY ON POSITIVE, COHERENT, ACTIONABLE EVIDENCE.
|
|
8
|
+
*
|
|
9
|
+
* https://downloads.clearly.sh/cli/minimum.json
|
|
10
|
+
* { "minVersion": "0.8.0", "reason": "The relay protocol changed." }
|
|
11
|
+
*
|
|
12
|
+
* ## ⚠ A CLI IS NOT AN APP, AND THREE THINGS CHANGE
|
|
13
|
+
*
|
|
14
|
+
* 1. NO BLOCKING FETCH ON THE COMMAND PATH. The desktop app checks at launch and every four
|
|
15
|
+
* hours across a session that lasts all day; a CLI process lives for milliseconds, and a
|
|
16
|
+
* network round trip on every invocation would put ~100ms of latency on `beehaven status`
|
|
17
|
+
* forever. The verdict is CACHED on disk and refreshed at most once every six hours, in the
|
|
18
|
+
* BACKGROUND — the current command is never delayed by the check it triggers.
|
|
19
|
+
*
|
|
20
|
+
* 2. THE REPAIR DEPENDS ON THE INSTALL. A curl install runs `beehaven update`; a bundled CLI
|
|
21
|
+
* updates with the Mac app. The block must name the right path and allow the update verb.
|
|
22
|
+
*
|
|
23
|
+
* 3. THE ESCAPE HATCHES MUST STAY OPEN. `--version`, `--help` and anything that could upgrade or
|
|
24
|
+
* diagnose the install keep working while blocked. Blocking `beehaven --version` would hide
|
|
25
|
+
* the single fact a person needs to understand what just happened to them.
|
|
26
|
+
*
|
|
27
|
+
* Everything else is the app's design, for the app's reasons: fail open on any uncertainty, and
|
|
28
|
+
* never demand a version that is not actually published (see `shouldBlock`).
|
|
29
|
+
*/
|
|
30
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
31
|
+
import { minVersionMessage } from './min-version-copy.js';
|
|
32
|
+
import { homedir } from 'os';
|
|
33
|
+
import { join } from 'path';
|
|
34
|
+
// ⚠ ESM: there is no `__filename` here, and the detached refresh needs this module's own path.
|
|
35
|
+
// Using the CommonJS global would throw inside the try and silently disable the refresh for ever
|
|
36
|
+
// — the cache would stay empty and the gate would never fire at all.
|
|
37
|
+
import { fileURLToPath } from 'url';
|
|
38
|
+
import { spawn } from 'child_process';
|
|
39
|
+
/**
|
|
40
|
+
* ⚠ THE CLI'S EXISTING COMPARATOR, not a second one. A private copy here would be the third
|
|
41
|
+
* implementation of "which version is newer" in this codebase, and the failure mode of two that
|
|
42
|
+
* disagree is telling a user on a NEWER build to downgrade. One comparator, one answer.
|
|
43
|
+
*/
|
|
44
|
+
import { compareVersions } from './version-utils.js';
|
|
45
|
+
import { installKind, updateHint } from './commands/self-update.js';
|
|
46
|
+
const FEED = process.env.BEEHAVEN_CLI_FEED || 'https://downloads.clearly.sh/cli/';
|
|
47
|
+
const CACHE_FILE = join(homedir(), '.beehaven', 'min-version.json');
|
|
48
|
+
const CACHE_TTL_MS = 6 * 60 * 60_000;
|
|
49
|
+
const FETCH_TIMEOUT_MS = 4000;
|
|
50
|
+
/** How a blocked user fixes it. The single most important string in this file. */
|
|
51
|
+
export const UPGRADE_COMMAND = 'beehaven update';
|
|
52
|
+
/**
|
|
53
|
+
* Commands that must NEVER be blocked.
|
|
54
|
+
*
|
|
55
|
+
* ⚠ `--version` ABOVE ALL. A person told "you are too old" needs to be able to ask what they
|
|
56
|
+
* have; blocking that turns a clear message into a mystery. `upgrade`/`install`/`doctor` are the
|
|
57
|
+
* repair verbs, and blocking a repair verb is how a recoverable state becomes a stuck one.
|
|
58
|
+
*/
|
|
59
|
+
const ALWAYS_ALLOWED = new Set([
|
|
60
|
+
'--version', '-v', 'version', '--help', '-h', 'help', 'update', 'upgrade', 'install', 'doctor',
|
|
61
|
+
]);
|
|
62
|
+
const isVersion = (v) => typeof v === 'string' && /^\d+\.\d+\.\d+([-+].*)?$/.test(v.trim());
|
|
63
|
+
/**
|
|
64
|
+
* THE DECISION, pure and identical in spirit to the app's.
|
|
65
|
+
*
|
|
66
|
+
* ⚠ `latest` IS THE INTERLOCK THAT MATTERS. Requiring a version that is not published means
|
|
67
|
+
* telling every user to install something that does not exist — an unfixable lockout produced by
|
|
68
|
+
* a typo. If the feed cannot confirm a satisfying build exists, nobody is blocked.
|
|
69
|
+
*/
|
|
70
|
+
export function shouldBlock(input) {
|
|
71
|
+
const { current, minimum, latest } = input;
|
|
72
|
+
if (!isVersion(current))
|
|
73
|
+
return { block: false, why: 'current-version-unreadable' };
|
|
74
|
+
if (!minimum)
|
|
75
|
+
return { block: false, why: 'no-minimum-published' };
|
|
76
|
+
if (!isVersion(minimum))
|
|
77
|
+
return { block: false, why: 'minimum-unparseable' };
|
|
78
|
+
if (compareVersions(current, minimum) >= 0)
|
|
79
|
+
return { block: false, why: 'already-current' };
|
|
80
|
+
if (!isVersion(latest))
|
|
81
|
+
return { block: false, why: 'feed-unreadable' };
|
|
82
|
+
if (compareVersions(latest, minimum) < 0)
|
|
83
|
+
return { block: false, why: 'minimum-exceeds-published-version' };
|
|
84
|
+
return { block: true, why: 'below-minimum', minVersion: minimum };
|
|
85
|
+
}
|
|
86
|
+
function readCache() {
|
|
87
|
+
try {
|
|
88
|
+
const c = JSON.parse(readFileSync(CACHE_FILE, 'utf8'));
|
|
89
|
+
return typeof c?.checkedAt === 'number' ? c : null;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function writeCache(c) {
|
|
96
|
+
try {
|
|
97
|
+
mkdirSync(join(homedir(), '.beehaven'), { recursive: true });
|
|
98
|
+
writeFileSync(CACHE_FILE, JSON.stringify(c), { mode: 0o600 });
|
|
99
|
+
}
|
|
100
|
+
catch { /* cache is an optimisation, never a requirement */ }
|
|
101
|
+
}
|
|
102
|
+
async function getText(url) {
|
|
103
|
+
const ctl = new AbortController();
|
|
104
|
+
const t = setTimeout(() => ctl.abort(), FETCH_TIMEOUT_MS);
|
|
105
|
+
try {
|
|
106
|
+
const r = await fetch(`${url}?_=${Date.now()}`, { signal: ctl.signal, cache: 'no-store' });
|
|
107
|
+
return r.ok ? await r.text() : null;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
clearTimeout(t);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** Fetch the minimum + the newest published version, and cache the pair. */
|
|
117
|
+
export async function refreshMinVersion() {
|
|
118
|
+
const [minBody, latestBody] = await Promise.all([
|
|
119
|
+
getText(`${FEED}minimum.json`),
|
|
120
|
+
getText(`${FEED}latest.json`),
|
|
121
|
+
]);
|
|
122
|
+
let minVersion = null;
|
|
123
|
+
let reason = '';
|
|
124
|
+
let category = '';
|
|
125
|
+
try {
|
|
126
|
+
const j = JSON.parse(minBody || '{}');
|
|
127
|
+
if (isVersion(j?.minVersion))
|
|
128
|
+
minVersion = String(j.minVersion).trim();
|
|
129
|
+
if (typeof j?.reason === 'string')
|
|
130
|
+
reason = j.reason.slice(0, 300);
|
|
131
|
+
// ⚠ Stored raw and resolved at RENDER time, so an unrecognised key prints nothing rather than
|
|
132
|
+
// itself — a cache written by a newer publisher must not make an older client echo a word.
|
|
133
|
+
if (typeof j?.category === 'string')
|
|
134
|
+
category = j.category.slice(0, 40);
|
|
135
|
+
}
|
|
136
|
+
catch { /* absent or malformed ⇒ no minimum */ }
|
|
137
|
+
let latest = null;
|
|
138
|
+
try {
|
|
139
|
+
const j = JSON.parse(latestBody || '{}');
|
|
140
|
+
if (isVersion(j?.version))
|
|
141
|
+
latest = String(j.version).trim();
|
|
142
|
+
}
|
|
143
|
+
catch { /* no manifest ⇒ cannot confirm ⇒ never blocks */ }
|
|
144
|
+
const c = { checkedAt: Date.now(), minVersion, latest, category, reason };
|
|
145
|
+
writeCache(c);
|
|
146
|
+
return c;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Gate the CLI. Returns true when the process should stop.
|
|
150
|
+
*
|
|
151
|
+
* ⚠ SYNCHRONOUS AND CHEAP. It reads a cached verdict and — when that is stale — spawns a
|
|
152
|
+
* DETACHED refresh that outlives this process. The current command never waits: at worst it runs
|
|
153
|
+
* once more on an old verdict, which is exactly the trade a millisecond-scale process should
|
|
154
|
+
* make. The alternative, awaiting a network call before every command, is a tax on thousands of
|
|
155
|
+
* invocations to catch a condition that arises once.
|
|
156
|
+
*/
|
|
157
|
+
export function enforceMinVersion(argv, currentVersion) {
|
|
158
|
+
try {
|
|
159
|
+
if (process.env.BEEHAVEN_SKIP_VERSION_CHECK === '1')
|
|
160
|
+
return false;
|
|
161
|
+
// The repair verbs and the diagnostic ones always run.
|
|
162
|
+
if (argv.some((a) => ALWAYS_ALLOWED.has(a)))
|
|
163
|
+
return false;
|
|
164
|
+
const cache = readCache();
|
|
165
|
+
const stale = !cache || Date.now() - cache.checkedAt > CACHE_TTL_MS;
|
|
166
|
+
if (stale)
|
|
167
|
+
refreshInBackground();
|
|
168
|
+
if (!cache)
|
|
169
|
+
return false; // never seen the server ⇒ never block
|
|
170
|
+
const verdict = shouldBlock({ current: currentVersion, minimum: cache.minVersion, latest: cache.latest });
|
|
171
|
+
if (!verdict.block)
|
|
172
|
+
return false;
|
|
173
|
+
const line = (s = '') => process.stderr.write(`${s}\n`);
|
|
174
|
+
line();
|
|
175
|
+
line(` beehaven ${currentVersion} is no longer supported.`);
|
|
176
|
+
/**
|
|
177
|
+
* ⚠ THE CATEGORY FIRST, THE LEGACY REASON ONLY IF THERE IS NO CATEGORY. Both would print two
|
|
178
|
+
* explanations of one block, and the older one is the free text this design removed.
|
|
179
|
+
*/
|
|
180
|
+
const why = minVersionMessage(cache.category) || cache.reason;
|
|
181
|
+
if (why)
|
|
182
|
+
line(` ${why}`);
|
|
183
|
+
line();
|
|
184
|
+
line(` Version ${verdict.minVersion} or later is required. To update:`);
|
|
185
|
+
line();
|
|
186
|
+
line(` ${installKind() === 'user' ? UPGRADE_COMMAND : updateHint(installKind())}`);
|
|
187
|
+
line();
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return false; // ⚠ A THROW HERE MUST NOT BLOCK. Nothing about this check is worth an outage.
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Refresh without delaying anything.
|
|
196
|
+
*
|
|
197
|
+
* ⚠ DETACHED AND UNREF'D. Doing it in-process would either block the command (defeating the
|
|
198
|
+
* point) or be killed the moment the command exits, so the cache would never populate and the
|
|
199
|
+
* gate would never fire at all.
|
|
200
|
+
*/
|
|
201
|
+
function refreshInBackground() {
|
|
202
|
+
try {
|
|
203
|
+
const self = fileURLToPath(import.meta.url);
|
|
204
|
+
const child = spawn(process.execPath, [self, '--refresh-min-version'], {
|
|
205
|
+
detached: true, stdio: 'ignore',
|
|
206
|
+
});
|
|
207
|
+
child.unref();
|
|
208
|
+
}
|
|
209
|
+
catch { /* best-effort */ }
|
|
210
|
+
}
|
|
211
|
+
// Entry point for the detached refresh above.
|
|
212
|
+
if (process.argv[2] === '--refresh-min-version') {
|
|
213
|
+
void refreshMinVersion().finally(() => process.exit(0));
|
|
214
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Public app/command policy shared by the CLI and daemon catalog surfaces. */
|
|
2
|
+
export const PUBLICLY_HIDDEN_APP_NAMES = new Set([
|
|
3
|
+
'mwah', 'clearly-voice', 'recorder', 'claude-code', 'memory-cloud',
|
|
4
|
+
'ableton', 'blender', 'logic-pro', 'hue', 'nitepunk-piano',
|
|
5
|
+
'shopify', 'figma', 'jira', 'gsc', 'marketing', 'backlinks',
|
|
6
|
+
'vector', 'image', 'bee-canvas',
|
|
7
|
+
]);
|
|
8
|
+
/** Voice/TTS commands are retained in source but withheld from the public CLI for now. */
|
|
9
|
+
export const HIDDEN_PUBLIC_COMMANDS = new Set(['speak', 'voice', 'tts']);
|
|
10
|
+
/** Avatar actions whose public CLI surface is paused with voice/TTS. */
|
|
11
|
+
export const HIDDEN_PUBLIC_APP_ACTIONS = new Set([
|
|
12
|
+
'speak', 'stream', 'setTts', 'voices', 'transcribe', 'transcribeCancel', 'transcribeStats',
|
|
13
|
+
]);
|
|
14
|
+
export function isPubliclyVisibleAppAction(building, action) {
|
|
15
|
+
return String(building ?? '').split('/')[0].toLowerCase() !== 'avatar'
|
|
16
|
+
|| !HIDDEN_PUBLIC_APP_ACTIONS.has(String(action ?? ''));
|
|
17
|
+
}
|
|
18
|
+
export function isPubliclyVisibleApp(app) {
|
|
19
|
+
if (process.env.HIVE_DEV_MODE === '1')
|
|
20
|
+
return true;
|
|
21
|
+
const name = String(app?.name ?? '').trim().toLowerCase();
|
|
22
|
+
return !!name && !PUBLICLY_HIDDEN_APP_NAMES.has(name) && app?.internal !== true;
|
|
23
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure relay-selection helpers — the single READ-side source of truth for
|
|
3
|
+
* "which relay / which env" from a loaded ~/.beehaven/config.json.
|
|
4
|
+
*
|
|
5
|
+
* Kept in its own dependency-free module (no disk, no daemon imports) so EVERY
|
|
6
|
+
* surface that reports the relay — `beehaven env`, `whoami`/status, the admin
|
|
7
|
+
* console URL, auth-origin selection, the daemon spawn path — can import it
|
|
8
|
+
* WITHOUT pulling in the heavy daemon module graph.
|
|
9
|
+
*
|
|
10
|
+
* `endpoint` is the load-bearing key: the relay client (`Relay.loadConfig`)
|
|
11
|
+
* returns null without it and dials `config.endpoint`. `relayUrl` / `envName`
|
|
12
|
+
* are back-compat ALIASES — never trust them over the derived value, or the
|
|
13
|
+
* report drifts from what the daemon actually dials (the bug this fixes: a
|
|
14
|
+
* fresh login / Mac-app install writes only `endpoint`, leaving the aliases
|
|
15
|
+
* stale or unset).
|
|
16
|
+
*/
|
|
17
|
+
/** Map env name → relay URL. Used by `beehaven env <name>`. */
|
|
18
|
+
export const RELAY_PRESETS = {
|
|
19
|
+
prod: 'wss://relay.clearly.sh',
|
|
20
|
+
staging: 'wss://bee-relay-staging.throbbing-unit-1b3f.workers.dev',
|
|
21
|
+
dev: 'ws://localhost:8787',
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* The canonical dial target from a config:
|
|
25
|
+
* 1. `endpoint` — what the relay client actually connects to
|
|
26
|
+
* 2. `relayUrl` — legacy alias, kept in sync for back-compat
|
|
27
|
+
* Returns undefined when neither is set so each caller picks its own default
|
|
28
|
+
* (the daemon falls back to prod; admin commands error instead).
|
|
29
|
+
*/
|
|
30
|
+
export function relayUrlFromConfig(cfg) {
|
|
31
|
+
if (cfg && typeof cfg.endpoint === 'string' && cfg.endpoint.length > 0)
|
|
32
|
+
return cfg.endpoint;
|
|
33
|
+
if (cfg && typeof cfg.relayUrl === 'string' && cfg.relayUrl.length > 0)
|
|
34
|
+
return cfg.relayUrl;
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Resolve BOTH the relay URL and the env NAME from a config, keying off the
|
|
39
|
+
* canonical endpoint. The name is derived FROM the resolved URL (reverse
|
|
40
|
+
* RELAY_PRESETS lookup, else 'custom') so a stale `envName` alias can never
|
|
41
|
+
* mislabel the connection. This is the one helper every "which env" surface
|
|
42
|
+
* must use instead of reading cfg.envName / cfg.relayUrl directly.
|
|
43
|
+
*/
|
|
44
|
+
export function resolveRelaySelection(cfg) {
|
|
45
|
+
const url = relayUrlFromConfig(cfg) ?? RELAY_PRESETS.prod;
|
|
46
|
+
const envName = Object.keys(RELAY_PRESETS).find((k) => RELAY_PRESETS[k] === url) ?? 'custom';
|
|
47
|
+
return { url, envName };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* ⚠⚠ THE ONE ORIGIN RESOLVER ON THIS SIDE — where the WEB APP lives for the env this daemon dials.
|
|
51
|
+
*
|
|
52
|
+
* ── WHY IT EXISTS (CBH-45) ────────────────────────────────────────────────────────────────────
|
|
53
|
+
*
|
|
54
|
+
* `beehaven open <uuid>` printed `https://localhost:3000/documents/…` on one run and
|
|
55
|
+
* `https://www.clearly.sh/documents/…` on the next, same workspace, seconds apart. The server
|
|
56
|
+
* preferred `_uiClientOrigin` — ONE workspace-global slot any browser overwrites on mount — so
|
|
57
|
+
* whichever of ten connected surfaces reported last decided the URL for every caller, and a
|
|
58
|
+
* developer's dev tab made this CLI print a link to a server it has never run. The printed URL is
|
|
59
|
+
* the only thing a CLI user can act on, so that is a dead link that reads as a broken document.
|
|
60
|
+
*
|
|
61
|
+
* The server now prefers the origin the CALLER declares. This is what the CLI declares, and it is
|
|
62
|
+
* derived from the env the daemon is actually dialling — so the answer is a property of THIS
|
|
63
|
+
* machine's configuration and cannot be moved by anyone else's tab.
|
|
64
|
+
*
|
|
65
|
+
* ⚠ THERE WERE AT LEAST THREE SPELLINGS IN THE TREE and this is now the only one:
|
|
66
|
+
* `https://www.clearly.sh` (open), `https://clearly.sh` (canvas.ts — apex, no www) and
|
|
67
|
+
* `https://localhost:3000` (canvas.ts's `--dev` branch). They are not interchangeable: the apex
|
|
68
|
+
* 301s to www, so the apex spelling handed people a link with a redirect in it.
|
|
69
|
+
*
|
|
70
|
+
* ⚠ STAGING RETURNS THE PRODUCTION SITE, AND THAT IS CORRECT. The frontend is prod-only — the
|
|
71
|
+
* `staging.clearly-sh.pages.dev` alias is retired (CLAUDE.md) — so the staging worker's app really
|
|
72
|
+
* is `www.clearly.sh`. Inventing a `staging.clearly.sh` here would be the dead-link bug again.
|
|
73
|
+
*
|
|
74
|
+
* ⚠ `custom` RETURNS undefined, DELIBERATELY. A custom relay says nothing about where its frontend
|
|
75
|
+
* is, and guessing production for it is how you hand someone a link into the wrong deployment.
|
|
76
|
+
* The caller then declares nothing and the server falls back to its own env — which knows.
|
|
77
|
+
*/
|
|
78
|
+
export function appOriginForEnv(envName) {
|
|
79
|
+
switch (envName) {
|
|
80
|
+
case 'prod':
|
|
81
|
+
case 'staging':
|
|
82
|
+
return 'https://www.clearly.sh';
|
|
83
|
+
// `npm run dev` is `next dev --experimental-https`, so the local app is HTTPS, not HTTP.
|
|
84
|
+
case 'dev':
|
|
85
|
+
return 'https://localhost:3000';
|
|
86
|
+
default:
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|