nebula-ai 0.0.14 → 0.0.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.
Files changed (3) hide show
  1. package/README.md +65 -12
  2. package/bin/nebula-ai.js +32 -68
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -18,6 +18,8 @@ npx nebula-ai # interactive TUI
18
18
  npx nebula-ai chat "summarize the open issues in repo X"
19
19
  ```
20
20
 
21
+ At login you pick where the auth token lives — your **system keychain** (recommended; uses `Bun.secrets` → macOS Keychain / Windows Credential Manager / libsecret) or a **local file** at `~/.nebula/.auth-token` (0600). Keychain failures fall back transparently to the file store so you never lose a session you just authenticated.
22
+
21
23
  **Supported platforms:** macOS (arm64, x64), Linux (x64, arm64), Windows (x64).
22
24
 
23
25
  ## Install via Homebrew
@@ -47,21 +49,62 @@ The TUI gives you a full dashboard with agents, channels, and settings — all n
47
49
  - **Ctrl+K command palette** — jump to any agent, channel, or settings page instantly. Start typing to filter, or create channels and send messages directly from the palette.
48
50
  - **Tab / Shift+Tab** — cycle between Agents, Channels, and Settings sections on the home screen.
49
51
  - **@ / #** — jump directly to the Agents or Channels section.
52
+ - **Vim-style motion** — `j`/`k` up/down, `h`/`Esc` back, `l`/`Enter` open. Works everywhere lists are rendered.
53
+ - **Tab bar** — `h`/`l` switch between open tabs, `x` closes the current tab.
54
+ - **Status bar** — always-on mode badge (`NAV` / `INSERT` / `HITL` / `FIND`), breadcrumb, contextual key hints, and a live connection indicator.
55
+ - **q** from any navigation-mode screen exits cleanly.
50
56
 
51
57
  ### Chat
52
58
 
53
59
  - **Markdown rendering** — agent responses display bold, italic, inline code, headings, and clickable links.
54
- - **Per-channel model selection** — press `m` in any DM or channel to switch the AI model for that conversation. A global default model can be set in Settings.
55
- - **File attachments** — drag and drop files from your file manager into the chat input. Files appear as chips above the message area and are sent with your next message.
56
- - **Activity log** — tool calls and execution details are surfaced inline in chat, with a keystroke to open the full activity log for a completed turn.
57
- - **Interactive prompts** — when an agent asks a multi-choice question, it renders as a widget with option descriptions, number-key shortcuts (1–9), and an "Other" free-text fallback.
60
+ - **Per-channel model selection** — press `m` in any DM or channel to open the model picker. Verified and experimental model lists are surfaced separately; a global default model can be set in Settings.
61
+ - **File attachments** — drag and drop files from your file manager into the chat input. Files appear as chips above the message area and are sent with your next message. When the local device daemon is running, files are delivered through the sandbox instead of being uploaded to the cloud.
62
+ - **Activity log** — tool calls and execution details are surfaced inline in chat, with `Enter` on a completed turn opening the full activity log.
63
+ - **Entity navigation** — press `Tab` on a selected message to cycle through referenced agents, triggers, files, and skills; `Enter` opens the detail view.
64
+ - **Message history** — press `↑` from an empty input (or `k` in navigation mode) to scroll back through previous turns.
65
+ - **Interactive prompts (HITL)** — when an agent asks a multi-choice question or requests plan approval, it renders as a widget with option descriptions, number-key shortcuts (1–9), and an "Other" free-text fallback.
66
+ - **Archive** — press `Shift+A` in any channel to archive it with a confirm prompt.
67
+ - **Refresh** — press `Shift+R` to pull the latest state for the current view.
58
68
  - **Auto-focus input** — configurable in Settings. When disabled, chats open in navigation mode; press `i` to start typing.
59
69
 
60
70
  ### Workspace & session
61
71
 
62
72
  - **Multi-tab workspace** — tabs dynamically resize to your terminal width and scroll horizontally when they overflow.
63
73
  - **Instant tab switching** — previously visited screens load from cache while fresh data is fetched in the background (stale-while-revalidate).
64
- - **Session restore** — enable in Settings to automatically reopen your tabs and navigation state when you restart the CLI.
74
+ - **Session restore** — enable in Settings to automatically reopen your tabs and navigation state when you restart the CLI. Session state is also flushed on clean Ctrl+C / SIGTERM.
75
+
76
+ ### Theming
77
+
78
+ Sixteen base16 color themes ship in the box: `default-dark`, `monokai`, `ocean`, `eighties`, `gruvbox`, `solarized`, `tokyo-night`, `dracula`, `nord`, `onedark`, `catppuccin`, `rose-pine`, `kanagawa`, `everforest`, `night-owl`, `material`. Cycle through them from **Settings → Configuration** (press `Enter` on the Theme row), or set a fixed value from the CLI:
79
+
80
+ ```sh
81
+ npx nebula-ai user config set theme tokyo-night
82
+ ```
83
+
84
+ ### Settings
85
+
86
+ Reachable from the home screen (`Settings` row) or the command palette:
87
+
88
+ - **Profile & Account** — username, email, display name.
89
+ - **Default Model** — global LLM fallback for new chats.
90
+ - **Local Device** — daemon status, start/stop/restart controls, device ID.
91
+ - **Mounted Folders** — add/remove host folders that are mounted into the sandbox.
92
+ - **Files** — browse and manage files stored on the platform.
93
+ - **Configuration** — theme, color, verbose, auto-focus, session restore, telemetry.
94
+
95
+ ## Local device & sandbox
96
+
97
+ For long-running tasks, agents can execute code on your own machine inside a managed microVM sandbox (powered by [Boxlite](https://boxlite.ai)). The sandbox is isolated from your host but can optionally mount folders you choose as read-write or read-only.
98
+
99
+ ```sh
100
+ npx nebula-ai install # install and start the background daemon
101
+ npx nebula-ai local-device status # check daemon state
102
+ npx nebula-ai local-device folders add ~/code/my-project
103
+ npx nebula-ai shell # open an interactive shell inside the sandbox
104
+ npx nebula-ai uninstall # remove the daemon (use --keep-data to keep config/logs)
105
+ ```
106
+
107
+ The daemon is installed as a native OS service — **launchd** on macOS, **systemd --user** on Linux, **Task Scheduler** on Windows — and runs under your user account so there's no root prompt. The sandbox image is pre-pulled during install so the first real RPC doesn't block on a download.
65
108
 
66
109
  ## CLI commands
67
110
 
@@ -70,21 +113,26 @@ The same surface is available from the command line:
70
113
  - `chat` — send a message to an agent (`-a/--agent`, `-c/--channel`, `--no-stream`).
71
114
  - `channels` — list, create, get, delete, archive, unarchive, share, update, and view messages in conversation threads.
72
115
  - `agents` — list, create, update, delete, enable/disable, view logs/errors, manage triggers, and attach/detach skills.
73
- - `user` — profile, billing, files, variables (including secrets), and local config.
74
- - `user config` — list, get, set, and reset configuration values; list available AI models.
116
+ - `user` — profile, account, and platform-side resources.
117
+ - `user update` — change username or display name.
118
+ - `user config` — list, get, set, and reset CLI configuration values; list available AI models.
75
119
  - `user variables` — manage global or agent-scoped variables and secrets.
76
120
  - `user billing` — check balance, view transaction history, and configure auto top-up.
77
121
  - `user files` — upload, download, list, share, and manage files and folders.
78
- - `status` — current CLI and device state.
122
+ - `user devices` — manage your cloud sandbox device (info, start, stop, rename, visibility, file browse/search, and long-running services: create, start, stop, logs).
123
+ - `local-device` — manage the on-host daemon (status, logs, start, stop, restart, folders add/remove).
124
+ - `install` / `uninstall` — set up or tear down the on-host daemon as a background service.
125
+ - `shell` — open an interactive shell inside the local sandbox VM (`--shell <path>` to pick a different binary).
126
+ - `status` — current CLI, auth, daemon, device, and folder state.
79
127
  - `login` / `logout` — authenticate or clear stored credentials.
80
128
 
81
- Every command supports `--json` for machine-readable output, `-v/--verbose` for extra detail, and `--no-color` to disable ANSI styling.
129
+ Every command supports `--json` for machine-readable output, `-v/--verbose` for extra detail, `--no-color` to disable ANSI styling, and `--api-url <url>` to point at a non-default backend.
82
130
 
83
131
  ## Privacy and telemetry
84
132
 
85
133
  The CLI collects anonymized usage analytics (via PostHog) to help improve the product. You can opt out at any time:
86
134
 
87
- - Toggle telemetry off in the TUI under **Settings > Configuration**
135
+ - Toggle telemetry off in the TUI under **Settings → Configuration**
88
136
  - From the CLI: `npx nebula-ai user config set telemetry false`
89
137
  - Or set `DO_NOT_TRACK=1` (or `NEBULA_DO_NOT_TRACK=1`) in your environment
90
138
 
@@ -94,8 +142,13 @@ No events are sent when telemetry is disabled.
94
142
 
95
143
  | Path / variable | Purpose |
96
144
  |---|---|
97
- | `~/.config/nebula-cli/` | Stored auth session and CLI settings. |
98
- | `~/.cache/nebula-ai/` | Cached prebuilt binary. |
145
+ | `~/.nebula/` | Stored auth session, CLI settings, sandbox data, daemon logs. |
146
+ | `~/.cache/nebula-ai/` | Cached prebuilt binary (per-version, per-platform). |
147
+ | `NEBULA_HOME` | Override the `~/.nebula` home directory. |
99
148
  | `NEBULA_AI_CACHE_DIR` | Override the binary cache location. |
100
149
  | `NEBULA_AI_DOWNLOAD_BASE` | Use an alternate mirror for the prebuilt binary download (useful behind restrictive firewalls). |
150
+ | `NEBULA_AI_NO_UPDATE_CHECK` | Set to `1` to suppress the daily update check. Already suppressed under `npx`, CI, or a non-TTY stderr. |
151
+ | `API_URL` | Override the Nebula API base URL (alternative to the `--api-url` flag). |
152
+ | `NEBULA_AUTH_TOKEN` | Use a pre-minted auth token instead of the stored credential. Scripting / CI escape hatch — takes precedence over both keychain and file stores. |
153
+ | `NEBULA_DEBUG` / `DEBUG=nebula-cli` | Enable verbose logging (same effect as `-v/--verbose`). |
101
154
  | `DO_NOT_TRACK` / `NEBULA_DO_NOT_TRACK` | Set to `1` to disable product telemetry. |
package/bin/nebula-ai.js CHANGED
@@ -46,6 +46,10 @@ import { homedir } from "node:os";
46
46
  import { dirname, join } from "node:path";
47
47
  import { pipeline } from "node:stream/promises";
48
48
  import { fileURLToPath } from "node:url";
49
+ import { detectInstallMethod, getChannel, getUpdateCheckCachePath, getUpgradeCommand, isNewerVersion, isNpx, isUpdateCheckDisabled, UPDATE_CHECK_TTL_MS, } from "./update-check-lib.js";
50
+ // Re-export the pure update-check helpers so older tests / downstream
51
+ // importers that still reach for `./launcher.js` keep working.
52
+ export { detectInstallMethod, getChannel, getUpdateCheckCachePath, getUpgradeCommand, isNewerVersion, isNpx, isUpdateCheckDisabled, } from "./update-check-lib.js";
49
53
  // The public half of the ed25519 key that signs `checksums.json`. The
50
54
  // private half lives only in the `NEBULA_AI_SIGNING_PRIVATE_KEY` GitHub
51
55
  // Actions secret.
@@ -66,68 +70,11 @@ const SUPPORTED_PLATFORMS = new Set([
66
70
  "linux-arm64",
67
71
  "win32-x64",
68
72
  ]);
69
- // ─── update-check constants ────────────────────────────────────────────────
70
- const UPDATE_CHECK_TTL = 4 * 60 * 60 * 1000; // 4 hours
71
73
  // ─── pure helpers (exported for tests) ─────────────────────────────────────
72
- /**
73
- * Return the path to the update-check cache file. Lives at the top of the
74
- * cache directory (NOT under a version-specific subdirectory) so it persists
75
- * across version upgrades.
76
- */
77
- export function getUpdateCheckCachePath() {
78
- const base = process.env.NEBULA_AI_CACHE_DIR ||
79
- (process.platform === "win32"
80
- ? process.env.LOCALAPPDATA || join(homedir(), "AppData", "Local")
81
- : process.env.XDG_CACHE_HOME || join(homedir(), ".cache"));
82
- return join(base, "nebula-ai", "update-check.json");
83
- }
84
- /**
85
- * Determine the release channel from a version string.
86
- * Dev versions look like `x.y.z-dev.YYYYMMDDHHMMSS.sha`.
87
- */
88
- export function getChannel(version) {
89
- return version.includes("-dev.") ? "dev" : "prod";
90
- }
91
- /**
92
- * Return true if `latest` is a newer semver than `current` (prod), or simply
93
- * different (dev — timestamps are monotonically increasing).
94
- */
95
- export function isNewerVersion(latest, current) {
96
- if (current.includes("-dev.")) {
97
- return latest !== current;
98
- }
99
- // Strip any prerelease suffix (e.g., "-beta.1") before parsing.
100
- const clean = (v) => v.replace(/-.*$/, "");
101
- const [a1, a2, a3] = clean(latest).split(".").map(Number);
102
- const [b1, b2, b3] = clean(current).split(".").map(Number);
103
- if (a1 !== b1)
104
- return a1 > b1;
105
- if (a2 !== b2)
106
- return a2 > b2;
107
- if (a3 !== b3)
108
- return a3 > b3;
109
- // Same numeric version: a stable release is newer than a prerelease
110
- // (e.g. 1.0.0 is newer than 1.0.0-beta.1).
111
- if (current.includes("-") && !latest.includes("-"))
112
- return true;
113
- return false;
114
- }
115
- /**
116
- * Return true when the update check should be suppressed entirely.
117
- */
118
- export function isUpdateCheckDisabled() {
119
- return (process.env.NEBULA_AI_NO_UPDATE_CHECK === "1" ||
120
- process.env.NEBULA_AI_NO_UPDATE_CHECK === "true" ||
121
- process.env.CI === "true" ||
122
- !process.stderr.isTTY);
123
- }
124
- /**
125
- * Return true when running under `npx` (which always fetches the latest
126
- * wrapper, making the check redundant).
127
- */
128
- export function isNpx() {
129
- return process.env.npm_command === "exec";
130
- }
74
+ //
75
+ // Version/update-check helpers live in ./update-check-lib.ts and are
76
+ // re-exported above. Supply-chain helpers remain here because they import
77
+ // node:crypto and aren't used outside the launcher.
131
78
  export function sha256HexSync(buffer) {
132
79
  return createHash("sha256").update(buffer).digest("hex");
133
80
  }
@@ -288,7 +235,7 @@ function maybeSpawnUpdateCheck(currentVersion) {
288
235
  const cachePath = getUpdateCheckCachePath();
289
236
  // Skip if the cache is still fresh (stat is cheaper than read + parse).
290
237
  try {
291
- if (Date.now() - statSync(cachePath).mtimeMs < UPDATE_CHECK_TTL)
238
+ if (Date.now() - statSync(cachePath).mtimeMs < UPDATE_CHECK_TTL_MS)
292
239
  return;
293
240
  }
294
241
  catch {
@@ -331,16 +278,22 @@ function maybeSpawnUpdateCheck(currentVersion) {
331
278
  /**
332
279
  * Read the cached check result (written by a *previous* invocation's
333
280
  * background process) and print a one-line update notification to stderr
334
- * if a newer version is available.
281
+ * if a newer version is available. The suggested upgrade command is
282
+ * chosen based on the detected install method so pnpm/yarn/bun/brew users
283
+ * see an actually-applicable command.
335
284
  */
336
- function maybeShowUpdateNotification(currentVersion) {
285
+ function maybeShowUpdateNotification(currentVersion, installMethod) {
337
286
  try {
338
287
  const raw = readFileSync(getUpdateCheckCachePath(), "utf8");
339
288
  const cached = JSON.parse(raw);
340
289
  if (cached.latest && isNewerVersion(cached.latest, currentVersion)) {
341
- const cmd = currentVersion.includes("-dev.")
342
- ? "npm install -g nebula-ai@dev"
343
- : "npm install -g nebula-ai";
290
+ const channel = getChannel(currentVersion);
291
+ // Fall back to the canonical npm command when the install method
292
+ // is unknown — matches the wrapper's install-time docs.
293
+ const cmd = getUpgradeCommand(installMethod, channel) ||
294
+ (channel === "dev"
295
+ ? "npm install -g nebula-ai@dev"
296
+ : "npm install -g nebula-ai");
344
297
  process.stderr.write(`\nnebula-ai: update available ${currentVersion} \u2192 ${cached.latest}\n` +
345
298
  ` Run: ${cmd}\n`);
346
299
  }
@@ -404,12 +357,23 @@ async function main() {
404
357
  if (!isUpdateCheckDisabled() && !isNpx()) {
405
358
  maybeSpawnUpdateCheck(version);
406
359
  }
360
+ // Figure out how the user installed us (npm/pnpm/yarn/bun/npx/...) from
361
+ // the wrapper's own launch path + env. We forward the result to the Bun
362
+ // binary via NEBULA_AI_INSTALL_METHOD so the in-TUI "What's new" banner
363
+ // can surface the right upgrade command without redoing the detection.
364
+ const installMethod = detectInstallMethod({
365
+ execPath: fileURLToPath(import.meta.url),
366
+ argv0: process.argv[0],
367
+ npmCommand: process.env.npm_command,
368
+ npmUserAgent: process.env.npm_config_user_agent,
369
+ });
407
370
  const result = spawnSync(binaryPath, process.argv.slice(2), {
408
371
  stdio: "inherit",
372
+ env: { ...process.env, NEBULA_AI_INSTALL_METHOD: installMethod },
409
373
  });
410
374
  // Show update notification (reads PREVIOUS check's cached result).
411
375
  if (!isUpdateCheckDisabled() && !isNpx()) {
412
- maybeShowUpdateNotification(version);
376
+ maybeShowUpdateNotification(version, installMethod);
413
377
  }
414
378
  if (result.error) {
415
379
  console.error(`nebula-ai: unable to start the application.\n` +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nebula-ai",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "CLI/TUI for the nebula.gg AI task orchestration platform",
5
5
  "type": "module",
6
6
  "bin": {