ultracontext 1.1.0 → 1.1.4

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/README.md CHANGED
@@ -4,13 +4,11 @@
4
4
  </a>
5
5
  </p>
6
6
 
7
- <h3 align="center">The Context Hub for AI agents.</h3>
7
+ <h3 align="center">Context infrastructure for AI agents.</h3>
8
8
 
9
9
  <p align="center">
10
- <a href="https://ultracontext.ai/docs">Documentation</a>
11
- ·
12
- <a href="https://ultracontext.ai/docs/api-reference/introduction">API Reference</a>
13
- ·
10
+ <a href="https://ultracontext.ai/docs">Documentation</a> ·
11
+ <a href="https://ultracontext.ai/docs/api-reference/introduction">API Reference</a> ·
14
12
  <a href="https://ultracontext.ai/docs/changelog">Changelog</a>
15
13
  </p>
16
14
 
@@ -24,8 +22,8 @@
24
22
  <a href="https://github.com/ultracontext/ultracontext/blob/main/LICENSE">
25
23
  <img src="https://img.shields.io/github/license/ultracontext/ultracontext" alt="license" />
26
24
  </a>
27
- <a href="https://github.com/ultracontext/ultracontext">
28
- <img src="https://img.shields.io/github/stars/ultracontext/ultracontext.svg?style=social&label=Star" alt="GitHub stars" />
25
+ <a href="https://ultracontext.ai">
26
+ <img src="https://img.shields.io/badge/Visit-ultracontext.ai-4B6EF5" alt="Visit ultracontext.ai" />
29
27
  </a>
30
28
  </p>
31
29
 
@@ -33,94 +31,92 @@
33
31
  <a href="https://twitter.com/ultracontext">
34
32
  <img src="https://img.shields.io/badge/Follow%20on%20X-000000?style=for-the-badge&logo=x&logoColor=white" alt="Follow on X" />
35
33
  </a>
34
+ <a href="https://discord.com/invite/4HjcS6KwhW">
35
+ <img src="https://img.shields.io/badge/Join%20our%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" alt="Join our Discord" />
36
+ </a>
36
37
  </div>
37
38
 
38
- <div align="center">
39
+ ---
39
40
 
40
- ## All agents. All machines. One Hub.
41
+ <h2 align="center">All agents. One context.</h2>
41
42
 
42
- </div>
43
- <img src="https://ultracontext.ai/ultracontext-hub.gif" alt="How it works" />
43
+ Auto-capture and share your agents' context everywhere. Realtime. Open source.
44
44
 
