brane 0.1.1 → 0.3.1
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/AGENTS.md +367 -0
- package/CHANGELOG.md +66 -0
- package/CONTRIBUTING.md +101 -0
- package/DESIGN.md +405 -0
- package/Dockerfile +61 -0
- package/LICENSE +656 -17
- package/README.md +174 -2
- package/SECURITY.md +213 -0
- package/dist/absorb/brane-id.js +11 -0
- package/dist/absorb/chatgpt.js +161 -0
- package/dist/absorb/claude-code.js +504 -0
- package/dist/absorb/index.js +543 -0
- package/dist/absorb/organelle.js +345 -0
- package/dist/absorb/run.js +520 -0
- package/dist/absorb/sources.js +168 -0
- package/dist/absorb/types.js +3 -0
- package/dist/agent-env.js +45 -0
- package/dist/agents.js +1168 -0
- package/dist/claude-live-session.js +124 -0
- package/dist/cli.js +50 -0
- package/dist/codex-tool-log.js +144 -0
- package/dist/commands/absorb.js +291 -0
- package/dist/commands/attach.js +31 -0
- package/dist/commands/certify.js +62 -0
- package/dist/commands/config.js +133 -0
- package/dist/commands/cost.js +126 -0
- package/dist/commands/daemon.js +350 -0
- package/dist/commands/doctor.js +262 -0
- package/dist/commands/fold.js +95 -0
- package/dist/commands/list.js +89 -0
- package/dist/commands/mcp.js +84 -0
- package/dist/commands/meld.js +360 -0
- package/dist/commands/memory.js +215 -0
- package/dist/commands/revive.js +74 -0
- package/dist/commands/run.js +85 -0
- package/dist/commands/spawn.js +1101 -0
- package/dist/commands/token.js +154 -0
- package/dist/copilot-tool-log.js +145 -0
- package/dist/cost/pricing.js +116 -0
- package/dist/cost/reprice.js +136 -0
- package/dist/crush-tool-log.js +293 -0
- package/dist/daemon/agent-scope.js +116 -0
- package/dist/daemon/awaiting.js +54 -0
- package/dist/daemon/cert-manager.js +349 -0
- package/dist/daemon/client.js +262 -0
- package/dist/daemon/gated-routes.js +112 -0
- package/dist/daemon/keep-awake.js +103 -0
- package/dist/daemon/local-socket.js +145 -0
- package/dist/daemon/presence-gate.js +206 -0
- package/dist/daemon/server.js +6353 -0
- package/dist/daemon/signal-source.js +35 -0
- package/dist/daemon/tailscale-rebind.js +58 -0
- package/dist/daemon/token-ring.js +263 -0
- package/dist/daemon/token-routes.js +190 -0
- package/dist/db/events.js +152 -0
- package/dist/db/mentions.js +179 -0
- package/dist/db/schema.js +643 -0
- package/dist/db/tokens.js +624 -0
- package/dist/db/tool-uses.js +647 -0
- package/dist/diagram/index.js +85 -0
- package/dist/diagram/label.js +455 -0
- package/dist/diagram/layout.js +476 -0
- package/dist/diagram/links.js +50 -0
- package/dist/diagram/metrics.js +152 -0
- package/dist/diagram/parse.js +487 -0
- package/dist/diagram/svg.js +389 -0
- package/dist/diagram/types.js +9 -0
- package/dist/export.js +629 -0
- package/dist/files/archive-types.js +2 -0
- package/dist/files/archive.js +182 -0
- package/dist/files/browse.js +327 -0
- package/dist/files/bytes.js +162 -0
- package/dist/files/codecs.js +33 -0
- package/dist/files/find.js +212 -0
- package/dist/files/tar.js +223 -0
- package/dist/files/zip.js +209 -0
- package/dist/folds.js +72 -0
- package/dist/grammars/ebnf.js +92 -0
- package/dist/highlight.js +250 -0
- package/dist/ids.js +24 -0
- package/dist/markdown-diagnostics.js +233 -0
- package/dist/math.js +168 -0
- package/dist/mcp/server.js +886 -0
- package/dist/memory/embedder.js +145 -0
- package/dist/memory/embedding-worker.js +71 -0
- package/dist/memory/embeddings.js +91 -0
- package/dist/memory/index.js +232 -0
- package/dist/memory/storage.js +587 -0
- package/dist/memory/wiki.js +688 -0
- package/dist/memory/wordpiece.js +247 -0
- package/dist/mentions-scan.js +40 -0
- package/dist/presence.js +259 -0
- package/dist/session-affinity.js +185 -0
- package/dist/shared/api-request.js +124 -0
- package/dist/shared/app-origin.js +26 -0
- package/dist/shared/block-glyphs.js +114 -0
- package/dist/shared/diagram-css.js +17 -0
- package/dist/shared/export-selection.js +101 -0
- package/dist/shared/find-in-text.js +110 -0
- package/dist/shared/glow-hues.js +225 -0
- package/dist/shared/hex-dump.js +62 -0
- package/dist/shared/hex-pages.js +52 -0
- package/dist/shared/hit-drum.js +79 -0
- package/dist/shared/known-nids.js +58 -0
- package/dist/shared/list-scroll-keys.js +56 -0
- package/dist/shared/markdown-lists.js +72 -0
- package/dist/shared/markdown-math.js +485 -0
- package/dist/shared/markdown-mentions.js +79 -0
- package/dist/shared/markdown-nids.js +41 -0
- package/dist/shared/markdown-protocols.js +53 -0
- package/dist/shared/markdown-refs.js +123 -0
- package/dist/shared/membrane-invasion.js +342 -0
- package/dist/shared/membrane-motion.js +83 -0
- package/dist/shared/membrane-palette.js +253 -0
- package/dist/shared/membrane-terrain.js +212 -0
- package/dist/shared/message-selection.js +123 -0
- package/dist/shared/mo-glyph.js +57 -0
- package/dist/shared/narrative-format.js +237 -0
- package/dist/shared/narrative-menu.js +84 -0
- package/dist/shared/narrative-sort.js +47 -0
- package/dist/shared/narrative-status.js +52 -0
- package/dist/shared/no-pan.js +85 -0
- package/dist/shared/pane-layout.js +50 -0
- package/dist/shared/payload-inline.js +83 -0
- package/dist/shared/speaker-color.js +61 -0
- package/dist/shared/token-rotation.js +14 -0
- package/dist/shared/view-url.js +216 -0
- package/dist/shared/wiki-links.js +70 -0
- package/dist/shared/wiki-window.js +4 -0
- package/dist/tailscale.js +55 -0
- package/dist/test/absorb-claude.test.js +892 -0
- package/dist/test/agent-scope.test.js +193 -0
- package/dist/test/agents.test.js +55 -0
- package/dist/test/api-request.test.js +122 -0
- package/dist/test/app-host-literal.test.js +53 -0
- package/dist/test/awaiting-glow.test.js +54 -0
- package/dist/test/awaiting.test.js +122 -0
- package/dist/test/bind-token.test.js +88 -0
- package/dist/test/block-glyphs.test.js +68 -0
- package/dist/test/cert-manager.test.js +262 -0
- package/dist/test/claude-live-session.test.js +110 -0
- package/dist/test/codex-tool-log.test.js +568 -0
- package/dist/test/config.test.js +71 -0
- package/dist/test/copilot-tool-log.test.js +93 -0
- package/dist/test/cors-preflight.test.js +56 -0
- package/dist/test/cost-linger.test.js +31 -0
- package/dist/test/cost-pricing.test.js +72 -0
- package/dist/test/cost-reprice.test.js +139 -0
- package/dist/test/cost-sessions.test.js +301 -0
- package/dist/test/crush-tool-log.test.js +289 -0
- package/dist/test/daemon-client.test.js +109 -0
- package/dist/test/daemon-socket.test.js +293 -0
- package/dist/test/diagram-arrows.test.js +205 -0
- package/dist/test/diagram-export.test.js +210 -0
- package/dist/test/diagram-labels.test.js +313 -0
- package/dist/test/diagram-parse.test.js +191 -0
- package/dist/test/diagram-render.test.js +175 -0
- package/dist/test/diagram-review.test.js +288 -0
- package/dist/test/diagram-self-loop-labels.test.js +303 -0
- package/dist/test/embedder.test.js +84 -0
- package/dist/test/events.test.js +123 -0
- package/dist/test/export-selection.test.js +102 -0
- package/dist/test/export.test.js +505 -0
- package/dist/test/files-browse.test.js +393 -0
- package/dist/test/files-find.test.js +272 -0
- package/dist/test/find-in-text.test.js +86 -0
- package/dist/test/first-paint-viewport.test.js +59 -0
- package/dist/test/fixtures/embedding-golden.js +23 -0
- package/dist/test/fixtures/wordpiece-golden.js +167 -0
- package/dist/test/glow-hues.test.js +202 -0
- package/dist/test/harness-cost.test.js +423 -0
- package/dist/test/helpers.js +21 -0
- package/dist/test/hex-pages.test.js +68 -0
- package/dist/test/highlight.test.js +69 -0
- package/dist/test/hit-drum.test.js +71 -0
- package/dist/test/hook-config.test.js +299 -0
- package/dist/test/json-gzip.test.js +70 -0
- package/dist/test/key-routing.test.js +58 -0
- package/dist/test/known-nids.test.js +106 -0
- package/dist/test/link-preview.test.js +67 -0
- package/dist/test/list-scroll-keys.test.js +66 -0
- package/dist/test/listen-own-words.test.js +147 -0
- package/dist/test/listen-timeout.test.js +33 -0
- package/dist/test/markdown-diagnostics.test.js +121 -0
- package/dist/test/markdown-lists.test.js +182 -0
- package/dist/test/markdown-math.test.js +256 -0
- package/dist/test/markdown-nids.test.js +146 -0
- package/dist/test/markdown-protocols.test.js +139 -0
- package/dist/test/mcp-bridge-cancel.test.js +98 -0
- package/dist/test/mcp-bridge-reconnect.test.js +121 -0
- package/dist/test/mcp-narrative.test.js +193 -0
- package/dist/test/mcp.test.js +548 -0
- package/dist/test/meld.test.js +143 -0
- package/dist/test/membrane-invasion.test.js +482 -0
- package/dist/test/membrane-motion.test.js +101 -0
- package/dist/test/membrane-palette.test.js +166 -0
- package/dist/test/membrane-terrain.test.js +170 -0
- package/dist/test/memory-log.test.js +202 -0
- package/dist/test/mentions-e2e.test.js +261 -0
- package/dist/test/mentions-ledger.test.js +265 -0
- package/dist/test/mentions-listen.test.js +265 -0
- package/dist/test/mentions.test.js +238 -0
- package/dist/test/message-selection.test.js +114 -0
- package/dist/test/mo-glyph.test.js +22 -0
- package/dist/test/model-resolution.test.js +166 -0
- package/dist/test/narrate-validate.test.js +77 -0
- package/dist/test/narrate-wait.test.js +160 -0
- package/dist/test/narrative-format.test.js +219 -0
- package/dist/test/narrative-menu.test.js +150 -0
- package/dist/test/narrative-sort.test.js +75 -0
- package/dist/test/no-pan.test.js +60 -0
- package/dist/test/pane-layout.test.js +153 -0
- package/dist/test/paths.test.js +37 -0
- package/dist/test/payload-inline.test.js +91 -0
- package/dist/test/precache-fonts.test.js +72 -0
- package/dist/test/presence-gate.test.js +440 -0
- package/dist/test/presence.test.js +160 -0
- package/dist/test/read-events.test.js +220 -0
- package/dist/test/remote-meld.test.js +128 -0
- package/dist/test/revive-cli.test.js +93 -0
- package/dist/test/search-floor.test.js +206 -0
- package/dist/test/search-fusion.test.js +168 -0
- package/dist/test/search.test.js +205 -0
- package/dist/test/security.test.js +48 -0
- package/dist/test/selection-wiring.test.js +61 -0
- package/dist/test/session-affinity.test.js +295 -0
- package/dist/test/signal-source.test.js +32 -0
- package/dist/test/socket-account.test.js +268 -0
- package/dist/test/spawn-toml.test.js +72 -0
- package/dist/test/speaker-color.test.js +90 -0
- package/dist/test/tailscale-rebind.test.js +79 -0
- package/dist/test/terminal-cursor.test.js +61 -0
- package/dist/test/terminal-resync.test.js +23 -0
- package/dist/test/terminal-size.test.js +36 -0
- package/dist/test/thin-cli.test.js +382 -0
- package/dist/test/token-bootstrap.test.js +272 -0
- package/dist/test/token-devices.test.js +646 -0
- package/dist/test/token-lapse.test.js +94 -0
- package/dist/test/token-ring.test.js +188 -0
- package/dist/test/token-rotate-cli.test.js +58 -0
- package/dist/test/token-routes.test.js +95 -0
- package/dist/test/tool-uses.test.js +1006 -0
- package/dist/test/usage-ledger.test.js +126 -0
- package/dist/test/view-url.test.js +627 -0
- package/dist/test/wiki-links.test.js +57 -0
- package/dist/test/wiki-sources.test.js +85 -0
- package/dist/test/wiki-window.test.js +31 -0
- package/dist/test/wiki.test.js +399 -0
- package/dist/test/wordpiece.test.js +68 -0
- package/dist/test/wrangler-route.test.js +27 -0
- package/dist/tls.js +148 -0
- package/dist/tmux.js +213 -0
- package/dist/user-agent.js +46 -0
- package/docker-entrypoint.sh +154 -0
- package/docs/README.md +3 -0
- package/docs/absorb.md +231 -0
- package/docs/cost-ledger.md +125 -0
- package/docs/daemon-api.md +189 -0
- package/docs/diagrams.md +163 -0
- package/docs/doctor.md +36 -0
- package/docs/export-scope.md +69 -0
- package/docs/files-view.md +133 -0
- package/docs/link-previews.md +38 -0
- package/docs/math.md +32 -0
- package/docs/mcp.md +69 -0
- package/docs/memory-search.md +75 -0
- package/docs/mentions.md +101 -0
- package/docs/multi-brane.md +14 -0
- package/docs/passkeys.md +165 -0
- package/docs/presence.md +202 -0
- package/docs/releasing.md +104 -0
- package/docs/rendering-warnings.md +22 -0
- package/docs/request-layer.md +72 -0
- package/docs/selection-store.md +50 -0
- package/docs/session-affinity.md +61 -0
- package/docs/terminal-mirroring.md +243 -0
- package/docs/token-rotation.md +431 -0
- package/docs/view-urls.md +134 -0
- package/docs/web-app.md +23 -0
- package/docs/wiki.md +43 -0
- package/expo/dist/_expo/static/js/web/entry-e94247f119fdcd3b424a1764233a41d2.js +1099 -0
- package/expo/dist/apple-touch-icon.png +0 -0
- package/expo/dist/favicon.svg +3 -0
- package/expo/dist/index.html +177 -0
- package/expo/dist/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/expo/dist/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/expo/dist/katex/katex.min.css +1 -0
- package/expo/dist/manifest.json +16 -0
- package/expo/dist/metadata.json +1 -0
- package/expo/dist/og/membrane.v1.png +0 -0
- package/expo/dist/pdf.min.mjs +28 -0
- package/expo/dist/pdf.worker.min.mjs +28 -0
- package/expo/dist/sw.js +154 -0
- package/package.json +79 -27
- package/protocols/LIVE_loop.md +407 -0
- package/protocols/bebrief.md +41 -0
- package/protocols/digest.md +231 -0
- package/protocols/fly.md +266 -0
- package/protocols/idle.md +81 -0
- package/protocols/keeper.md +829 -0
- package/protocols/markdown.md +80 -0
- package/protocols/memory.md +419 -0
- package/protocols/mentions.md +93 -0
- package/protocols/payloads.md +53 -0
- package/protocols/prelude.md +91 -0
- package/protocols/ramify.md +105 -0
- package/protocols/review.md +356 -0
- package/protocols/rrr.md +232 -0
- package/protocols/scribe.md +429 -0
- package/protocols/summarize.md +203 -0
- package/protocols/twin.md +174 -0
- package/protocols/wiki.md +438 -0
- package/protocols/worker.md +38 -0
- package/wiki/shared/append-only-event-log.md +148 -0
- package/wiki/shared/index.md +53 -0
- package/wiki/shared/owning-your-narratives.md +161 -0
- package/wiki/shared/premature-ontologies.md +435 -0
- package/wiki/shared/sedimentary-superintelligence.md +128 -0
- package/wiki/shared/sequestering-the-narrative.md +143 -0
- package/wiki/shared/why-wikis-work.md +216 -0
- package/.travis.yml +0 -7
- package/lib/brane.cjs.js +0 -12
- package/lib/brane.cjs.js.map +0 -1
- package/lib/brane.d.ts +0 -11
- package/lib/brane.js +0 -6
- package/lib/brane.js.map +0 -1
- package/lib/tests.d.ts +0 -1
- package/lib/tests.js +0 -8
- package/lib/tests.js.map +0 -1
- package/rollup.config.js +0 -21
- package/scripts/test.sh +0 -8
- package/src/brane.ts +0 -21
- package/src/tests.ts +0 -8
- package/tsconfig.json +0 -16
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { existsSync, readdirSync, readFileSync, realpathSync } from "node:fs";
|
|
5
|
+
import { dirname, join, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { openDatabase, getDaemonAddress, getTokenFilePath } from "../db/schema.js";
|
|
8
|
+
import { ACCOUNT_HASH_PREFIX, readTokenFile, tokenFileStanding } from "../db/tokens.js";
|
|
9
|
+
import { readDaemonSocket } from "../daemon/client.js";
|
|
10
|
+
import { accountDirTrust } from "../daemon/local-socket.js";
|
|
11
|
+
import { resolveTmux } from "../tmux.js";
|
|
12
|
+
import { getTailscaleHost } from "../tailscale.js";
|
|
13
|
+
/**
|
|
14
|
+
* `brane doctor` — is this install sound, on the machine it landed on?
|
|
15
|
+
*
|
|
16
|
+
* Two halves. The first is a handful of environment checks that are cheap to
|
|
17
|
+
* run and expensive to discover by surprise: the Node this CLI runs under
|
|
18
|
+
* against `engines`, whether tmux and Tailscale are reachable, and whether a
|
|
19
|
+
* daemon is running — and if so, whether it is running *this* install's code
|
|
20
|
+
* or some other checkout's. The second is the shipped test suite: `dist/test`
|
|
21
|
+
* is in the tarball on purpose, so an install can prove itself. It runs under
|
|
22
|
+
* the same `node` binary as this process, streams the reporter, and the
|
|
23
|
+
* command exits with the suite's status. Neither half needs a daemon,
|
|
24
|
+
* network, or the embedding model.
|
|
25
|
+
*/
|
|
26
|
+
const INSTALL_DIR = resolve(join(dirname(fileURLToPath(import.meta.url)), "..", ".."));
|
|
27
|
+
const require = createRequire(import.meta.url);
|
|
28
|
+
const pkg = require("../../package.json");
|
|
29
|
+
const MARK = { ok: "✓", warn: "–", fail: "✗" };
|
|
30
|
+
function checkNode() {
|
|
31
|
+
const want = pkg.engines?.node ?? ">=20";
|
|
32
|
+
const min = Number(/(\d+)/.exec(want)?.[1] ?? "20");
|
|
33
|
+
const major = Number(process.versions.node.split(".")[0]);
|
|
34
|
+
return {
|
|
35
|
+
name: "node",
|
|
36
|
+
status: major >= min ? "ok" : "fail",
|
|
37
|
+
detail: `${process.version} at ${process.execPath} (engines ${want})`,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function checkTmux() {
|
|
41
|
+
try {
|
|
42
|
+
return { name: "tmux", status: "ok", detail: resolveTmux() };
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return {
|
|
46
|
+
name: "tmux",
|
|
47
|
+
status: "warn",
|
|
48
|
+
detail: "not found — spawn will try to download a static binary into ~/.brane/bin; otherwise `brew install tmux`",
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function checkTailscale() {
|
|
53
|
+
const host = getTailscaleHost();
|
|
54
|
+
return host
|
|
55
|
+
? { name: "tailscale", status: "ok", detail: host }
|
|
56
|
+
: {
|
|
57
|
+
name: "tailscale",
|
|
58
|
+
status: "warn",
|
|
59
|
+
detail: "not detected — `brane meld` will only advertise 127.0.0.1 (same machine)",
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** The daemon's argv[1], so we can say which checkout it is actually running. */
|
|
63
|
+
function daemonScript(pid) {
|
|
64
|
+
const res = spawnSync("ps", ["-p", String(pid), "-o", "command="], { encoding: "utf8" });
|
|
65
|
+
if (res.status !== 0)
|
|
66
|
+
return undefined;
|
|
67
|
+
const parts = res.stdout.trim().split(/\s+/);
|
|
68
|
+
return parts[1];
|
|
69
|
+
}
|
|
70
|
+
/** Why this shell could not talk to a running daemon, or null if it can.
|
|
71
|
+
* Running is one question and reach is another: an agent session presents
|
|
72
|
+
* its own token; anything else presents nothing at all over the daemon's
|
|
73
|
+
* socket, which the daemon lets in only while the brane dir keeps other
|
|
74
|
+
* accounts out. Pure, for src/test/socket-account.test.ts. */
|
|
75
|
+
export function shellReachProblem(opts) {
|
|
76
|
+
if (opts.secret)
|
|
77
|
+
return null;
|
|
78
|
+
if (!opts.socketPath) {
|
|
79
|
+
return ("it is not listening on its socket (daemon.sock), the only way this shell reaches it; " +
|
|
80
|
+
"`brane daemon logs` says why (`[socket]`), and `brane daemon restart` retries");
|
|
81
|
+
}
|
|
82
|
+
const trust = accountDirTrust(opts.braneDir);
|
|
83
|
+
if (!trust.ok) {
|
|
84
|
+
return `it will refuse this shell without a token: ${trust.reason}; run \`chmod 700 ${opts.braneDir}\``;
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
function checkDaemon() {
|
|
89
|
+
let db;
|
|
90
|
+
try {
|
|
91
|
+
db = openDatabase();
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
return { name: "daemon", status: "fail", detail: `could not open ~/.brane/brane.db: ${String(err)}` };
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const info = getDaemonAddress(db);
|
|
98
|
+
if (!info)
|
|
99
|
+
return { name: "daemon", status: "warn", detail: "not running (any command that needs it will start one)" };
|
|
100
|
+
const unreachable = shellReachProblem({
|
|
101
|
+
secret: process.env.BRANE_NARRATIVE_SECRET,
|
|
102
|
+
socketPath: readDaemonSocket(db),
|
|
103
|
+
braneDir: dirname(db.name),
|
|
104
|
+
});
|
|
105
|
+
if (unreachable)
|
|
106
|
+
return { name: "daemon", status: "fail", detail: `running, but ${unreachable}` };
|
|
107
|
+
const pidRow = db.prepare("SELECT value FROM meta WHERE key = 'daemon_pid'").get();
|
|
108
|
+
const pid = pidRow ? Number(pidRow.value) : NaN;
|
|
109
|
+
// Which checkout is the daemon actually running? Its argv[1] is a
|
|
110
|
+
// `dist/cli.js` — behind the `/opt/homebrew/bin/brane` symlink, usually —
|
|
111
|
+
// and that checkout's package.json says which version, which the meta
|
|
112
|
+
// table does not (it records a coarse compatibility version).
|
|
113
|
+
let where = "";
|
|
114
|
+
let status = "ok";
|
|
115
|
+
const script = Number.isFinite(pid) ? daemonScript(pid) : undefined;
|
|
116
|
+
if (script) {
|
|
117
|
+
let real = script;
|
|
118
|
+
try {
|
|
119
|
+
real = realpathSync(script);
|
|
120
|
+
}
|
|
121
|
+
catch { }
|
|
122
|
+
const theirs = dirname(dirname(resolve(real)));
|
|
123
|
+
if (theirs === INSTALL_DIR) {
|
|
124
|
+
where = " — this install";
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
let theirVersion = "unknown version";
|
|
128
|
+
try {
|
|
129
|
+
theirVersion = JSON.parse(readFileSync(join(theirs, "package.json"), "utf8")).version ?? theirVersion;
|
|
130
|
+
}
|
|
131
|
+
catch { }
|
|
132
|
+
where = ` — a different checkout: ${theirs} (${theirVersion}; this install is ${pkg.version})`;
|
|
133
|
+
status = "warn";
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
name: "daemon",
|
|
138
|
+
status,
|
|
139
|
+
detail: `running, pid ${pid}, port ${info.port}${where}`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
db.close();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** What to say about a `.daemon_token` left by an older brane, which kept
|
|
147
|
+
* this machine's local token there. The daemon empties it at its first
|
|
148
|
+
* start and nothing presents it; removing the file is the operator's to do.
|
|
149
|
+
* `standing` is `empty` for that normal leftover, else how a token still in
|
|
150
|
+
* it stands (`tokenFileStanding`): the daemon could not empty it. Pure, for
|
|
151
|
+
* src/test/socket-account.test.ts. */
|
|
152
|
+
export function leftoverTokenFile(path, standing) {
|
|
153
|
+
const remove = `rm ${path}`;
|
|
154
|
+
return {
|
|
155
|
+
name: "token file",
|
|
156
|
+
status: "warn",
|
|
157
|
+
detail: standing === "empty"
|
|
158
|
+
? `${path} is an empty leftover from an older brane; nothing uses it. Remove it: ${remove}`
|
|
159
|
+
: standing === "dead"
|
|
160
|
+
? `${path} still holds a token from an older brane, which the daemon could not empty; the token no longer works. Remove it: ${remove}`
|
|
161
|
+
: `${path} still holds a token that works (the one every device shared before tokens were hashed), which the daemon could not empty. Remove it: ${remove}`,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/** The `token file` check for the `.daemon_token` at `path`, or null when
|
|
165
|
+
* there is nothing to say: no file, or one that still holds a token before
|
|
166
|
+
* this version's daemon has started once — nothing has retired it yet, and
|
|
167
|
+
* the daemon's first start will. Takes the database, for
|
|
168
|
+
* src/test/socket-account.test.ts. */
|
|
169
|
+
export function tokenFileCheck(db, path) {
|
|
170
|
+
if (!existsSync(path))
|
|
171
|
+
return null;
|
|
172
|
+
const token = readTokenFile(path);
|
|
173
|
+
if (!token)
|
|
174
|
+
return leftoverTokenFile(path, "empty");
|
|
175
|
+
const hasAccountRow = db
|
|
176
|
+
.prepare(`SELECT 1 FROM tokens WHERE kind = 'local' AND revoked_at IS NULL AND hash LIKE '${ACCOUNT_HASH_PREFIX}%'`)
|
|
177
|
+
.get();
|
|
178
|
+
if (!hasAccountRow)
|
|
179
|
+
return null;
|
|
180
|
+
return leftoverTokenFile(path, tokenFileStanding(db, token));
|
|
181
|
+
}
|
|
182
|
+
function checkTokenFile() {
|
|
183
|
+
let db;
|
|
184
|
+
try {
|
|
185
|
+
db = openDatabase();
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return null; // checkDaemon already reports a database that will not open
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
return tokenFileCheck(db, getTokenFilePath(db));
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
db.close();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function printChecks(checks) {
|
|
201
|
+
const width = Math.max(...checks.map((c) => c.name.length));
|
|
202
|
+
for (const c of checks) {
|
|
203
|
+
console.log(`${MARK[c.status]} ${c.name.padEnd(width)} ${c.detail}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/** Run the shipped suite under this process's node; returns its exit status. */
|
|
207
|
+
function runSuite() {
|
|
208
|
+
const dir = join(INSTALL_DIR, "dist", "test");
|
|
209
|
+
if (!existsSync(dir)) {
|
|
210
|
+
console.error(`✗ suite dist/test not found under ${INSTALL_DIR} — run \`npm run build\` in a checkout, or reinstall the package`);
|
|
211
|
+
return 1;
|
|
212
|
+
}
|
|
213
|
+
const files = readdirSync(dir)
|
|
214
|
+
.filter((f) => f.endsWith(".test.js"))
|
|
215
|
+
.sort()
|
|
216
|
+
.map((f) => join(dir, f));
|
|
217
|
+
if (files.length === 0) {
|
|
218
|
+
console.error(`✗ suite no test files in ${dir}`);
|
|
219
|
+
return 1;
|
|
220
|
+
}
|
|
221
|
+
console.log(`\nRunning the shipped test suite: ${files.length} files under ${dir}\n`);
|
|
222
|
+
const res = spawnSync(process.execPath, ["--test", ...files], { stdio: "inherit" });
|
|
223
|
+
if (res.error) {
|
|
224
|
+
console.error(`✗ suite could not start node --test: ${res.error.message}`);
|
|
225
|
+
return 1;
|
|
226
|
+
}
|
|
227
|
+
return res.status ?? 1;
|
|
228
|
+
}
|
|
229
|
+
export const doctorCommand = defineCommand({
|
|
230
|
+
meta: {
|
|
231
|
+
name: "doctor",
|
|
232
|
+
description: "Check this install (node, tmux, Tailscale, daemon) and run the shipped test suite against it",
|
|
233
|
+
},
|
|
234
|
+
args: {
|
|
235
|
+
suite: {
|
|
236
|
+
type: "boolean",
|
|
237
|
+
description: "Run the shipped test suite (default: yes; --no-suite for checks only)",
|
|
238
|
+
default: true,
|
|
239
|
+
},
|
|
240
|
+
"suite-only": {
|
|
241
|
+
type: "boolean",
|
|
242
|
+
description: "Skip the environment checks and only run the test suite",
|
|
243
|
+
default: false,
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
async run({ args }) {
|
|
247
|
+
let failed = false;
|
|
248
|
+
if (!args["suite-only"]) {
|
|
249
|
+
console.log(`brane ${pkg.version} at ${INSTALL_DIR}\n`);
|
|
250
|
+
const checks = [checkNode(), checkTmux(), checkTailscale(), checkDaemon(), checkTokenFile()].filter((c) => c !== null);
|
|
251
|
+
printChecks(checks);
|
|
252
|
+
failed = checks.some((c) => c.status === "fail");
|
|
253
|
+
}
|
|
254
|
+
if (args.suite) {
|
|
255
|
+
const status = runSuite();
|
|
256
|
+
if (status !== 0)
|
|
257
|
+
failed = true;
|
|
258
|
+
}
|
|
259
|
+
process.exit(failed ? 1 : 0);
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { resolveDaemonEndpoint, daemonRequest, warnIfVersionMismatch } from "../daemon/client.js";
|
|
4
|
+
function describeFold(fold) {
|
|
5
|
+
const created = new Date(fold.created_at).toLocaleDateString();
|
|
6
|
+
const spawned = fold.last_spawned
|
|
7
|
+
? new Date(fold.last_spawned).toLocaleDateString()
|
|
8
|
+
: "never";
|
|
9
|
+
return [
|
|
10
|
+
`Fold: ${fold.path}${fold.archived ? " (archived)" : ""}`,
|
|
11
|
+
` created: ${created} last spawned: ${spawned}${fold.brane_id ? ` brane: ${fold.brane_id}` : ""}`,
|
|
12
|
+
];
|
|
13
|
+
}
|
|
14
|
+
/** The fold's line plus the narratives spawned from it — the listing the
|
|
15
|
+
* daemon already serves, filtered to this fold. */
|
|
16
|
+
export function formatFoldInfo(fold, narratives) {
|
|
17
|
+
const lines = describeFold(fold);
|
|
18
|
+
const mine = narratives.filter((n) => n.fold_path === fold.path).slice(0, 10);
|
|
19
|
+
if (mine.length > 0) {
|
|
20
|
+
lines.push("", " Narratives:");
|
|
21
|
+
for (const n of mine) {
|
|
22
|
+
const icon = n.status === "active" ? "●" : "○";
|
|
23
|
+
lines.push(` ${icon} ${n.id} (${n.agent_type}, ${n.role})`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return lines.join("\n");
|
|
27
|
+
}
|
|
28
|
+
export function formatFoldList(folds) {
|
|
29
|
+
if (folds.length === 0) {
|
|
30
|
+
return "No folds registered. Run `brane fold` in a directory to register it.";
|
|
31
|
+
}
|
|
32
|
+
const lines = ["Registered folds:", ""];
|
|
33
|
+
for (const fold of folds) {
|
|
34
|
+
const created = new Date(fold.created_at).toLocaleDateString();
|
|
35
|
+
const spawned = fold.last_spawned
|
|
36
|
+
? new Date(fold.last_spawned).toLocaleDateString()
|
|
37
|
+
: "never";
|
|
38
|
+
const braneTag = fold.brane_id ? ` brane: ${fold.brane_id}` : "";
|
|
39
|
+
lines.push(` ${fold.path}${fold.archived ? " (archived)" : ""}`);
|
|
40
|
+
lines.push(` created: ${created} last spawned: ${spawned}${braneTag}`, "");
|
|
41
|
+
}
|
|
42
|
+
return lines.join("\n");
|
|
43
|
+
}
|
|
44
|
+
export const foldCommand = defineCommand({
|
|
45
|
+
meta: {
|
|
46
|
+
name: "fold",
|
|
47
|
+
description: "Register the current directory as a fold (spawn point)",
|
|
48
|
+
},
|
|
49
|
+
args: {
|
|
50
|
+
directory: {
|
|
51
|
+
type: "positional",
|
|
52
|
+
description: "Directory to fold (default: current directory)",
|
|
53
|
+
required: false,
|
|
54
|
+
},
|
|
55
|
+
list: {
|
|
56
|
+
type: "boolean",
|
|
57
|
+
description: "List all registered folds",
|
|
58
|
+
},
|
|
59
|
+
brane: {
|
|
60
|
+
type: "string",
|
|
61
|
+
description: "Associate this fold with a brane ID",
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
async run({ args }) {
|
|
65
|
+
try {
|
|
66
|
+
// Registration is `POST /api/folds`, the same route the app's "add a
|
|
67
|
+
// spawn point" uses. The daemon recognizes this CLI as a loopback
|
|
68
|
+
// caller and grants it the reach the operator's shell always had;
|
|
69
|
+
// a remote client is confined to $HOME.
|
|
70
|
+
const endpoint = await resolveDaemonEndpoint();
|
|
71
|
+
warnIfVersionMismatch(endpoint);
|
|
72
|
+
if (args.list) {
|
|
73
|
+
const { folds } = await daemonRequest(endpoint, "/api/folds?archived=1");
|
|
74
|
+
console.log(formatFoldList(folds));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const absolute = resolve(args.directory ?? process.cwd());
|
|
78
|
+
const { fold, created } = await daemonRequest(endpoint, "/api/folds", { method: "POST", body: { path: absolute, ...(args.brane ? { brane_id: args.brane } : {}) } });
|
|
79
|
+
if (created) {
|
|
80
|
+
console.log(`Folded: ${fold.path} (brane: ${fold.brane_id})`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Already registered: the brane may have been rebound; show the fold.
|
|
84
|
+
if (args.brane)
|
|
85
|
+
console.log(`Updated brane: ${args.brane}`);
|
|
86
|
+
const { narratives } = await daemonRequest(endpoint, "/api/narratives");
|
|
87
|
+
console.log(formatFoldInfo(fold, narratives));
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
//# sourceMappingURL=fold.js.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { resolveDaemonEndpoint, daemonRequest, warnIfVersionMismatch } from "../daemon/client.js";
|
|
3
|
+
import { timeSince } from "./memory.js";
|
|
4
|
+
export const listCommand = defineCommand({
|
|
5
|
+
meta: {
|
|
6
|
+
name: "list",
|
|
7
|
+
description: "List narratives grouped by brane",
|
|
8
|
+
},
|
|
9
|
+
args: {
|
|
10
|
+
brane: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Filter to a specific brane ID",
|
|
13
|
+
},
|
|
14
|
+
archived: {
|
|
15
|
+
type: "boolean",
|
|
16
|
+
description: "Include archived narratives",
|
|
17
|
+
default: false,
|
|
18
|
+
},
|
|
19
|
+
limit: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "Max narratives per brane (0 = unlimited, default: 10)",
|
|
22
|
+
default: "10",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
async run({ args }) {
|
|
26
|
+
try {
|
|
27
|
+
const endpoint = await resolveDaemonEndpoint();
|
|
28
|
+
warnIfVersionMismatch(endpoint);
|
|
29
|
+
const query = new URLSearchParams();
|
|
30
|
+
if (args.archived)
|
|
31
|
+
query.set("archived", "1");
|
|
32
|
+
if (args.brane)
|
|
33
|
+
query.set("brane_ids", args.brane);
|
|
34
|
+
const qs = query.toString();
|
|
35
|
+
const [{ narratives }, { branes }] = await Promise.all([
|
|
36
|
+
daemonRequest(endpoint, `/api/narratives${qs ? `?${qs}` : ""}`),
|
|
37
|
+
daemonRequest(endpoint, "/api/branes"),
|
|
38
|
+
]);
|
|
39
|
+
console.log(formatNarrativeList(narratives, branes, { limit: parseInt(args.limit, 10) }));
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
/** Group by brane in first-seen order (the API lists newest first, so the
|
|
48
|
+
* brane with the newest narrative leads), home brane marked. */
|
|
49
|
+
export function formatNarrativeList(narratives, branes, opts) {
|
|
50
|
+
if (narratives.length === 0) {
|
|
51
|
+
return "No narratives. Use `brane spawn <agent>` to start one.";
|
|
52
|
+
}
|
|
53
|
+
const homeBraneId = branes.find((b) => b.is_home)?.brane_id;
|
|
54
|
+
const byBrane = new Map();
|
|
55
|
+
for (const n of narratives) {
|
|
56
|
+
let group = byBrane.get(n.brane_id);
|
|
57
|
+
if (!group) {
|
|
58
|
+
group = [];
|
|
59
|
+
byBrane.set(n.brane_id, group);
|
|
60
|
+
}
|
|
61
|
+
group.push(n);
|
|
62
|
+
}
|
|
63
|
+
const out = [];
|
|
64
|
+
for (const [braneId, narrs] of byBrane) {
|
|
65
|
+
const isHome = braneId === homeBraneId;
|
|
66
|
+
const activeCount = narrs.filter((n) => n.status === "active").length;
|
|
67
|
+
const label = isHome ? `${braneId} (home)` : braneId;
|
|
68
|
+
const counts = `${activeCount} active, ${narrs.length} total`;
|
|
69
|
+
out.push(`\n ${isHome ? "●" : "○"} ${label} [${counts}]`);
|
|
70
|
+
const shown = opts.limit > 0 ? narrs.slice(0, opts.limit) : narrs;
|
|
71
|
+
for (const n of shown) {
|
|
72
|
+
const age = timeSince(n.created_at, opts.now);
|
|
73
|
+
const statusIcon = n.status === "active" ? "●" : "○";
|
|
74
|
+
const desc = n.summary
|
|
75
|
+
? n.summary.length > 50
|
|
76
|
+
? n.summary.slice(0, 47) + "..."
|
|
77
|
+
: n.summary
|
|
78
|
+
: `${n.agent_type}/${n.role}`;
|
|
79
|
+
out.push(` ${statusIcon} ${n.id} ${desc} ${age}`);
|
|
80
|
+
}
|
|
81
|
+
const remaining = narrs.length - shown.length;
|
|
82
|
+
if (remaining > 0) {
|
|
83
|
+
out.push(` ... and ${remaining} more (--limit 0 to show all)`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
out.push("");
|
|
87
|
+
return out.join("\n");
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { openDatabase, getDaemonAddress, getPackageVersion } from "../db/schema.js";
|
|
3
|
+
import { startMcpBridge } from "../mcp/server.js";
|
|
4
|
+
import { connect as netConnect } from "node:net";
|
|
5
|
+
import { readDaemonSocket, resolveCredential } from "../daemon/client.js";
|
|
6
|
+
import { ensureDaemonRunning } from "./daemon.js";
|
|
7
|
+
/** Resolve daemon WebSocket URL, auto-starting daemon if needed */
|
|
8
|
+
async function resolveDaemonUrl() {
|
|
9
|
+
let db = openDatabase();
|
|
10
|
+
let info = getDaemonAddress(db);
|
|
11
|
+
db.close();
|
|
12
|
+
if (!info) {
|
|
13
|
+
process.stderr.write("MCP bridge: daemon not running, starting it...\n");
|
|
14
|
+
try {
|
|
15
|
+
info = await ensureDaemonRunning();
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// Check if daemon is running with TLS, and settle what to present: a
|
|
22
|
+
// spawned agent's own token, or nothing — an unspawned client dials the
|
|
23
|
+
// daemon's socket as this account, and `welcome` hands it a token.
|
|
24
|
+
db = openDatabase();
|
|
25
|
+
const tlsRow = db.prepare("SELECT value FROM meta WHERE key = 'daemon_tls'").get();
|
|
26
|
+
let token;
|
|
27
|
+
let socketPath;
|
|
28
|
+
try {
|
|
29
|
+
token = resolveCredential();
|
|
30
|
+
socketPath = readDaemonSocket(db);
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
db.close();
|
|
34
|
+
}
|
|
35
|
+
// Warn on version mismatch between this CLI and the running daemon
|
|
36
|
+
if (info.version) {
|
|
37
|
+
const myVersion = getPackageVersion();
|
|
38
|
+
if (info.version !== myVersion) {
|
|
39
|
+
process.stderr.write(`MCP bridge: version mismatch — daemon is v${info.version}, this CLI is v${myVersion}\n` +
|
|
40
|
+
` Consider: npm i -g brane (and check $PATH priority)\n`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const tls = tlsRow?.value === "true";
|
|
44
|
+
// The socket only if it answers now: the bridge re-resolves on every
|
|
45
|
+
// reconnect, so a socket that stops answering falls back to TCP next time.
|
|
46
|
+
const reachable = socketPath && (await socketAnswers(socketPath)) ? socketPath : undefined;
|
|
47
|
+
if (token === null && !reachable) {
|
|
48
|
+
// Over TCP a dial with no token is refused, so there is nothing to dial:
|
|
49
|
+
// the first resolve exits ("daemon not available"), a reconnect retries.
|
|
50
|
+
process.stderr.write("MCP bridge: the daemon's socket is not answering, and there is no token for TCP\n");
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return mcpDialTarget({ port: info.port, tls, token, socketPath: reachable });
|
|
54
|
+
}
|
|
55
|
+
/** Where `brane mcp` dials. Over the daemon's Unix socket when it has one —
|
|
56
|
+
* only this account can reach it, and there is no certificate to skip
|
|
57
|
+
* verifying — else TCP on loopback. A null token presents none. Pure, for src/test/daemon-socket.test.ts. */
|
|
58
|
+
export function mcpDialTarget(opts) {
|
|
59
|
+
// No token is only ever meant for the socket, where it acts as this account.
|
|
60
|
+
const query = opts.token === null ? "/ws/mcp?" : `/ws/mcp?token=${encodeURIComponent(opts.token)}`;
|
|
61
|
+
if (opts.socketPath)
|
|
62
|
+
return { url: `ws://localhost${query}`, socketPath: opts.socketPath };
|
|
63
|
+
return { url: `${opts.tls ? "wss" : "ws"}://localhost:${opts.port}${query}` };
|
|
64
|
+
}
|
|
65
|
+
function socketAnswers(path) {
|
|
66
|
+
return new Promise((resolve) => {
|
|
67
|
+
const probe = netConnect(path);
|
|
68
|
+
probe.once("connect", () => {
|
|
69
|
+
probe.destroy();
|
|
70
|
+
resolve(true);
|
|
71
|
+
});
|
|
72
|
+
probe.once("error", () => resolve(false));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
export const mcpCommand = defineCommand({
|
|
76
|
+
meta: {
|
|
77
|
+
name: "mcp",
|
|
78
|
+
description: "Run as MCP stdio server (for agent processes)",
|
|
79
|
+
},
|
|
80
|
+
async run() {
|
|
81
|
+
startMcpBridge(resolveDaemonUrl);
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
//# sourceMappingURL=mcp.js.map
|