From 9b30fa477b999c36fdbac43019cea08a9f0aaad4 Mon Sep 17 00:00:00 2001 From: Kenny Daniel Date: Wed, 24 Jun 2026 12:48:08 -0700 Subject: [PATCH 1/2] Print hypaware not hyp on --version message --- README.md | 2 ++ src/core/cli/core_commands.js | 2 +- src/core/cli/dispatch.js | 2 +- test/core/cli-version.test.js | 8 ++++---- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2a5fc5cd..09f2dbf5 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ logs / traces / metrics into a local query cache and optional Parquet exports. Everything runs on the local machine — no central server is required for V1. +> Part of **[HypStack](https://hypstack.ai/)**, an open-source stack for AI observability. + ## Quickstart ```sh diff --git a/src/core/cli/core_commands.js b/src/core/cli/core_commands.js index 7c326b2b..0ef315c8 100644 --- a/src/core/cli/core_commands.js +++ b/src/core/cli/core_commands.js @@ -2250,7 +2250,7 @@ async function runVersion(_argv, ctx) { const require = createRequire(import.meta.url) const { version } = require('../../../package.json') const { hypHome } = readObservabilityEnv(ctx.env) - ctx.stdout.write(`hyp ${version}\n`) + ctx.stdout.write(`hypaware ${version}\n`) ctx.stdout.write(` node: ${process.version}\n`) ctx.stdout.write(` platform: ${process.platform} ${process.arch}\n`) ctx.stdout.write(` hyp_home: ${hypHome}\n`) diff --git a/src/core/cli/dispatch.js b/src/core/cli/dispatch.js index 828dde9d..062fbbf4 100644 --- a/src/core/cli/dispatch.js +++ b/src/core/cli/dispatch.js @@ -88,7 +88,7 @@ export async function dispatch(argv, opts = {}) { if (argv.length > 0 && VERSION_FLAGS.has(argv[0])) { const require = createRequire(import.meta.url) const { version } = require('../../../package.json') - stdout.write(`hyp ${version}\n`) + stdout.write(`hypaware ${version}\n`) return 0 } if (argv.length > 0 && HELP_FLAGS.has(argv[0])) { diff --git a/test/core/cli-version.test.js b/test/core/cli-version.test.js index 3620d700..ec4e9a01 100644 --- a/test/core/cli-version.test.js +++ b/test/core/cli-version.test.js @@ -44,14 +44,14 @@ test('hyp --version prints version and exits 0', async () => { const opts = makeDispatchOpts() const code = await dispatch(['--version'], opts) assert.equal(code, 0) - assert.equal(opts.stdout.text(), `hyp ${pkg.version}\n`) + assert.equal(opts.stdout.text(), `hypaware ${pkg.version}\n`) }) test('hyp -V prints version and exits 0', async () => { const opts = makeDispatchOpts() const code = await dispatch(['-V'], opts) assert.equal(code, 0) - assert.equal(opts.stdout.text(), `hyp ${pkg.version}\n`) + assert.equal(opts.stdout.text(), `hypaware ${pkg.version}\n`) }) test('hyp version prints version info and exits 0', async () => { @@ -59,7 +59,7 @@ test('hyp version prints version info and exits 0', async () => { const code = await dispatch(['version'], opts) assert.equal(code, 0) const out = opts.stdout.text() - assert.ok(out.startsWith(`hyp ${pkg.version}\n`)) + assert.ok(out.startsWith(`hypaware ${pkg.version}\n`)) assert.ok(out.includes('node:')) assert.ok(out.includes('platform:')) assert.ok(out.includes('hyp_home:')) @@ -69,6 +69,6 @@ test('version string matches package.json', async () => { const opts = makeDispatchOpts() const code = await dispatch(['--version'], opts) assert.equal(code, 0) - const version = opts.stdout.text().trim().replace('hyp ', '') + const version = opts.stdout.text().trim().replace('hypaware ', '') assert.equal(version, pkg.version) }) From 428f7adf7e16cd0a1c449020314ed75024794bd2 Mon Sep 17 00:00:00 2001 From: Kenny Daniel Date: Wed, 24 Jun 2026 14:41:10 -0700 Subject: [PATCH 2/2] Clearer error messages --- .../central/src/identity_client.js | 4 +++- package.json | 6 ++--- src/core/cli/dispatch.js | 23 +++++++++++++++++++ src/core/commands/backfill.js | 11 ++++++++- src/core/daemon/runtime.js | 22 +++++++++++++++++- 5 files changed, 60 insertions(+), 6 deletions(-) diff --git a/hypaware-core/plugins-workspace/central/src/identity_client.js b/hypaware-core/plugins-workspace/central/src/identity_client.js index c6e4fc6b..312a87a6 100644 --- a/hypaware-core/plugins-workspace/central/src/identity_client.js +++ b/hypaware-core/plugins-workspace/central/src/identity_client.js @@ -88,7 +88,9 @@ export class IdentityClient { async bootstrap() { const token = this.bootstrapToken if (typeof token !== 'string' || token.length === 0) { - throw new Error('identity bootstrap failed: identity.bootstrap_token is not set') + throw new Error( + 'identity bootstrap failed: identity.bootstrap_token is not set. Run `hyp join ` to enroll this host, or remove the central sink if you only capture locally' + ) } const url = joinUrl(this.centralUrl, '/v1/identity/bootstrap') const body = JSON.stringify({ bootstrap_token: token }) diff --git a/package.json b/package.json index 415a9f1d..6681db63 100644 --- a/package.json +++ b/package.json @@ -34,11 +34,11 @@ "smoke": "node ./hypaware-core/smoke/index.js" }, "dependencies": { - "@aws-sdk/client-s3": "3.1074.0", + "@aws-sdk/client-s3": "3.1075.0", "@aws-sdk/credential-provider-ini": "3.972.56", "hyparquet": "1.26.1", "hyparquet-compressors": "1.1.1", - "icebird": "0.8.10", + "icebird": "0.8.11", "squirreling": "0.12.24" }, "optionalDependencies": { @@ -51,7 +51,7 @@ "squirreling": "0.12.24" }, "devDependencies": { - "@types/node": "26.0.0", + "@types/node": "26.0.1", "typescript": "6.0.3" } } diff --git a/src/core/cli/dispatch.js b/src/core/cli/dispatch.js index 062fbbf4..17860490 100644 --- a/src/core/cli/dispatch.js +++ b/src/core/cli/dispatch.js @@ -32,6 +32,27 @@ import { materializeSinks } from '../sinks/materialize.js' const HELP_FLAGS = new Set(['--help', '-h', 'help']) const VERSION_FLAGS = new Set(['--version', '-V']) +/** + * Map a sink materialization failure to a one-line, actionable hint. + * The raw `[errorKind]: message` line stays for operators; this adds a + * "what do I do about it" line for the common, confusing cases: a host + * that hasn't joined a fleet, and the expected-but-noisy plugin-not-active + * warning that read-only commands emit because they don't load sink + * writer plugins. + * + * @param {{ instance: string, errorKind: string, message: string }} err + * @returns {string | undefined} + */ +function sinkWarningHint(err) { + if (/bootstrap_token is not set/.test(err.message)) { + return "this host hasn't joined a fleet. Run `hyp join ` to enable the central sink, or ignore this warning if you only capture locally" + } + if (err.errorKind === 'sink_plugin_not_active') { + return `expected for read-only commands (the writer/destination plugin for '${err.instance}' isn't loaded here); the running daemon is unaffected` + } + return undefined +} + /** * Boot the kernel CLI and dispatch `argv` to a registered command. * @@ -132,6 +153,8 @@ export async function dispatch(argv, opts = {}) { stderr.write( `warning: sink '${err.instance}' not materialized [${err.errorKind}]: ${err.message}\n` ) + const hint = sinkWarningHint(err) + if (hint) stderr.write(` → ${hint}\n`) } } diff --git a/src/core/commands/backfill.js b/src/core/commands/backfill.js index dbbee034..bff2fffa 100644 --- a/src/core/commands/backfill.js +++ b/src/core/commands/backfill.js @@ -69,8 +69,14 @@ export async function runBackfill(argv, ctx) { if (selected.providers.length === 0) { if (parsed.json) { ctx.stdout.write(JSON.stringify({ run_id: devRunId, providers: [] }, null, 2) + '\n') + } else if (ctx.backfills.list().length > 0) { + ctx.stdout.write( + 'No backfill providers matched your active config. Providers are registered but none are enabled here. Run `hyp backfill list` to see them, or name one explicitly (e.g. `hyp backfill claude`).\n' + ) } else { - ctx.stdout.write('No backfill providers selected. Run `hyp backfill list` for the registered set.\n') + ctx.stdout.write( + 'No backfill providers registered. No active plugin contributes one; check enabled plugins with `hyp daemon status`, and if you just joined a fleet the config may still be syncing.\n' + ) } return 0 } @@ -157,6 +163,9 @@ export async function runBackfillList(argv, ctx) { } if (providers.length === 0) { ctx.stdout.write('No backfill providers registered.\n') + ctx.stdout.write( + 'No active plugin contributes a backfill provider. Check enabled plugins with `hyp daemon status`; if you just joined a fleet, the config may still be syncing.\n' + ) return 0 } ctx.stdout.write('Backfill providers:\n') diff --git a/src/core/daemon/runtime.js b/src/core/daemon/runtime.js index 3637e679..000c00c2 100644 --- a/src/core/daemon/runtime.js +++ b/src/core/daemon/runtime.js @@ -557,6 +557,26 @@ function clampTickInterval(value) { return Math.max(value, MIN_TICK_INTERVAL_MS) } +/** + * Turn a raw source-start error into an operator-actionable message. + * The common failure is a port collision (EADDRINUSE): a second + * HypAware daemon or an unrelated service already holds the gateway + * port. The bare Node string ("listen EADDRINUSE ... 127.0.0.1:8787") + * doesn't say what to do, so this appends the remedy. + * + * @param {unknown} err + * @param {string} source + * @returns {string} + */ +function describeSourceStartError(err, source) { + const base = err instanceof Error ? err.message : String(err) + if (/EADDRINUSE/.test(base)) { + const addr = base.match(/[\d.]+:\d+/)?.[0] ?? 'its configured address' + return `${base}. Source '${source}' could not bind ${addr}; another process (a second HypAware daemon or an unrelated service) already holds it. Stop that process or change the listen address, then restart the daemon.` + } + return base +} + /** * Start every registered source that has not auto-started during * `activate()`. Returns one snapshot per source — including the @@ -612,7 +632,7 @@ async function startConfiguredSources({ runtime, log, fileLog, sourcePluginByNam details, }) } catch (err) { - const message = err instanceof Error ? err.message : String(err) + const message = describeSourceStartError(err, contribution.name) fileLog.error('daemon.source_start_failed', { source: contribution.name, plugin,