@connectai/selfhost
Advanced tools
+21
-8
@@ -20,3 +20,3 @@ // `@connectai/selfhost` CLI entry. Dependency-free arg parsing + command dispatch. | ||
| import { runUpScript, printNextStep } from './boot.js' | ||
| import { down, logs, printSetupToken } from './compose.js' | ||
| import { down, getBootstrapStatus, logs, printSetupToken } from './compose.js' | ||
@@ -234,11 +234,23 @@ const KNOWN_COMMANDS = new Set(['run', 'down', 'logs', 'token', 'help', 'version']) | ||
| function cmdToken(flags) { | ||
| const { found, lines } = printSetupToken(flags.dir) | ||
| export async function cmdToken( | ||
| flags, | ||
| deps = { getBootstrapStatus, printSetupToken, ui }, | ||
| ) { | ||
| const status = await deps.getBootstrapStatus() | ||
| if (status.known && status.configured) { | ||
| deps.ui.info('this self-host instance is already configured; no setup token is available.') | ||
| deps.ui.info('Open the console login instead.') | ||
| return 0 | ||
| } | ||
| const { found, lines } = deps.printSetupToken(flags.dir) | ||
| if (!found) { | ||
| ui.warn('no setup token found in the api logs yet.') | ||
| ui.info('The token is logged once at api boot. If the stack just started, wait a few') | ||
| ui.info(`seconds and retry, or check: npx @connectai/selfhost logs --dir ${flags.dir} api`) | ||
| deps.ui.warn('no setup token found in the api logs yet.') | ||
| if (status.known && !status.configured) { | ||
| deps.ui.info('Bootstrap is still open, so the api may still be starting up.') | ||
| } | ||
| deps.ui.info('The token is logged once at api boot. If the stack just started, wait a few') | ||
| deps.ui.info(`seconds and retry, or check: npx @connectai/selfhost logs --dir ${flags.dir} api`) | ||
| return 1 | ||
| } | ||
| for (const l of lines) ui.raw(l.trim()) | ||
| for (const l of lines) deps.ui.raw(l.trim()) | ||
| return 0 | ||
@@ -271,3 +283,4 @@ } | ||
| logs tail stack logs (e.g. \`logs api\`) | ||
| token print the one-time first-run setup token (from the api logs) | ||
| token print the one-time first-run setup token, or tell you the instance | ||
| is already configured | ||
| help show this help | ||
@@ -274,0 +287,0 @@ version print the version |
+35
-4
@@ -7,3 +7,3 @@ // Helpers for invoking `docker compose` against the materialized stack, and the | ||
| import path from 'node:path' | ||
| import { DEFAULT_IMAGE_TAG, COMPOSE_FILENAME } from './constants.js' | ||
| import { BOOTSTRAP_STATUS_URL, DEFAULT_IMAGE_TAG, COMPOSE_FILENAME } from './constants.js' | ||
@@ -61,2 +61,35 @@ export function composeFile(dir) { | ||
| export function extractSetupTokenLines(logOutput) { | ||
| return String(logOutput ?? '') | ||
| .split('\n') | ||
| .filter((l) => /setup token/i.test(l)) | ||
| } | ||
| export function parseBootstrapStatus(body) { | ||
| try { | ||
| const parsed = JSON.parse(String(body ?? '')) | ||
| if (typeof parsed?.configured === 'boolean') { | ||
| return { known: true, configured: parsed.configured } | ||
| } | ||
| } catch {} | ||
| return { known: false, configured: false } | ||
| } | ||
| export async function getBootstrapStatus(url = BOOTSTRAP_STATUS_URL) { | ||
| const controller = new AbortController() | ||
| const timeout = setTimeout(() => controller.abort(), 2000) | ||
| try { | ||
| const res = await fetch(url, { | ||
| headers: { accept: 'application/json' }, | ||
| signal: controller.signal, | ||
| }) | ||
| if (!res.ok) return { reachable: true, known: false, configured: false } | ||
| return { reachable: true, ...parseBootstrapStatus(await res.text()) } | ||
| } catch { | ||
| return { reachable: false, known: false, configured: false } | ||
| } finally { | ||
| clearTimeout(timeout) | ||
| } | ||
| } | ||
| // `connectai token` — print the one-time setup token the api logs at boot. The | ||
@@ -73,6 +106,4 @@ // token is emitted to the api logs only (never over the network), so we grep them. | ||
| } | ||
| const lines = res.stdout | ||
| .split('\n') | ||
| .filter((l) => /setup token/i.test(l)) | ||
| const lines = extractSetupTokenLines(res.stdout) | ||
| return { found: lines.length > 0, lines } | ||
| } |
+1
-0
@@ -34,2 +34,3 @@ // Shared constants for the @connectai/selfhost CLI. | ||
| export const API_HEALTH_URL = 'http://localhost:4000/health' | ||
| export const BOOTSTRAP_STATUS_URL = 'http://localhost:4000/bootstrap/status' | ||
@@ -36,0 +37,0 @@ // Resource advisories (warn, never block) — the CON-94 footprint. |
+1
-1
| { | ||
| "name": "@connectai/selfhost", | ||
| "version": "0.1.2", | ||
| "version": "0.1.3", | ||
| "description": "One-command self-host installer for ConnectAI. Takes a clean machine (only Docker + Node) to a running, health-checked company-brain in one command, with no source clone and no local image build: `npx @connectai/selfhost run`. Bundles the image-based docker-compose stack + boot scripts, generates a strong .env (CSPRNG ENCRYPTION_SECRET + POSTGRES_PASSWORD), and boots the stack without bundling Ollama.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
94091
1.76%915
4.81%1
Infinity%