45
- ## Introduction
45
+ ![ultracontext-gif](https://github.com/user-attachments/assets/be73afe5-161d-4fa3-8f4d-c4987fe63cb4)
46
46
 
47
- Agents now do our work. Today, most of our knowledge and key decisions live inside context windows. But they are spread across many agents, computers, and teams.
47
+ Everyone is shipping with agents. Few are shipping with agents together.
48
48
 
49
- Not anymore.
49
+ Multiple people, multiple agents, multiple machines. Our contexts are spread everywhere. There's no standard for context engineering. No infrastructure to build on. No fundamental building blocks to agree on. So we decided to make it.
50
50
 
51
- ## UltraContext Features
52
-
53
- - **Auto-ingest** — Captures contexts from your agents in realtime.
54
- - **Realtime access** — Immediately see contexts created on any machine, by any agent, whenever you want.
55
- - **Collaborate** — Share contexts across your team. See what everyone sees, instantly and without any friction.
56
- - **Switch between agents** — Pick up where one agent left off with another.
57
- - **Open source** — Own your data. Self-host when you need to.
58
- - **Plug and play** — Install and run with a single line of code.
59
- - **Fork & clone** — Continue contexts while preserving the full history.
60
- - **Customizable** — Add your own agents and extend behavior with the context API ([Docs here](https://ultracontext.ai/docs/api-reference/introduction)).
51
+ UltraContext is the context infrastructure. The API gives you git-like primitives for context engineering. The Hub lets you auto-capture, share, and collaborate across agents in realtime.
61
52
 
62
53
  ## Install
63
54
 
64
- Requires **Node >= 22**.
55
+ Requires Node >= 22.
65
56
 
66
57
  ```bash
67
58
  npm install -g ultracontext
68
59
  ```
69
60
 
70
- That's it. The setup wizard runs automatically — walks you through API key, sync preferences, and launches the dashboard.
61
+ ## The Hub
71
62
 
72
- Already installed? Run `ultracontext config` to reconfigure.
63
+ **All agents. One context.**
73
64
 
74
- ## Quick Start
65
+ The Hub lets you auto-capture, share, and collaborate across agents in realtime.
75
66
 
76
- ```bash
77
- ultracontext # start daemon + open dashboard
78
- ultracontext config # run setup wizard
79
- ultracontext start # start daemon only
80
- ultracontext stop # stop daemon
81
- ultracontext status # check if daemon is running
82
- ultracontext tui # open dashboard only
83
- ```
67
+ ### Features
84
68
 
85
- The default ultracontext command does everything: checks the daemon, starts it if needed, and opens the TUI dashboard.
69
+ - **Auto-capture** Ingests your agents' context in realtime. Zero config.
70
+ - **Switch between agents** — Pick up where one agent left off with another.
71
+ - **Collaborate** — Share contexts across your team. See what everyone sees. Realtime.
72
+ - **Fork & clone** — Continue contexts while preserving the full history.
73
+ - **Own your data** — Open source. Your contexts. Your rules.
86
74
 
87
- ## How it works
75
+ ### How it works
88
76
 
89
77
  1. A daemon runs in the background, watching your agents.
90
- 2. Contexts are ingested in realtime with the Context API.
91
- 3. Your Context Hub gets updated.
78
+ 2. Contexts are ingested in realtime.
79
+ 3. Your dashboard gets updated.
92
80
 
93
- We use a git-like context engineering API under the hood to interact with the agent's contexts. You can use it to add your own custom agents, tweak behavior and more. ([Docs here](https://ultracontext.ai/docs/))
81
+ ### Quick Start
94
82
 
95
- When you open an existing session from the hub, it forks the context, so the original context is always preserved by default and automatically versioned so you can keep track of it later using metadata.
83
+ ```bash
84
+ ultracontext # start daemon + open dashboard
85
+ ultracontext config # run setup wizard
86
+ ultracontext start # start daemon only
87
+ ultracontext stop # stop daemon
88
+ ultracontext status # check if daemon is running
89
+ ultracontext tui # open dashboard only
90
+ ```
96
91
 
97
- There is a local caching layer that prevents duplicate context creations and appends.
92
+ The default `ultracontext` command does everything: checks the daemon, starts it if needed, and opens the dashboard.
98
93
 
99
- ## Star History
94
+ When you open an existing session, it forks the context — the original is always preserved and automatically versioned. A local caching layer prevents duplicate context creations and appends.
100
95
 
101
- [![Star History Chart](https://api.star-history.com/svg?repos=ultracontext/ultracontext&type=date&legend=top-left)](https://www.star-history.com/#ultracontext/ultracontext&type=date&legend=top-left)
96
+ Add your own agents and extend behavior with the Context API. ([Docs here](https://ultracontext.ai/docs/))
102
97
 
103
- ## Everything we built so far
98
+ ## The API
104
99
 
105
- - Daemon - Runs in the background, watching your agents.
106
- - TUI - Terminal UI for the daemon.
107
- - [Context API](https://ultracontext.ai/docs/) - Git-like context engineering API.
108
- - [Context API SDKs](https://ultracontext.ai/docs/quickstart/nodejs) - Node and Python SDKs.
100
+ **Context engineering built like Git.**
109
101
 
110
- <div align="center">
102
+ The API gives you git-like primitives for context engineering, without the complexity.
111
103
 
112
- # The Context API
104
+ ### Features
113
105
 
114
- </div>
106
+ - **Five methods** — Create, get, append, update, delete. That's it.
107
+ - **Automatic versioning** — Every change creates a new version. Full history out of the box.
108
+ - **Time-travel** — Jump to any point in your context history.
109
+ - **Framework-agnostic** — Works with any LLM framework. No vendor lock-in.
110
+
111
+ The simplest way to control what your agents see. Replace messages, compact long context, replay decisions and roll back mistakes — all with a single API call.
115
112
 
116
- The Context API is the simplest way to control what your agents see. Replace messages, compact/offload long context, replay decisions and roll back mistakes with a single API call. Versioned context out of the box. Full history. Zero complexity. You can use the API standalone to build your own agents or to tweak behavior of existing ones in ultracontext. ([Docs here](https://ultracontext.ai/docs/api-reference/introduction))
113
+ Use the API standalone to build your own agents, or to extend existing ones in UltraContext.
117
114
 
118
- ## Context API SDKs
119
115
 
120
- | SDK | Install | Source |
121
- |-----|---------|--------|
122
- | JavaScript/TypeScript | `npm install ultracontext` | [apps/js-sdk](./apps/js-sdk) |
123
- | Python | `pip install ultracontext` | [apps/python-sdk](./apps/python-sdk) |
116
+ | SDK | Install | Source |
117
+ | --------------------- | -------------------------- | ------------------------------------ |
118
+ | JavaScript/TypeScript | `npm install ultracontext` | [apps/js-sdk](./apps/js-sdk) |
119
+ | Python | `pip install ultracontext` | [apps/python-sdk](./apps/python-sdk) |
124
120
 
125
121
 
126
122
  ### JavaScript/TypeScript
@@ -129,7 +125,7 @@ The Context API is the simplest way to control what your agents see. Replace mes
129
125
  npm install ultracontext
130
126
  ```
131
127
 
132
- ```js
128
+ ```typescript
133
129
  import { UltraContext } from 'ultracontext';
134
130
 
135
131
  const uc = new UltraContext({ apiKey: 'uc_live_...' });
@@ -159,12 +155,24 @@ uc.append(ctx["id"], {"role": "user", "content": "Hello!"})
159
155
  response = generate_text(model=model, messages=uc.get(ctx["id"])["data"])
160
156
  ```
161
157
 
162
- Get an API key from the [UltraContext Dashboard](https://ultracontext.ai/dashboard).
158
+ <p align="center">📚 Context API Guides</p>
159
+ <p align="center">
160
+ <a href="https://ultracontext.ai/docs/guides/store-retrieve-contexts">Store & Retrieve</a>
161
+ ·
162
+ <a href="https://ultracontext.ai/docs/guides/edit-contexts">Edit Contexts</a>
163
+ ·
164
+ <a href="https://ultracontext.ai/docs/guides/fork-clone-contexts">Fork & Clone</a>
165
+ ·
166
+ <a href="https://ultracontext.ai/docs/guides/view-context-history">View History</a>
167
+ </p>
168
+
169
+ ## Star History
170
+
171
+ [![Star History Chart](https://api.star-history.com/svg?repos=ultracontext/ultracontext-node&type=date&legend=top-left)](https://www.star-history.com/#ultracontext/ultracontext-node&type=date&legend=top-left)
172
+
163
173
 
164
174
  ## Documentation
165
175
 
166
176
  - [Quickstart](https://ultracontext.ai/docs/quickstart/nodejs) — Get running in 2 minutes
167
177
  - [Guides](https://ultracontext.ai/docs/guides/store-retrieve-contexts) — Practical patterns for common use cases
168
- - [API Reference](https://ultracontext.ai/docs/api-reference/introduction) — Full endpoint documentation
169
-
170
- ---
178
+ - [API Reference](https://ultracontext.ai/docs/api-reference/introduction) — Full endpoint documentation
@@ -3,10 +3,15 @@ import process from "node:process";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import path from "node:path";
5
5
  import fs from "node:fs";
6
+ import os from "node:os";
7
+ import { spawnSync } from "node:child_process";
6
8
 
7
9
  //#region src/cli/entry.mjs
8
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
11
  const command = (process.argv[2] ?? "").trim().toLowerCase().replace(/^--?/, "");
12
+ const PACKAGE_NAME = "ultracontext";
13
+ const packageRoot = path.resolve(__dirname, "..", "..");
14
+ const cliWrapperPath = path.join(packageRoot, "ultracontext.mjs");
10
15
  function readVersion() {
11
16
  try {
12
17
  const pkgPath = path.resolve(__dirname, "..", "..", "package.json");
@@ -19,7 +24,7 @@ function printHelp() {
19
24
  const version = readVersion();
20
25
  console.log(`ultracontext v${version}
21
26
 
22
- Usage: ultracontext [command]
27
+ Usage: ultracontext [command] [options]
23
28
 
24
29
  Commands:
25
30
  (none) Start daemon if needed, then open TUI
@@ -28,6 +33,7 @@ Commands:
28
33
  stop Stop a running daemon
29
34
  status Show daemon status
30
35
  tui Launch the interactive terminal UI
36
+ update Update CLI globally via npm/pnpm/bun
31
37
  version Print version
32
38
  help Show this help message
33
39
 
@@ -36,6 +42,16 @@ Environment:
36
42
  ULTRACONTEXT_BASE_URL API base URL (default: https://api.ultracontext.ai)
37
43
  `);
38
44
  }
45
+ function printUpdateHelp() {
46
+ console.log(`Usage: ultracontext update [options]
47
+
48
+ Options:
49
+ --tag <dist-tag|version> Package tag/version (default: latest)
50
+ --manager <npm|pnpm|bun> Force package manager (auto-detected by default)
51
+ --no-restart Do not restart daemon after update
52
+ -h, --help Show this help message
53
+ `);
54
+ }
39
55
  const NEEDS_KEY = new Set([
40
56
  "",
41
57
  "start",
@@ -65,7 +81,257 @@ function loadApiKeyFromConfig() {
65
81
  if (cfg.apiKey) process.env.ULTRACONTEXT_API_KEY = String(cfg.apiKey);
66
82
  } catch {}
67
83
  }
84
+ function normalizeTag(value) {
85
+ const trimmed = String(value ?? "").trim();
86
+ if (!trimmed) return "latest";
87
+ if (trimmed.startsWith(`${PACKAGE_NAME}@`)) return trimmed.slice(`${PACKAGE_NAME}@`.length);
88
+ return trimmed;
89
+ }
90
+ function safeRealpath(targetPath) {
91
+ try {
92
+ return fs.realpathSync(targetPath);
93
+ } catch {
94
+ return path.resolve(targetPath);
95
+ }
96
+ }
97
+ function pathsEqual(left, right) {
98
+ return path.resolve(left) === path.resolve(right);
99
+ }
100
+ function runCapture(commandName, args) {
101
+ const result = spawnSync(commandName, args, {
102
+ env: process.env,
103
+ encoding: "utf8"
104
+ });
105
+ return {
106
+ ok: result.status === 0,
107
+ stdout: String(result.stdout ?? ""),
108
+ stderr: String(result.stderr ?? "")
109
+ };
110
+ }
111
+ function resolveBunGlobalRoot() {
112
+ const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
113
+ const bunInstall = String(process.env.BUN_INSTALL ?? "").trim() || path.join(home, ".bun");
114
+ return path.join(bunInstall, "install", "global", "node_modules");
115
+ }
116
+ function detectGlobalInstallManager() {
117
+ const packageRootReal = safeRealpath(packageRoot);
118
+ const npmGlobalRoot = runCapture("npm", ["root", "-g"]);
119
+ if (npmGlobalRoot.ok) {
120
+ const npmRoot = npmGlobalRoot.stdout.trim();
121
+ if (npmRoot) {
122
+ if (pathsEqual(safeRealpath(path.join(npmRoot, PACKAGE_NAME)), packageRootReal)) return "npm";
123
+ }
124
+ }
125
+ const pnpmGlobalRoot = runCapture("pnpm", ["root", "-g"]);
126
+ if (pnpmGlobalRoot.ok) {
127
+ const pnpmRoot = pnpmGlobalRoot.stdout.trim();
128
+ if (pnpmRoot) {
129
+ if (pathsEqual(safeRealpath(path.join(pnpmRoot, PACKAGE_NAME)), packageRootReal)) return "pnpm";
130
+ }
131
+ }
132
+ if (pathsEqual(safeRealpath(path.join(resolveBunGlobalRoot(), PACKAGE_NAME)), packageRootReal)) return "bun";
133
+ return null;
134
+ }
135
+ function parseUpdateOptions(args) {
136
+ const opts = {
137
+ tag: "latest",
138
+ manager: null,
139
+ restart: true,
140
+ help: false
141
+ };
142
+ for (let i = 0; i < args.length; i += 1) {
143
+ const arg = String(args[i] ?? "").trim();
144
+ if (!arg) continue;
145
+ if (arg === "-h" || arg === "--help") {
146
+ opts.help = true;
147
+ continue;
148
+ }
149
+ if (arg === "--no-restart") {
150
+ opts.restart = false;
151
+ continue;
152
+ }
153
+ if (arg === "--tag") {
154
+ const next = args[i + 1];
155
+ if (!next || String(next).startsWith("-")) throw new Error("Missing value for --tag");
156
+ opts.tag = normalizeTag(next);
157
+ i += 1;
158
+ continue;
159
+ }
160
+ if (arg === "--manager") {
161
+ const next = String(args[i + 1] ?? "").trim().toLowerCase();
162
+ if (!next || next.startsWith("-")) throw new Error("Missing value for --manager");
163
+ if (![
164
+ "npm",
165
+ "pnpm",
166
+ "bun"
167
+ ].includes(next)) throw new Error(`Invalid --manager value: ${next}`);
168
+ opts.manager = next;
169
+ i += 1;
170
+ continue;
171
+ }
172
+ throw new Error(`Unknown update option: ${arg}`);
173
+ }
174
+ return opts;
175
+ }
176
+ function runCliSubcommand(subcommand) {
177
+ return spawnSync(process.execPath, [cliWrapperPath, subcommand], {
178
+ env: process.env,
179
+ stdio: "inherit"
180
+ }).status ?? 1;
181
+ }
182
+ function runGlobalUpdate(manager, tag) {
183
+ const spec = `${PACKAGE_NAME}@${normalizeTag(tag)}`;
184
+ const tuple = {
185
+ npm: ["npm", [
186
+ "i",
187
+ "-g",
188
+ spec
189
+ ]],
190
+ pnpm: ["pnpm", [
191
+ "add",
192
+ "-g",
193
+ spec
194
+ ]],
195
+ bun: ["bun", [
196
+ "add",
197
+ "-g",
198
+ spec
199
+ ]]
200
+ }[manager];
201
+ if (!tuple) throw new Error(`Unsupported package manager: ${manager}`);
202
+ const [bin, args] = tuple;
203
+ if (spawnSync(bin, args, {
204
+ env: process.env,
205
+ stdio: "inherit"
206
+ }).status !== 0) throw new Error(`Update failed while running: ${bin} ${args.join(" ")}`);
207
+ }
208
+ const isTTY = process.stdout.isTTY;
209
+ const esc = (code) => isTTY ? `\x1b[${code}m` : "";
210
+ const r = esc(0);
211
+ const b = esc(1);
212
+ const d = esc(2);
213
+ const blue = esc("38;2;47;111;179");
214
+ const green = esc("38;2;80;200;120");
215
+ esc("38;2;220;80;80");
216
+ const cyan = esc("36");
217
+ const gray = esc("38;5;245");
218
+ function runUpdate(rawArgs) {
219
+ const opts = parseUpdateOptions(rawArgs);
220
+ if (opts.help) {
221
+ printUpdateHelp();
222
+ return;
223
+ }
224
+ const manager = opts.manager ?? detectGlobalInstallManager();
225
+ if (!manager) throw new Error("Could not detect install manager for this CLI. Re-run with --manager <npm|pnpm|bun>.");
226
+ const previousVersion = readVersion();
227
+ console.log("");
228
+ console.log(` ${blue}${b}UltraContext${r} ${d}Update${r}`);
229
+ console.log("");
230
+ const wasRunning = isDaemonRunning();
231
+ if (wasRunning && opts.restart) {
232
+ console.log(` ${gray}○${r} ${d}Stopping daemon...${r}`);
233
+ const stopCode = runCliSubcommand("stop");
234
+ if (stopCode !== 0) throw new Error(`Failed to stop daemon before update (exit ${stopCode}).`);
235
+ }
236
+ console.log(` ${gray}↓${r} ${d}Updating via ${manager}${r} ${gray}(${PACKAGE_NAME}@${opts.tag})${r}`);
237
+ runGlobalUpdate(manager, opts.tag);
238
+ const newVersion = readVersion();
239
+ console.log("");
240
+ console.log(` ${green}✓${r} ${b}Updated${r} ${gray}${previousVersion} → ${newVersion}${r}`);
241
+ if (wasRunning && opts.restart) {
242
+ console.log(` ${green}●${r} ${d}Restarting daemon...${r}`);
243
+ const startCode = runCliSubcommand("start");
244
+ if (startCode !== 0) throw new Error(`Update succeeded but daemon restart failed (exit ${startCode}).`);
245
+ } else if (wasRunning) console.log(` ${gray}○${r} ${d}Daemon was stopped. Run:${r} ${cyan}ultracontext start${r}`);
246
+ console.log("");
247
+ }
248
+ const UPDATE_CHECK_INTERVAL = 10800 * 1e3;
249
+ const SKIP_UPDATE_CHECK = new Set([
250
+ "version",
251
+ "v",
252
+ "update",
253
+ "upgrade",
254
+ "help",
255
+ "h",
256
+ "stop",
257
+ "status"
258
+ ]);
259
+ function getUpdateCheckPath() {
260
+ const home = process.env.HOME || process.env.USERPROFILE || "~";
261
+ return path.join(home, ".ultracontext", "update-check.json");
262
+ }
263
+ function readUpdateCache() {
264
+ try {
265
+ return JSON.parse(fs.readFileSync(getUpdateCheckPath(), "utf8"));
266
+ } catch {
267
+ return null;
268
+ }
269
+ }
270
+ function writeUpdateCache(data) {
271
+ try {
272
+ fs.writeFileSync(getUpdateCheckPath(), JSON.stringify(data));
273
+ } catch {}
274
+ }
275
+ async function fetchLatestVersion() {
276
+ const controller = new AbortController();
277
+ const timeout = setTimeout(() => controller.abort(), 3e3);
278
+ try {
279
+ return (await (await fetch("https://registry.npmjs.org/ultracontext/latest", { signal: controller.signal })).json()).version ?? null;
280
+ } catch {
281
+ return null;
282
+ } finally {
283
+ clearTimeout(timeout);
284
+ }
285
+ }
286
+ function isNewer(latest, current) {
287
+ const l = latest.split(".").map(Number);
288
+ const c = current.split(".").map(Number);
289
+ for (let i = 0; i < 3; i++) {
290
+ if ((l[i] ?? 0) > (c[i] ?? 0)) return true;
291
+ if ((l[i] ?? 0) < (c[i] ?? 0)) return false;
292
+ }
293
+ return false;
294
+ }
295
+ function printUpdateNotice(current, latest) {
296
+ console.log(`\n ${blue}${b}UltraContext${r} ${d}Update available${r}`);
297
+ console.log(` ${gray}${current}${r} → ${green}${b}${latest}${r}`);
298
+ console.log(` Run ${cyan}ultracontext update${r} to upgrade.\n`);
299
+ }
300
+ async function checkForUpdate() {
301
+ const current = readVersion();
302
+ if (current === "unknown") return;
303
+ const cache = readUpdateCache();
304
+ if (cache?.lastCheck && Date.now() - cache.lastCheck < UPDATE_CHECK_INTERVAL) {
305
+ if (cache.latestVersion && isNewer(cache.latestVersion, current)) printUpdateNotice(current, cache.latestVersion);
306
+ return;
307
+ }
308
+ const latest = await fetchLatestVersion();
309
+ writeUpdateCache({
310
+ lastCheck: Date.now(),
311
+ latestVersion: latest
312
+ });
313
+ if (latest && isNewer(latest, current)) printUpdateNotice(current, latest);
314
+ }
315
+ async function launchDaemonSDK() {
316
+ const { launchDaemon } = await import("@ultracontext/daemon/launcher");
317
+ await launchDaemon({
318
+ entryPath: fileURLToPath(new URL("./sdk-daemon.mjs", import.meta.url)),
319
+ diagnosticsHint: "DAEMON_VERBOSE=1 ultracontext start"
320
+ });
321
+ }
322
+ async function runCtlSDK() {
323
+ const { runCtl } = await import("@ultracontext/daemon/ctl");
324
+ await runCtl();
325
+ }
326
+ async function launchTuiSDK() {
327
+ const { tuiBoot } = await import("@ultracontext/tui/tui");
328
+ await tuiBoot({
329
+ assetsRoot: path.resolve(__dirname, "..", ".."),
330
+ offlineNotice: "Daemon offline. Run: ultracontext start"
331
+ });
332
+ }
68
333
  async function run() {
334
+ if (!SKIP_UPDATE_CHECK.has(command)) await checkForUpdate();
69
335
  let onboardResult = null;
70
336
  if (NEEDS_KEY.has(command)) {
71
337
  loadApiKeyFromConfig();
@@ -73,33 +339,37 @@ async function run() {
73
339
  }
74
340
  switch (command) {
75
341
  case "start":
76
- await import("./daemon/launcher.mjs");
77
- if (onboardResult?.launchTui) await import("./tui/index.mjs");
342
+ await launchDaemonSDK();
343
+ if (onboardResult?.launchTui) await launchTuiSDK();
78
344
  break;
79
345
  case "stop":
80
346
  process.argv[2] = "stop";
81
- await import("./daemon/ctl.mjs");
347
+ await runCtlSDK();
82
348
  break;
83
349
  case "status":
84
350
  process.argv[2] = "status";
85
- await import("./daemon/ctl.mjs");
351
+ await runCtlSDK();
86
352
  break;
87
353
  case "config":
88
354
  if ((await runOnboarding())?.launchTui) {
89
- if (!isDaemonRunning()) await import("./daemon/launcher.mjs");
90
- await import("./tui/index.mjs");
355
+ if (!isDaemonRunning()) await launchDaemonSDK();
356
+ await launchTuiSDK();
91
357
  }
92
358
  break;
93
359
  case "tui":
94
- await import("./tui/index.mjs");
360
+ await launchTuiSDK();
95
361
  break;
96
362
  case "version":
97
363
  case "v":
98
364
  console.log(readVersion());
99
365
  break;
366
+ case "update":
367
+ case "upgrade":
368
+ runUpdate(process.argv.slice(3));
369
+ break;
100
370
  case "":
101
- if (!isDaemonRunning()) await import("./daemon/launcher.mjs");
102
- await import("./tui/index.mjs");
371
+ if (!isDaemonRunning()) await launchDaemonSDK();
372
+ await launchTuiSDK();
103
373
  break;
104
374
  case "help":
105
375
  case "h":
@@ -1 +1 @@
1
- {"version":3,"file":"entry.mjs","names":[],"sources":["../../src/cli/entry.mjs"],"sourcesContent":["#!/usr/bin/env node\n\n// CLI router — dispatches subcommands to daemon/tui entry points\nimport process from \"node:process\";\nimport { fileURLToPath } from \"node:url\";\nimport path from \"node:path\";\nimport fs from \"node:fs\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst command = (process.argv[2] ?? \"\").trim().toLowerCase().replace(/^--?/, \"\");\n\n// resolve package version\nfunction readVersion() {\n try {\n const pkgPath = path.resolve(__dirname, \"..\", \"..\", \"package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n return pkg.version ?? \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nfunction printHelp() {\n const version = readVersion();\n console.log(`ultracontext v${version}\n\nUsage: ultracontext [command]\n\nCommands:\n (none) Start daemon if needed, then open TUI\n config Run the setup wizard\n start Start the daemon in the background\n stop Stop a running daemon\n status Show daemon status\n tui Launch the interactive terminal UI\n version Print version\n help Show this help message\n\nEnvironment:\n ULTRACONTEXT_API_KEY Required. Your UltraContext API key.\n ULTRACONTEXT_BASE_URL API base URL (default: https://api.ultracontext.ai)\n`);\n}\n\n// commands that need an API key\nconst NEEDS_KEY = new Set([\"\", \"start\", \"tui\"]);\n\n// interactive onboarding wizard (Ink-based), returns { launchTui }\nasync function runOnboarding() {\n const { onboard } = await import(\"./onboarding.mjs\");\n return onboard();\n}\n\n// check if daemon is already running via lock file\nfunction isDaemonRunning() {\n try {\n const lockPath = path.join(process.env.HOME || process.env.USERPROFILE || \"~\", \".ultracontext\", \"daemon.lock\");\n const lock = JSON.parse(fs.readFileSync(lockPath, \"utf8\"));\n const pid = Number.parseInt(String(lock?.pid ?? \"\"), 10);\n if (!Number.isInteger(pid) || pid <= 1) return false;\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n// try loading key from config file if env is empty\nfunction loadApiKeyFromConfig() {\n if (process.env.ULTRACONTEXT_API_KEY) return;\n try {\n const configPath = path.join(process.env.HOME || process.env.USERPROFILE || \"~\", \".ultracontext\", \"config.json\");\n const cfg = JSON.parse(fs.readFileSync(configPath, \"utf8\"));\n if (cfg.apiKey) process.env.ULTRACONTEXT_API_KEY = String(cfg.apiKey);\n } catch { /* no config */ }\n}\n\nasync function run() {\n // load saved key, then onboard if still missing\n let onboardResult = null;\n if (NEEDS_KEY.has(command)) {\n loadApiKeyFromConfig();\n if (!process.env.ULTRACONTEXT_API_KEY) onboardResult = await runOnboarding();\n }\n\n switch (command) {\n case \"start\":\n await import(\"./daemon/launcher.mjs\");\n if (onboardResult?.launchTui) await import(\"./tui/index.mjs\");\n break;\n\n case \"stop\":\n process.argv[2] = \"stop\";\n await import(\"./daemon/ctl.mjs\");\n break;\n\n case \"status\":\n process.argv[2] = \"status\";\n await import(\"./daemon/ctl.mjs\");\n break;\n\n case \"config\": {\n const configResult = await runOnboarding();\n if (configResult?.launchTui) {\n if (!isDaemonRunning()) await import(\"./daemon/launcher.mjs\");\n await import(\"./tui/index.mjs\");\n }\n break;\n }\n\n case \"tui\":\n await import(\"./tui/index.mjs\");\n break;\n\n case \"version\":\n case \"v\":\n console.log(readVersion());\n break;\n\n // default: ensure daemon running, then open TUI\n case \"\": {\n if (!isDaemonRunning()) await import(\"./daemon/launcher.mjs\");\n await import(\"./tui/index.mjs\");\n break;\n }\n\n case \"help\":\n case \"h\":\n printHelp();\n break;\n\n default:\n console.error(`Unknown command: ${process.argv[2]}`);\n printHelp();\n process.exit(1);\n }\n}\n\nrun().catch((error) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n});\n"],"mappings":";;;;;;;AAQA,MAAM,YAAY,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAC9D,MAAM,WAAW,QAAQ,KAAK,MAAM,IAAI,MAAM,CAAC,aAAa,CAAC,QAAQ,QAAQ,GAAG;AAGhF,SAAS,cAAc;AACrB,KAAI;EACF,MAAM,UAAU,KAAK,QAAQ,WAAW,MAAM,MAAM,eAAe;AAEnE,SADY,KAAK,MAAM,GAAG,aAAa,SAAS,OAAO,CAAC,CAC7C,WAAW;SAChB;AACN,SAAO;;;AAIX,SAAS,YAAY;CACnB,MAAM,UAAU,aAAa;AAC7B,SAAQ,IAAI,iBAAiB,QAAQ;;;;;;;;;;;;;;;;;EAiBrC;;AAIF,MAAM,YAAY,IAAI,IAAI;CAAC;CAAI;CAAS;CAAM,CAAC;AAG/C,eAAe,gBAAgB;CAC7B,MAAM,EAAE,YAAY,MAAM,OAAO;AACjC,QAAO,SAAS;;AAIlB,SAAS,kBAAkB;AACzB,KAAI;EACF,MAAM,WAAW,KAAK,KAAK,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe,KAAK,iBAAiB,cAAc;EAC9G,MAAM,OAAO,KAAK,MAAM,GAAG,aAAa,UAAU,OAAO,CAAC;EAC1D,MAAM,MAAM,OAAO,SAAS,OAAO,MAAM,OAAO,GAAG,EAAE,GAAG;AACxD,MAAI,CAAC,OAAO,UAAU,IAAI,IAAI,OAAO,EAAG,QAAO;AAC/C,UAAQ,KAAK,KAAK,EAAE;AACpB,SAAO;SACD;AACN,SAAO;;;AAKX,SAAS,uBAAuB;AAC9B,KAAI,QAAQ,IAAI,qBAAsB;AACtC,KAAI;EACF,MAAM,aAAa,KAAK,KAAK,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe,KAAK,iBAAiB,cAAc;EAChH,MAAM,MAAM,KAAK,MAAM,GAAG,aAAa,YAAY,OAAO,CAAC;AAC3D,MAAI,IAAI,OAAQ,SAAQ,IAAI,uBAAuB,OAAO,IAAI,OAAO;SAC/D;;AAGV,eAAe,MAAM;CAEnB,IAAI,gBAAgB;AACpB,KAAI,UAAU,IAAI,QAAQ,EAAE;AAC1B,wBAAsB;AACtB,MAAI,CAAC,QAAQ,IAAI,qBAAsB,iBAAgB,MAAM,eAAe;;AAG9E,SAAQ,SAAR;EACE,KAAK;AACH,SAAM,OAAO;AACb,OAAI,eAAe,UAAW,OAAM,OAAO;AAC3C;EAEF,KAAK;AACH,WAAQ,KAAK,KAAK;AAClB,SAAM,OAAO;AACb;EAEF,KAAK;AACH,WAAQ,KAAK,KAAK;AAClB,SAAM,OAAO;AACb;EAEF,KAAK;AAEH,QADqB,MAAM,eAAe,GACxB,WAAW;AAC3B,QAAI,CAAC,iBAAiB,CAAE,OAAM,OAAO;AACrC,UAAM,OAAO;;AAEf;EAGF,KAAK;AACH,SAAM,OAAO;AACb;EAEF,KAAK;EACL,KAAK;AACH,WAAQ,IAAI,aAAa,CAAC;AAC1B;EAGF,KAAK;AACH,OAAI,CAAC,iBAAiB,CAAE,OAAM,OAAO;AACrC,SAAM,OAAO;AACb;EAGF,KAAK;EACL,KAAK;AACH,cAAW;AACX;EAEF;AACE,WAAQ,MAAM,oBAAoB,QAAQ,KAAK,KAAK;AACpD,cAAW;AACX,WAAQ,KAAK,EAAE;;;AAIrB,KAAK,CAAC,OAAO,UAAU;AACrB,SAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CAAC;AACrE,SAAQ,KAAK,EAAE;EACf"}
1
+ {"version":3,"file":"entry.mjs","names":[],"sources":["../../src/cli/entry.mjs"],"sourcesContent":["#!/usr/bin/env node\n\n// CLI router — dispatches subcommands to daemon/tui entry points\nimport process from \"node:process\";\nimport { fileURLToPath } from \"node:url\";\nimport path from \"node:path\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport { spawnSync } from \"node:child_process\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst command = (process.argv[2] ?? \"\").trim().toLowerCase().replace(/^--?/, \"\");\nconst PACKAGE_NAME = \"ultracontext\";\nconst packageRoot = path.resolve(__dirname, \"..\", \"..\");\nconst cliWrapperPath = path.join(packageRoot, \"ultracontext.mjs\");\n\n// resolve package version\nfunction readVersion() {\n try {\n const pkgPath = path.resolve(__dirname, \"..\", \"..\", \"package.json\");\n const pkg = JSON.parse(fs.readFileSync(pkgPath, \"utf8\"));\n return pkg.version ?? \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nfunction printHelp() {\n const version = readVersion();\n console.log(`ultracontext v${version}\n\nUsage: ultracontext [command] [options]\n\nCommands:\n (none) Start daemon if needed, then open TUI\n config Run the setup wizard\n start Start the daemon in the background\n stop Stop a running daemon\n status Show daemon status\n tui Launch the interactive terminal UI\n update Update CLI globally via npm/pnpm/bun\n version Print version\n help Show this help message\n\nEnvironment:\n ULTRACONTEXT_API_KEY Required. Your UltraContext API key.\n ULTRACONTEXT_BASE_URL API base URL (default: https://api.ultracontext.ai)\n`);\n}\n\nfunction printUpdateHelp() {\n console.log(`Usage: ultracontext update [options]\n\nOptions:\n --tag <dist-tag|version> Package tag/version (default: latest)\n --manager <npm|pnpm|bun> Force package manager (auto-detected by default)\n --no-restart Do not restart daemon after update\n -h, --help Show this help message\n`);\n}\n\n// commands that need an API key\nconst NEEDS_KEY = new Set([\"\", \"start\", \"tui\"]);\n\n// interactive onboarding wizard (Ink-based), returns { launchTui }\nasync function runOnboarding() {\n const { onboard } = await import(\"./onboarding.mjs\");\n return onboard();\n}\n\n// check if daemon is already running via lock file\nfunction isDaemonRunning() {\n try {\n const lockPath = path.join(process.env.HOME || process.env.USERPROFILE || \"~\", \".ultracontext\", \"daemon.lock\");\n const lock = JSON.parse(fs.readFileSync(lockPath, \"utf8\"));\n const pid = Number.parseInt(String(lock?.pid ?? \"\"), 10);\n if (!Number.isInteger(pid) || pid <= 1) return false;\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n// try loading key from config file if env is empty\nfunction loadApiKeyFromConfig() {\n if (process.env.ULTRACONTEXT_API_KEY) return;\n try {\n const configPath = path.join(process.env.HOME || process.env.USERPROFILE || \"~\", \".ultracontext\", \"config.json\");\n const cfg = JSON.parse(fs.readFileSync(configPath, \"utf8\"));\n if (cfg.apiKey) process.env.ULTRACONTEXT_API_KEY = String(cfg.apiKey);\n } catch { /* no config */ }\n}\n\nfunction normalizeTag(value) {\n const trimmed = String(value ?? \"\").trim();\n if (!trimmed) return \"latest\";\n if (trimmed.startsWith(`${PACKAGE_NAME}@`)) return trimmed.slice(`${PACKAGE_NAME}@`.length);\n return trimmed;\n}\n\nfunction safeRealpath(targetPath) {\n try {\n return fs.realpathSync(targetPath);\n } catch {\n return path.resolve(targetPath);\n }\n}\n\nfunction pathsEqual(left, right) {\n return path.resolve(left) === path.resolve(right);\n}\n\nfunction runCapture(commandName, args) {\n const result = spawnSync(commandName, args, {\n env: process.env,\n encoding: \"utf8\",\n });\n return {\n ok: result.status === 0,\n stdout: String(result.stdout ?? \"\"),\n stderr: String(result.stderr ?? \"\"),\n };\n}\n\nfunction resolveBunGlobalRoot() {\n const home = process.env.HOME || process.env.USERPROFILE || os.homedir();\n const bunInstall = String(process.env.BUN_INSTALL ?? \"\").trim() || path.join(home, \".bun\");\n return path.join(bunInstall, \"install\", \"global\", \"node_modules\");\n}\n\nfunction detectGlobalInstallManager() {\n const packageRootReal = safeRealpath(packageRoot);\n const npmGlobalRoot = runCapture(\"npm\", [\"root\", \"-g\"]);\n if (npmGlobalRoot.ok) {\n const npmRoot = npmGlobalRoot.stdout.trim();\n if (npmRoot) {\n const npmExpected = safeRealpath(path.join(npmRoot, PACKAGE_NAME));\n if (pathsEqual(npmExpected, packageRootReal)) return \"npm\";\n }\n }\n\n const pnpmGlobalRoot = runCapture(\"pnpm\", [\"root\", \"-g\"]);\n if (pnpmGlobalRoot.ok) {\n const pnpmRoot = pnpmGlobalRoot.stdout.trim();\n if (pnpmRoot) {\n const pnpmExpected = safeRealpath(path.join(pnpmRoot, PACKAGE_NAME));\n if (pathsEqual(pnpmExpected, packageRootReal)) return \"pnpm\";\n }\n }\n\n const bunExpected = safeRealpath(path.join(resolveBunGlobalRoot(), PACKAGE_NAME));\n if (pathsEqual(bunExpected, packageRootReal)) return \"bun\";\n\n return null;\n}\n\nfunction parseUpdateOptions(args) {\n const opts = {\n tag: \"latest\",\n manager: null,\n restart: true,\n help: false,\n };\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = String(args[i] ?? \"\").trim();\n if (!arg) continue;\n\n if (arg === \"-h\" || arg === \"--help\") {\n opts.help = true;\n continue;\n }\n\n if (arg === \"--no-restart\") {\n opts.restart = false;\n continue;\n }\n\n if (arg === \"--tag\") {\n const next = args[i + 1];\n if (!next || String(next).startsWith(\"-\")) {\n throw new Error(\"Missing value for --tag\");\n }\n opts.tag = normalizeTag(next);\n i += 1;\n continue;\n }\n\n if (arg === \"--manager\") {\n const next = String(args[i + 1] ?? \"\").trim().toLowerCase();\n if (!next || next.startsWith(\"-\")) {\n throw new Error(\"Missing value for --manager\");\n }\n if (![\"npm\", \"pnpm\", \"bun\"].includes(next)) {\n throw new Error(`Invalid --manager value: ${next}`);\n }\n opts.manager = next;\n i += 1;\n continue;\n }\n\n throw new Error(`Unknown update option: ${arg}`);\n }\n\n return opts;\n}\n\nfunction runCliSubcommand(subcommand) {\n const result = spawnSync(process.execPath, [cliWrapperPath, subcommand], {\n env: process.env,\n stdio: \"inherit\",\n });\n return result.status ?? 1;\n}\n\nfunction runGlobalUpdate(manager, tag) {\n const spec = `${PACKAGE_NAME}@${normalizeTag(tag)}`;\n const argvByManager = {\n npm: [\"npm\", [\"i\", \"-g\", spec]],\n pnpm: [\"pnpm\", [\"add\", \"-g\", spec]],\n bun: [\"bun\", [\"add\", \"-g\", spec]],\n };\n\n const tuple = argvByManager[manager];\n if (!tuple) {\n throw new Error(`Unsupported package manager: ${manager}`);\n }\n\n const [bin, args] = tuple;\n const result = spawnSync(bin, args, {\n env: process.env,\n stdio: \"inherit\",\n });\n if (result.status !== 0) {\n throw new Error(`Update failed while running: ${bin} ${args.join(\" \")}`);\n }\n}\n\n// ── ANSI helpers ────────────────────────────────────────────────\n\nconst isTTY = process.stdout.isTTY;\nconst esc = (code) => (isTTY ? `\\x1b[${code}m` : \"\");\nconst r = esc(0);\nconst b = esc(1);\nconst d = esc(2);\nconst blue = esc(\"38;2;47;111;179\");\nconst green = esc(\"38;2;80;200;120\");\nconst red = esc(\"38;2;220;80;80\");\nconst cyan = esc(\"36\");\nconst gray = esc(\"38;5;245\");\n\nfunction runUpdate(rawArgs) {\n const opts = parseUpdateOptions(rawArgs);\n if (opts.help) {\n printUpdateHelp();\n return;\n }\n\n const manager = opts.manager ?? detectGlobalInstallManager();\n if (!manager) {\n throw new Error(\n \"Could not detect install manager for this CLI. Re-run with --manager <npm|pnpm|bun>.\",\n );\n }\n\n const previousVersion = readVersion();\n\n console.log(\"\");\n console.log(` ${blue}${b}UltraContext${r} ${d}Update${r}`);\n console.log(\"\");\n\n // stop daemon before update\n const wasRunning = isDaemonRunning();\n if (wasRunning && opts.restart) {\n console.log(` ${gray}○${r} ${d}Stopping daemon...${r}`);\n const stopCode = runCliSubcommand(\"stop\");\n if (stopCode !== 0) {\n throw new Error(`Failed to stop daemon before update (exit ${stopCode}).`);\n }\n }\n\n // run update\n console.log(` ${gray}↓${r} ${d}Updating via ${manager}${r} ${gray}(${PACKAGE_NAME}@${opts.tag})${r}`);\n runGlobalUpdate(manager, opts.tag);\n\n // read new version after update\n const newVersion = readVersion();\n console.log(\"\");\n console.log(` ${green}✓${r} ${b}Updated${r} ${gray}${previousVersion} → ${newVersion}${r}`);\n\n // restart daemon\n if (wasRunning && opts.restart) {\n console.log(` ${green}●${r} ${d}Restarting daemon...${r}`);\n const startCode = runCliSubcommand(\"start\");\n if (startCode !== 0) {\n throw new Error(`Update succeeded but daemon restart failed (exit ${startCode}).`);\n }\n } else if (wasRunning) {\n console.log(` ${gray}○${r} ${d}Daemon was stopped. Run:${r} ${cyan}ultracontext start${r}`);\n }\n\n console.log(\"\");\n}\n\n// ── update check ────────────────────────────────────────────────\n\nconst UPDATE_CHECK_INTERVAL = 3 * 60 * 60 * 1000; // 3h\nconst SKIP_UPDATE_CHECK = new Set([\"version\", \"v\", \"update\", \"upgrade\", \"help\", \"h\", \"stop\", \"status\"]);\n\nfunction getUpdateCheckPath() {\n const home = process.env.HOME || process.env.USERPROFILE || \"~\";\n return path.join(home, \".ultracontext\", \"update-check.json\");\n}\n\nfunction readUpdateCache() {\n try { return JSON.parse(fs.readFileSync(getUpdateCheckPath(), \"utf8\")); }\n catch { return null; }\n}\n\nfunction writeUpdateCache(data) {\n try { fs.writeFileSync(getUpdateCheckPath(), JSON.stringify(data)); }\n catch { /* best effort */ }\n}\n\nasync function fetchLatestVersion() {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 3000);\n try {\n const res = await fetch(\"https://registry.npmjs.org/ultracontext/latest\", { signal: controller.signal });\n const data = await res.json();\n return data.version ?? null;\n } catch { return null; }\n finally { clearTimeout(timeout); }\n}\n\nfunction isNewer(latest, current) {\n const l = latest.split(\".\").map(Number);\n const c = current.split(\".\").map(Number);\n for (let i = 0; i < 3; i++) {\n if ((l[i] ?? 0) > (c[i] ?? 0)) return true;\n if ((l[i] ?? 0) < (c[i] ?? 0)) return false;\n }\n return false;\n}\n\nfunction printUpdateNotice(current, latest) {\n console.log(`\\n ${blue}${b}UltraContext${r} ${d}Update available${r}`);\n console.log(` ${gray}${current}${r} → ${green}${b}${latest}${r}`);\n console.log(` Run ${cyan}ultracontext update${r} to upgrade.\\n`);\n}\n\nasync function checkForUpdate() {\n const current = readVersion();\n if (current === \"unknown\") return;\n\n // use cache if fresh\n const cache = readUpdateCache();\n if (cache?.lastCheck && Date.now() - cache.lastCheck < UPDATE_CHECK_INTERVAL) {\n if (cache.latestVersion && isNewer(cache.latestVersion, current)) {\n printUpdateNotice(current, cache.latestVersion);\n }\n return;\n }\n\n // fetch from registry\n const latest = await fetchLatestVersion();\n writeUpdateCache({ lastCheck: Date.now(), latestVersion: latest });\n if (latest && isNewer(latest, current)) {\n printUpdateNotice(current, latest);\n }\n}\n\n// ── launch helpers ──────────────────────────────────────────────\n\nasync function launchDaemonSDK() {\n const { launchDaemon } = await import(\"@ultracontext/daemon/launcher\");\n await launchDaemon({\n entryPath: fileURLToPath(new URL(\"./sdk-daemon.mjs\", import.meta.url)),\n diagnosticsHint: \"DAEMON_VERBOSE=1 ultracontext start\",\n });\n}\n\nasync function runCtlSDK() {\n const { runCtl } = await import(\"@ultracontext/daemon/ctl\");\n await runCtl();\n}\n\nasync function launchTuiSDK() {\n const { tuiBoot } = await import(\"@ultracontext/tui/tui\");\n await tuiBoot({\n assetsRoot: path.resolve(__dirname, \"..\", \"..\"),\n offlineNotice: \"Daemon offline. Run: ultracontext start\",\n });\n}\n\n// ── main ────────────────────────────────────────────────────────\n\nasync function run() {\n // check for updates (silent on error, cached 24h)\n if (!SKIP_UPDATE_CHECK.has(command)) await checkForUpdate();\n\n // load saved key, then onboard if still missing\n let onboardResult = null;\n if (NEEDS_KEY.has(command)) {\n loadApiKeyFromConfig();\n if (!process.env.ULTRACONTEXT_API_KEY) onboardResult = await runOnboarding();\n }\n\n switch (command) {\n case \"start\":\n await launchDaemonSDK();\n if (onboardResult?.launchTui) await launchTuiSDK();\n break;\n\n case \"stop\":\n process.argv[2] = \"stop\";\n await runCtlSDK();\n break;\n\n case \"status\":\n process.argv[2] = \"status\";\n await runCtlSDK();\n break;\n\n case \"config\": {\n const configResult = await runOnboarding();\n if (configResult?.launchTui) {\n if (!isDaemonRunning()) await launchDaemonSDK();\n await launchTuiSDK();\n }\n break;\n }\n\n case \"tui\":\n await launchTuiSDK();\n break;\n\n case \"version\":\n case \"v\":\n console.log(readVersion());\n break;\n\n case \"update\":\n case \"upgrade\":\n runUpdate(process.argv.slice(3));\n break;\n\n // default: ensure daemon running, then open TUI\n case \"\": {\n if (!isDaemonRunning()) await launchDaemonSDK();\n await launchTuiSDK();\n break;\n }\n\n case \"help\":\n case \"h\":\n printHelp();\n break;\n\n default:\n console.error(`Unknown command: ${process.argv[2]}`);\n printHelp();\n process.exit(1);\n }\n}\n\nrun().catch((error) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n});\n"],"mappings":"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"}
@@ -1,4 +1,3 @@
1
- import { c as heroArtForWidth, i as UC_BRAND_BLUE, r as UC_BLUE_LIGHT, t as Spinner } from "../Spinner-C7LzYron.mjs";
2
1
  import process from "node:process";
3
2
  import path from "node:path";
4
3
  import fs from "node:fs";
@@ -6,6 +5,9 @@ import { spawn } from "node:child_process";
6
5
  import React from "react";
7
6
  import { Box, Text, render, useInput, useStdout } from "ink";
8
7
  import { TitledBox } from "@mishieck/ink-titled-box";
8
+ import { heroArtForWidth } from "@ultracontext/tui/ui/hero-art";
9
+ import { UC_BLUE_LIGHT, UC_BRAND_BLUE } from "@ultracontext/tui/ui/constants";
10
+ import Spinner from "@ultracontext/tui/Spinner";
9
11
 
10
12
  //#region src/cli/onboarding.mjs
11
13
  const CONFIG_DIR = path.join(process.env.HOME || process.env.USERPROFILE || "~", ".ultracontext");