@datalabrotterdam/nova-ai-cli 1.1.0 → 1.2.0-alpha.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.
Files changed (51) hide show
  1. package/README.md +165 -96
  2. package/dist/acp-ZYOQ6PON.js +65 -0
  3. package/dist/chunk-GZZ7PY3S.js +8878 -0
  4. package/dist/chunk-JAEXUFAS.js +451 -0
  5. package/dist/chunk-Q7WL3DXP.js +19 -0
  6. package/dist/chunk-ZKRZIF3S.js +954 -0
  7. package/dist/headless-ZYUSTZDL.js +567 -0
  8. package/dist/index.d.ts +0 -1
  9. package/dist/index.js +155 -17
  10. package/dist/login-6U7QS7EA.js +118 -0
  11. package/dist/tui-XOKXPXN2.js +4801 -0
  12. package/dist/webui-L4GGMXJ3.js +9 -0
  13. package/package.json +24 -8
  14. package/src/acp/web/dist/assets/{index-D2Z0sfLR.js → index-DlCz3fmq.js} +1 -1
  15. package/src/acp/web/dist/index.html +1 -1
  16. package/dist/acp/agent.d.ts +0 -13
  17. package/dist/acp/agent.js +0 -242
  18. package/dist/acp/auth-server.d.ts +0 -10
  19. package/dist/acp/auth-server.js +0 -67
  20. package/dist/acp/credentials.d.ts +0 -11
  21. package/dist/acp/credentials.js +0 -30
  22. package/dist/acp/index.d.ts +0 -2
  23. package/dist/acp/index.js +0 -21
  24. package/dist/acp/mcp.d.ts +0 -11
  25. package/dist/acp/mcp.js +0 -90
  26. package/dist/acp/sessions.d.ts +0 -20
  27. package/dist/acp/sessions.js +0 -91
  28. package/dist/acp/tools/index.d.ts +0 -6
  29. package/dist/acp/tools/index.js +0 -5
  30. package/dist/acp/tools/marker.d.ts +0 -17
  31. package/dist/acp/tools/marker.js +0 -43
  32. package/dist/acp/tools/permission.d.ts +0 -2
  33. package/dist/acp/tools/permission.js +0 -20
  34. package/dist/acp/tools/read-file.d.ts +0 -2
  35. package/dist/acp/tools/read-file.js +0 -20
  36. package/dist/acp/tools/registry.d.ts +0 -4
  37. package/dist/acp/tools/registry.js +0 -10
  38. package/dist/acp/tools/reporting.d.ts +0 -3
  39. package/dist/acp/tools/reporting.js +0 -26
  40. package/dist/acp/tools/run-command.d.ts +0 -2
  41. package/dist/acp/tools/run-command.js +0 -29
  42. package/dist/acp/tools/system-prompt.d.ts +0 -2
  43. package/dist/acp/tools/system-prompt.js +0 -23
  44. package/dist/acp/tools/types.d.ts +0 -18
  45. package/dist/acp/tools/types.js +0 -1
  46. package/dist/acp/tools/write-file.d.ts +0 -2
  47. package/dist/acp/tools/write-file.js +0 -20
  48. package/dist/tui/index.d.ts +0 -7
  49. package/dist/tui/index.js +0 -11
  50. package/dist/webui/index.d.ts +0 -6
  51. package/dist/webui/index.js +0 -9
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # @datalabrotterdam/nova-ai-cli
6
6
 
7
- **CLI for DataLab Rotterdam's Nova AI — also speaks Agent Client Protocol**
7
+ **Agentic coding with DataLab Rotterdam's Nova AI: in your terminal, in scripts, and in your editor over the Agent Client Protocol**
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/@datalabrotterdam/nova-ai-cli?style=flat-square&color=f96743)](https://www.npmjs.com/package/@datalabrotterdam/nova-ai-cli)
10
10
  [![npm downloads](https://img.shields.io/npm/dm/@datalabrotterdam/nova-ai-cli?style=flat-square)](https://www.npmjs.com/package/@datalabrotterdam/nova-ai-cli)
@@ -12,136 +12,205 @@
12
12
  [![ACP](https://img.shields.io/badge/ACP-agent-blue?style=flat-square)](https://agentclientprotocol.com)
13
13
  [![issues](https://img.shields.io/github/issues/Datalab-Rotterdam/nova-ai-cli?style=flat-square)](https://github.com/Datalab-Rotterdam/nova-ai-cli/issues)
14
14
 
15
- Talk to DataLab Rotterdam's Nova AI from your terminal, or point your editor/IDE at it via [Agent Client Protocol](https://agentclientprotocol.com) — both modes share the same auth and run on [`@datalabrotterdam/nova-sdk`](https://www.npmjs.com/package/@datalabrotterdam/nova-sdk).
16
-
17
- [npm](https://www.npmjs.com/package/@datalabrotterdam/nova-ai-cli) · [Issues](https://github.com/Datalab-Rotterdam/nova-ai-cli/issues) · [ACP docs](https://agentclientprotocol.com)
15
+ [npm](https://www.npmjs.com/package/@datalabrotterdam/nova-ai-cli) · [Issues](https://github.com/Datalab-Rotterdam/nova-ai-cli/issues) · [ACP reference](./docs/ACP.md) · [Settings and memory](./docs/NOVA_HOME.md)
18
16
 
19
17
  </div>
20
18
 
21
- > **⚠️ Work in progress.** ACP mode (`--acp`) is the only fully implemented mode today — agentic tool use, permissions, and session persistence all work there. Interactive terminal chat (no flags) and the standalone browser UI (`--web`) are stubs that print a "not built yet" message. Expect breaking changes before a 1.0.
22
-
23
- ---
19
+ `nova-ai` is a coding agent in the spirit of `claude` and `codex`, backed by
20
+ [Nova AI](https://platform.nova.datalabrotterdam.nl). It reads and edits your
21
+ code, runs commands with your permission, asks when a decision is yours, and
22
+ keeps a task list for longer work. One agent, three front ends:
24
23
 
25
- ## Installation
24
+ - **Terminal UI**: `nova-ai`
25
+ - **Headless**: `nova-ai -p "…"` for scripts, CI and machines without a GUI
26
+ - **ACP agent**: `nova-ai --acp` for Zed, JetBrains, VS Code and any other
27
+ [Agent Client Protocol](https://agentclientprotocol.com) client
26
28
 
27
- No install needed — run directly with `npx`:
29
+ The terminal UI and headless mode talk to the agent over the same protocol an
30
+ editor uses, so all three behave the same: same tools, permissions, sessions,
31
+ memory, MCP servers and settings.
28
32
 
29
- ```sh
30
- npx @datalabrotterdam/nova-ai-cli --setup # store your Nova API key
31
- npx @datalabrotterdam/nova-ai-cli # interactive chat (WIP, stub)
32
- npx @datalabrotterdam/nova-ai-cli --acp # speak ACP over stdio (editors/IDEs) — fully working
33
- npx @datalabrotterdam/nova-ai-cli --web # standalone browser UI (WIP, stub)
34
- ```
33
+ ## Install
35
34
 
36
- Or install globally / as a project dependency:
35
+ Requires Node.js 22.19 or newer and a Nova AI API key
36
+ ([get one](https://platform.nova.datalabrotterdam.nl/dashboard/api-keys)).
37
37
 
38
38
  ```sh
39
39
  npm install -g @datalabrotterdam/nova-ai-cli
40
+ nova-ai login # opens a local page to enter the key; --no-browser asks in the terminal
41
+ nova-ai # start the terminal UI in your project
40
42
  ```
41
43
 
42
- **Requires:** Node.js >= 18, a [Nova AI](https://api.nova.datalabrotterdam.nl) API key.
44
+ Or without installing: `npx @datalabrotterdam/nova-ai-cli login`, then
45
+ `npx @datalabrotterdam/nova-ai-cli`.
43
46
 
44
- ---
45
-
46
- ## Overview
47
+ ```
48
+ nova-ai [options] Interactive terminal UI
49
+ nova-ai -p [options] [prompt] Run one request headless (see nova-ai -p --help)
50
+ nova-ai --acp Agent Client Protocol over stdio (for editors)
51
+ nova-ai login [--no-browser] Connect your Nova AI account
52
+ nova-ai logout Remove the stored API key
53
+
54
+ Interactive options:
55
+ --resume <session-id> Continue a saved session
56
+ -c, --continue Continue the most recent session in this folder
57
+ --model <id> Use this model
58
+ ```
47
59
 
48
- `nova-ai-cli` is a terminal client for Nova AI, in the same spirit as `claude`/`codex`. Pass `--acp` to run it in Agent Client Protocol mode, where it reads JSON-RPC requests from stdin and writes responses/notifications to stdout so any ACP-aware editor/IDE can drive it like any other coding agent — with tool calling, permission prompts, and persisted session history. Bare/`--web` modes are placeholders for now. All modes share the same stored credentials.
60
+ ## Terminal UI
61
+
62
+ The UI renders inline below your shell prompt. Finished messages go to the
63
+ terminal's own scrollback (scroll, search and copy as usual); only the
64
+ streaming answer, running tools, the prompt and the status rows are redrawn.
65
+
66
+ - **Modes** (Shift+Tab): `agent` does the work; `plan` reads the code, asks
67
+ questions and builds a task list but changes nothing; `ask` only answers.
68
+ - **Permissions**: before an edit or command you choose once, for this
69
+ session, always (saved as an exact rule) or deny. Ctrl+K switches between
70
+ asking, accepting edits, and bypass (never remembered).
71
+ - **Questions and tasks**: the agent can ask 1–4 multiple-choice questions
72
+ (with descriptions, a recommended option and your own answer) and shows a
73
+ live checklist for multi-step work.
74
+ - **Prompt**: history, `@file` mentions, `/command` completion, large pastes
75
+ as compact markers, Alt+V to paste an image (for models that accept
76
+ images). Messages typed while Nova works are queued; ↑ edits them.
77
+ `/steer <text>` reaches the running turn at the next tool boundary.
78
+
79
+ | Key | Action |
80
+ |---|---|
81
+ | Enter / Shift+Enter | Send / new line (Shift+Enter where the terminal supports the kitty keyboard protocol: VS Code, kitty, WezTerm, Ghostty, foot, Alacritty, recent iTerm2; Alt+Enter, Ctrl+J or a trailing `\` work everywhere) |
82
+ | Esc | Cancel the running turn |
83
+ | Ctrl+C twice | Exit; prints `nova-ai --resume <id>` |
84
+ | Shift+Tab | Cycle agent / ask / plan |
85
+ | Ctrl+R / Ctrl+P / Ctrl+K | Sessions / models / permission mode |
86
+ | Ctrl+O / Ctrl+T / Ctrl+B | Verbose tool output / tool inspector / background jobs |
87
+ | Alt+V | Paste a clipboard image |
88
+
89
+ Slash commands: `/help`, `/model`, `/mode`, `/permission`, `/session`,
90
+ `/resume`, `/clear`, `/compact`, `/rewind [n]`, `/queue`, `/steer`, `/usage`,
91
+ `/tools`, `/mcp`, `/skills`, `/shell` and `/agent` (background jobs), `/trust`
92
+ and `/exit`. Details:
93
+ [`src/tui/README.md`](./src/tui/README.md).
94
+
95
+ ## Headless
49
96
 
50
97
  ```sh
51
- # 1. one-time setup — opens a browser page to verify the key against Nova AI and store it locally
52
- npx @datalabrotterdam/nova-ai-cli --setup
53
-
54
- # 2. point your ACP client at this binary
55
- npx @datalabrotterdam/nova-ai-cli --acp
98
+ nova-ai -p "review the changed files"
99
+ git diff | nova-ai -p --output-format json "summarize this diff"
100
+ nova-ai -p --json --permission-mode accept-edits "fix the failing test"
101
+ nova-ai -p -c "and now add a changelog entry"
56
102
  ```
57
103
 
58
- Most editors invoke `--acp` for you once configured as an agent — `--setup` is the one command you run by hand first.
59
-
60
- **Status:** `--setup` and `--acp` (including tool calling and session persistence) are fully implemented. Interactive terminal chat (no flags) and `--web` are stubs — see the WIP note above.
104
+ | Option | |
105
+ |---|---|
106
+ | `--output-format text\|json\|stream-json` | `text` prints the answer; `json` one object at the end (text, tools, permissions); `stream-json` (or `--json`) JSON Lines while it runs: `session.started`, `message.delta`, `tool.started`/`tool.updated`/`tool.finished`, `permission`, `queue.changed`, `background.update`, `result`, `error` |
107
+ | `--permission-mode` | `read-only` (default): anything that needs approval is refused, since nobody can approve it. `accept-edits`: file edits are accepted, commands still refused. `bypass-all`: everything is accepted. Deny rules always win. |
108
+ | `--resume <id>`, `-c` | Continue a session |
109
+ | `--model`, `--cwd` | Model and workspace |
110
+ | `--trust-workspace` | Trust the workspace (remembered): start the MCP servers and apply the allow rules it declares |
111
+ | `--no-mcp` | Ignore workspace MCP configuration |
61
112
 
62
- ---
113
+ Exit codes: `0` success, `1` configuration or runtime error, `2` cancelled,
114
+ `3` tool-turn safety limit, `130` interrupted.
63
115
 
64
- ## Auth
116
+ ## Editors (ACP)
65
117
 
66
- This agent advertises **Agent Auth** (per [`AUTHENTICATION.md`](https://github.com/agentclientprotocol/registry/blob/main/AUTHENTICATION.md)): when the client calls `authenticate`, the agent itself spins up a local HTTP server, opens your browser to it, and resolves once you've connected your account.
118
+ Configure `nova-ai --acp` as a custom agent. For Zed:
67
119
 
68
120
  ```json
69
121
  {
70
- "id": "nova-api-key",
71
- "name": "Nova API Key"
122
+ "agent_servers": {
123
+ "Nova AI": { "command": "npx", "args": ["-y", "@datalabrotterdam/nova-ai-cli", "--acp"] }
124
+ }
72
125
  }
73
126
  ```
74
127
 
75
- Today the page served at that local URL is a small Svelte form that:
76
- 1. Collects your Nova API key.
77
- 2. Validates it against `client.models.list()`.
78
- 3. Picks a default model (first enabled model) and stores `{ apiKey, defaultModel }` in `~/.nova-ai/credentials.json`.
79
-
80
- You can also trigger this manually:
81
-
82
- ```sh
83
- npx @datalabrotterdam/nova-ai-cli --setup
84
- ```
85
-
86
- Overrides, no browser flow required:
87
-
88
- | Env var | Purpose |
89
- |---|---|
90
- | `NOVA_API_KEY` | Skip stored credentials, use this key |
91
- | `NOVA_MODEL` | Skip stored default model, use this model id |
92
-
93
- A future version will swap this for real OAuth once DataLab Rotterdam ships an OAuth provider for Nova AI — same local server (`src/acp/auth-server.ts`), same lifecycle, just a different page/flow. Credential handling stays isolated in `src/acp/credentials.ts` either way.
94
-
95
- ---
96
-
97
- ## What it implements
98
-
99
- | ACP method | Behavior |
128
+ The agent supports session load/list/resume/fork/close/delete, modes, a
129
+ permission-mode and model selector, permission requests with once / session /
130
+ always options, client file system and terminals, form questions, plans, usage
131
+ and title updates, MCP servers (stdio, HTTP, SSE), image prompts, and three
132
+ login methods (browser, `NOVA_API_KEY`, or `nova-ai login` in a terminal).
133
+ Nova-specific features (prompt queue, rewind, compaction, context usage,
134
+ background jobs) are `_nova/…` extension methods. Everything is documented in
135
+ [docs/ACP.md](./docs/ACP.md).
136
+
137
+ ## Security model
138
+
139
+ - **The agent enforces permissions**, not the client. Every front end and
140
+ every editor gets the same policy; a client can only answer the questions
141
+ it is asked. Anything other than an explicit "allow" counts as "deny".
142
+ - **Deny rules win**, also in bypass mode and for read-only tools.
143
+ - **A repository can't approve itself.** Allow rules in a workspace's
144
+ `.nova-ai/settings*.json`, and MCP servers it declares (`.mcp.json`), only
145
+ take effect after you trust that workspace (asked once at startup, `/trust`,
146
+ or `--trust-workspace`). The trust decision is stored in `~/.nova-ai`, never
147
+ in the repository. A repository can't set the permission mode either.
148
+ - **"Always allow" saves an exact rule** for that command or path in your
149
+ private `~/.nova-ai/projects/<key>/settings.json`, outside the repository.
150
+ Compound commands (`a && b`, pipes) are checked part by part; substitutions
151
+ and nested shells need an exact rule.
152
+ - **File access stays in the workspace** (symlinks resolved); commands run in
153
+ their own process group with a timeout, and the whole tree is stopped on
154
+ cancel.
155
+ - **Credentials** are stored in `~/.nova-ai/credentials.json` with mode 0600.
156
+ The browser login page only accepts a one-time token from the same origin.
157
+
158
+ ## Settings, memory and sessions
159
+
160
+ Everything lives in `~/.nova-ai` (or `$NOVA_AI_HOME`), shared with the Nova
161
+ VS Code extension: settings, per-project trust, sessions and memory.
162
+
163
+ - **Rules**: `{"permissions": {"allow": ["run_command(npm test)", "edit_file(src/*)"], "deny": ["read_file(.env)"]}}`
164
+ in `~/.nova-ai/settings.json`, the private project file, or the workspace's
165
+ `.nova-ai/settings.json` (team, committed) and `.nova-ai/settings.local.json`
166
+ (personal). Claude-style aliases such as `Bash(npm run *)` and `Edit(src/**)`
167
+ work too.
168
+ - **Instructions**: `NOVA.md` and `AGENTS.md` in the workspace.
169
+ - **Memory**: a `MEMORY.md` index plus notes, globally and per project,
170
+ through the `memory_read` and `memory_write` tools (writes need approval).
171
+ - **Skills**: `SKILL.md` files under `.agents/skills`, `.claude/skills` or
172
+ `.codex/skills`, in the workspace or your home folder.
173
+ - **MCP servers**: `.mcp.json` or `mcpServers` in `.nova-ai/settings.json`.
174
+
175
+ The complete layout and rule syntax: [docs/NOVA_HOME.md](./docs/NOVA_HOME.md).
176
+
177
+ ## Models
178
+
179
+ The model list shows the chat models of your account. On login Nova picks the
180
+ first one with native tool calling; `/model` or Ctrl+P changes it (and
181
+ remembers the choice), `--model` or `NOVA_MODEL` sets it for one run. Nova uses the gateway's function calling and falls back
182
+ to a text protocol for models that don't support it. When the context window
183
+ fills up, older messages are summarized automatically (`/compact` does it by
184
+ hand).
185
+
186
+ | Environment | |
100
187
  |---|---|
101
- | `initialize` | Returns protocol version + the `nova-api-key` agent auth method |
102
- | `session/new` | Allocates a session id with in-memory history |
103
- | `session/load` | Restores a previously-persisted session and replays its turns as `*_message_chunk` notifications |
104
- | `session/list` | Lists stored sessions, optionally filtered by `cwd` |
105
- | `authenticate` | Runs the local browser auth flow (`src/acp/auth-server.ts`) and resolves once a valid key is stored |
106
- | `session/prompt` | Streams a `chat.completions.stream()` call, forwarding `agent_message_chunk` updates as text arrives; drives the tool-calling loop below |
107
- | `session/cancel` | Aborts the in-flight Nova AI request via `AbortController` |
108
-
109
- Only `text` and `resource_link` content blocks are read from prompts today — images/audio/embeddings supported by `nova-sdk` aren't wired up yet.
110
-
111
- Sessions are persisted to disk (`src/acp/sessions.ts`) so `session/load` can rehydrate history, and a title is auto-derived from the conversation.
112
-
113
- ### Tool calling
114
-
115
- While streaming a response, the agent watches for a fenced tool-call marker in the model's output (`src/acp/tools/marker.ts`). When one appears, it stops streaming text and instead:
116
-
117
- 1. Reports the pending call via `session/update` (`tool_call` with status `pending`).
118
- 2. Requests permission from the client for any **mutating** tool (`allow_once` / `reject_once`), via `session/request_permission`.
119
- 3. Executes the tool and reports the result/error via a `tool_call_update`.
120
- 4. Feeds the tool result back into the conversation and loops (up to 10 rounds per prompt) until the model produces a plain answer.
121
-
122
- Built-in tools (`src/acp/tools/registry.ts`):
123
-
124
- | Tool | Mutating | Behavior |
125
- |---|---|---|
126
- | `read_file` | No | Reads a file's contents, gated by client `fs.readTextFile` capability |
127
- | `write_file` | Yes | Writes/overwrites a file, gated by client `fs.writeTextFile` capability — requires permission |
128
- | `run_command` | Yes | Runs a shell command in the session's `cwd` — requires permission |
129
-
130
- Tools are filtered per-session by the client's advertised `clientCapabilities`, so a client without filesystem support never sees `read_file`/`write_file` offered.
131
-
132
- ---
188
+ | `NOVA_API_KEY`, `NOVA_MODEL` | Use this key / model instead of the stored ones |
189
+ | `NOVA_BASE_URL` | Another Nova (OpenAI-compatible) gateway |
190
+ | `NOVA_AI_HOME` | Settings folder (default `~/.nova-ai`) |
191
+ | `NOVA_TOOL_PROTOCOL` | Force `native` or `text` tool calling |
192
+ | `NOVA_NES_MODEL` | Separate model for next-edit suggestions (ACP) |
193
+ | `NOVA_KITTY_KEYBOARD` | `1`/`0`: force the kitty keyboard protocol on or off |
133
194
 
134
195
  ## Development
135
196
 
136
197
  ```sh
137
- npm install
138
- npm test # tsc --noEmit + node --test on tests/
139
- npm run build # builds the Svelte setup page, then tsc -> dist/
198
+ npm ci
199
+ npm test # type check, unit tests (node:test) and the real-terminal (PTY) scenario
200
+ npm run build # the login page and the CLI bundle in dist/
201
+ npm run dev # run from source with tsx
140
202
  ```
141
203
 
142
- Releases are automated via `semantic-release` on push to `main` (see `.github/workflows/ci.yml`); commit messages drive version bumps.
204
+ Tests never touch your `~/.nova-ai` or the real Nova API: they run with a
205
+ throwaway home folder and point the client at a local port. The ACP
206
+ conformance test starts the real `--acp` process against a fake gateway. CI
207
+ runs the suite on Linux, macOS and Windows with Node 22 and 24 (the PTY
208
+ scenario on Linux and macOS). Releases are cut by `semantic-release` from
209
+ `main` and `alpha`.
143
210
 
144
- ---
211
+ Planned: OIDC login against the Nova platform instead of a pasted API key,
212
+ and Remote Control (driving a running session from the Nova web UI or mobile
213
+ app). The standalone browser UI (`--web`) is experimental.
145
214
 
146
215
  ## License
147
216
 
@@ -0,0 +1,65 @@
1
+ import {
2
+ NovaAgent,
3
+ createAgentApp
4
+ } from "./chunk-GZZ7PY3S.js";
5
+ import "./chunk-Q7WL3DXP.js";
6
+ import "./chunk-JAEXUFAS.js";
7
+
8
+ // src/acp/index.ts
9
+ import { Readable, Writable } from "stream";
10
+ import * as acp from "@agentclientprotocol/sdk";
11
+
12
+ // src/acp/stdio-guard.ts
13
+ function redirectConsoleToStderr() {
14
+ const methods = ["log", "info", "debug", "dir", "table", "trace"];
15
+ const original = Object.fromEntries(
16
+ methods.map((method) => [method, console[method]])
17
+ );
18
+ const toStderr = (...args) => console.error(...args);
19
+ for (const method of methods) {
20
+ console[method] = toStderr;
21
+ }
22
+ return () => {
23
+ for (const method of methods) {
24
+ console[method] = original[method];
25
+ }
26
+ };
27
+ }
28
+
29
+ // src/acp/index.ts
30
+ async function runAcp() {
31
+ redirectConsoleToStderr();
32
+ const agentImpl = new NovaAgent();
33
+ const output = Writable.toWeb(process.stdout);
34
+ const input = Readable.toWeb(process.stdin);
35
+ const stream = acp.ndJsonStream(output, input);
36
+ const connection = createAgentApp(agentImpl).connect(stream);
37
+ installLifecycle(agentImpl, connection.closed);
38
+ await connection.closed;
39
+ }
40
+ function installLifecycle(agent, closed) {
41
+ let shuttingDown = false;
42
+ const shutdown = async (code) => {
43
+ if (shuttingDown) process.exit(code);
44
+ shuttingDown = true;
45
+ const forced = setTimeout(() => process.exit(code), SHUTDOWN_TIMEOUT_MS);
46
+ forced.unref();
47
+ try {
48
+ await agent.shutdown();
49
+ } catch (error) {
50
+ console.error("nova-ai-cli: error during shutdown:", error);
51
+ }
52
+ process.exit(code);
53
+ };
54
+ void closed.then(() => shutdown(0));
55
+ process.on("SIGTERM", () => void shutdown(0));
56
+ process.on("SIGINT", () => void shutdown(130));
57
+ process.on("unhandledRejection", (reason) => {
58
+ console.error("nova-ai-cli: unhandled rejection:", reason);
59
+ });
60
+ }
61
+ var SHUTDOWN_TIMEOUT_MS = 5e3;
62
+ var acp_default = runAcp;
63
+ export {
64
+ acp_default as default
65
+ };