@datalabrotterdam/nova-ai-cli 1.2.0-alpha.5 → 1.2.0-alpha.6
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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# [@datalabrotterdam/nova-ai-cli-v1.2.0-alpha.6](https://github.com/Datalab-Rotterdam/nova-ai-cli/compare/@datalabrotterdam/nova-ai-cli@1.2.0-alpha.5...@datalabrotterdam/nova-ai-cli@1.2.0-alpha.6) (2026-10-23)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **cli:** start the session before the first prompt ([0c26456](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/0c26456b8126e3b42a6de2e38212213ae5380e62))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **agent:** set workspace trust over ACP ([cec4a89](https://github.com/Datalab-Rotterdam/nova-ai-cli/commit/cec4a8949a4232f46448219e4d5363289651215d))
|
|
12
|
+
|
|
1
13
|
# [@datalabrotterdam/nova-ai-cli-v1.2.0-alpha.5](https://github.com/Datalab-Rotterdam/nova-ai-cli/compare/@datalabrotterdam/nova-ai-cli@1.2.0-alpha.4...@datalabrotterdam/nova-ai-cli@1.2.0-alpha.5) (2026-10-23)
|
|
2
14
|
|
|
3
15
|
|
|
@@ -1,147 +1,3 @@
|
|
|
1
|
-
// src/tui/settings/workspace-mcp.ts
|
|
2
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
3
|
-
import { join as join2 } from "path";
|
|
4
|
-
import { isWorkspaceTrusted } from "@datalabrotterdam/nova-ai-agent/core/nova-home.js";
|
|
5
|
-
import { readSettings } from "@datalabrotterdam/nova-ai-agent/core/policy/settings.js";
|
|
6
|
-
|
|
7
|
-
// src/tui/settings/workspace-settings.ts
|
|
8
|
-
import { readFileSync } from "fs";
|
|
9
|
-
import { join } from "path";
|
|
10
|
-
function settingsPath(cwd) {
|
|
11
|
-
return join(cwd, ".nova-ai", "settings.json");
|
|
12
|
-
}
|
|
13
|
-
function readWorkspaceSettings(cwd) {
|
|
14
|
-
try {
|
|
15
|
-
const raw = readFileSync(settingsPath(cwd), "utf8");
|
|
16
|
-
return JSON.parse(raw);
|
|
17
|
-
} catch {
|
|
18
|
-
return {};
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// src/tui/settings/workspace-mcp.ts
|
|
23
|
-
var UNTRUSTED_MCP_MESSAGE = "Not started: MCP servers declared by this workspace only run once you trust it (/trust in the TUI, --trust-workspace headless).";
|
|
24
|
-
function readWorkspaceMcpConfiguration(cwd, options = {}) {
|
|
25
|
-
const configured = readWorkspaceSettings(cwd).mcpServers ?? [];
|
|
26
|
-
const project = readProjectMcpConfiguration(cwd);
|
|
27
|
-
const servers = new Map(configured.map((server) => [server.name, server]));
|
|
28
|
-
for (const server of project.servers) servers.set(server.name, server);
|
|
29
|
-
const declared = [...servers.values()];
|
|
30
|
-
if (declared.length && !(options.trusted ?? isWorkspaceTrusted(cwd))) {
|
|
31
|
-
return {
|
|
32
|
-
servers: [],
|
|
33
|
-
failures: [
|
|
34
|
-
...project.failures,
|
|
35
|
-
...declared.map((server) => ({ serverName: server.name, message: UNTRUSTED_MCP_MESSAGE }))
|
|
36
|
-
],
|
|
37
|
-
untrusted: declared.map((server) => server.name)
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
return { servers: declared, failures: project.failures };
|
|
41
|
-
}
|
|
42
|
-
function workspaceNeedsTrust(cwd) {
|
|
43
|
-
if (isWorkspaceTrusted(cwd)) return false;
|
|
44
|
-
if (readWorkspaceMcpConfiguration(cwd, { trusted: true }).servers.length) return true;
|
|
45
|
-
return loadWorkspaceAllowRules(cwd).length > 0;
|
|
46
|
-
}
|
|
47
|
-
function loadWorkspaceAllowRules(cwd) {
|
|
48
|
-
return ["settings.json", "settings.local.json"].flatMap((name) => {
|
|
49
|
-
const allow = readSettings(join2(cwd, ".nova-ai", name))?.permissions?.allow;
|
|
50
|
-
return Array.isArray(allow) ? allow : [];
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
function readProjectMcpConfiguration(cwd) {
|
|
54
|
-
const path = join2(cwd, ".mcp.json");
|
|
55
|
-
let raw;
|
|
56
|
-
try {
|
|
57
|
-
raw = readFileSync2(path, "utf8");
|
|
58
|
-
} catch (error) {
|
|
59
|
-
if (error.code === "ENOENT") return { servers: [], failures: [] };
|
|
60
|
-
return { servers: [], failures: [{ serverName: ".mcp.json", message: errorMessage(error, "Could not read file") }] };
|
|
61
|
-
}
|
|
62
|
-
let document;
|
|
63
|
-
try {
|
|
64
|
-
document = JSON.parse(raw);
|
|
65
|
-
} catch (error) {
|
|
66
|
-
return { servers: [], failures: [{ serverName: ".mcp.json", message: errorMessage(error, "Invalid JSON") }] };
|
|
67
|
-
}
|
|
68
|
-
const root = asRecord(document);
|
|
69
|
-
const definitions = root ? asRecord(root.mcpServers) : null;
|
|
70
|
-
if (!definitions) {
|
|
71
|
-
return {
|
|
72
|
-
servers: [],
|
|
73
|
-
failures: [{ serverName: ".mcp.json", message: 'Expected an "mcpServers" object.' }]
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
const servers = [];
|
|
77
|
-
const failures = [];
|
|
78
|
-
for (const [name, value] of Object.entries(definitions)) {
|
|
79
|
-
const config = asRecord(value);
|
|
80
|
-
if (config?.disabled === true || config?.enabled === false) continue;
|
|
81
|
-
try {
|
|
82
|
-
servers.push(normalizeProjectServer(name, config));
|
|
83
|
-
} catch (error) {
|
|
84
|
-
failures.push({ serverName: `.mcp.json:${name}`, message: errorMessage(error, "Invalid server configuration") });
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return { servers, failures };
|
|
88
|
-
}
|
|
89
|
-
function normalizeProjectServer(name, config) {
|
|
90
|
-
if (!config) throw new Error("Server definition must be an object.");
|
|
91
|
-
const type = typeof config.type === "string" ? config.type : config.url ? "http" : "stdio";
|
|
92
|
-
if (type === "http" || type === "sse") {
|
|
93
|
-
if (typeof config.url !== "string" || !config.url) throw new Error(`${type} server requires a URL.`);
|
|
94
|
-
try {
|
|
95
|
-
new URL(config.url);
|
|
96
|
-
} catch {
|
|
97
|
-
throw new Error(`${type} server requires a valid URL.`);
|
|
98
|
-
}
|
|
99
|
-
return {
|
|
100
|
-
name,
|
|
101
|
-
type,
|
|
102
|
-
url: config.url,
|
|
103
|
-
headers: normalizePairs(config.headers, "headers")
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
if (type !== "stdio") throw new Error(`Unsupported transport type: ${type}.`);
|
|
107
|
-
if (typeof config.command !== "string" || !config.command) throw new Error("stdio server requires a command.");
|
|
108
|
-
if (config.args !== void 0 && (!Array.isArray(config.args) || !config.args.every((arg) => typeof arg === "string"))) {
|
|
109
|
-
throw new Error("args must be an array of strings.");
|
|
110
|
-
}
|
|
111
|
-
return {
|
|
112
|
-
name,
|
|
113
|
-
command: config.command,
|
|
114
|
-
args: config.args ?? [],
|
|
115
|
-
env: normalizePairs(config.env, "env")
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
function normalizePairs(value, label) {
|
|
119
|
-
if (value === void 0) return [];
|
|
120
|
-
if (Array.isArray(value)) {
|
|
121
|
-
if (!value.every((entry) => {
|
|
122
|
-
const pair = asRecord(entry);
|
|
123
|
-
return typeof pair?.name === "string" && typeof pair.value === "string";
|
|
124
|
-
})) {
|
|
125
|
-
throw new Error(`${label} must contain name/value strings.`);
|
|
126
|
-
}
|
|
127
|
-
return value.map((entry) => {
|
|
128
|
-
const pair = entry;
|
|
129
|
-
return { name: pair.name, value: pair.value };
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
const record = asRecord(value);
|
|
133
|
-
if (!record || !Object.values(record).every((entry) => typeof entry === "string")) {
|
|
134
|
-
throw new Error(`${label} must be an object of string values.`);
|
|
135
|
-
}
|
|
136
|
-
return Object.entries(record).map(([name, entry]) => ({ name, value: entry }));
|
|
137
|
-
}
|
|
138
|
-
function asRecord(value) {
|
|
139
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
140
|
-
}
|
|
141
|
-
function errorMessage(error, fallback) {
|
|
142
|
-
return error instanceof Error && error.message ? `${fallback}: ${error.message}` : fallback;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
1
|
// src/tui/session/tui-acp-client.ts
|
|
146
2
|
import { NOVA_NOTIFICATIONS } from "@datalabrotterdam/nova-ai-agent/acp/extensions.js";
|
|
147
3
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
@@ -746,8 +602,6 @@ function createStore(initial) {
|
|
|
746
602
|
}
|
|
747
603
|
|
|
748
604
|
export {
|
|
749
|
-
readWorkspaceMcpConfiguration,
|
|
750
|
-
workspaceNeedsTrust,
|
|
751
605
|
TuiAcpClient,
|
|
752
606
|
choosePermissionOption,
|
|
753
607
|
createStore
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TuiAcpClient,
|
|
3
3
|
choosePermissionOption,
|
|
4
|
-
createStore
|
|
5
|
-
|
|
6
|
-
} from "./chunk-NHGIUEIR.js";
|
|
4
|
+
createStore
|
|
5
|
+
} from "./chunk-QSNU4H55.js";
|
|
7
6
|
|
|
8
7
|
// src/headless/index.ts
|
|
9
8
|
import { resolve } from "path";
|
|
@@ -12,7 +11,6 @@ import * as acp from "@agentclientprotocol/sdk";
|
|
|
12
11
|
import { NovaAgent } from "@datalabrotterdam/nova-ai-agent/acp/agent.js";
|
|
13
12
|
import { clientFromContext } from "@datalabrotterdam/nova-ai-agent/client/context-client.js";
|
|
14
13
|
import { startInProcessAgent } from "@datalabrotterdam/nova-ai-agent/client/in-process.js";
|
|
15
|
-
import { setWorkspaceTrusted } from "@datalabrotterdam/nova-ai-agent/core/nova-home.js";
|
|
16
14
|
import { listStoredSessions, loadStoredSession } from "@datalabrotterdam/nova-ai-agent/core/sessions.js";
|
|
17
15
|
|
|
18
16
|
// src/headless/client.ts
|
|
@@ -191,15 +189,19 @@ ${HEADLESS_HELP}
|
|
|
191
189
|
dependencies.agent ?? new NovaAgent()
|
|
192
190
|
);
|
|
193
191
|
const agent = connection.client;
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
serverName
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
192
|
+
const sessionMeta = { "nova-ai-cli/workspaceMcp": options.mcp };
|
|
193
|
+
const reportMcpFailures = (meta) => {
|
|
194
|
+
const failures = toRecord(toRecord(meta)?.["nova-ai-cli/mcp"])?.failures;
|
|
195
|
+
for (const failure of Array.isArray(failures) ? failures : []) {
|
|
196
|
+
const item = toRecord(failure);
|
|
197
|
+
if (typeof item?.serverName !== "string" || typeof item.message !== "string") continue;
|
|
198
|
+
output.write({
|
|
199
|
+
type: "mcp.configuration_error",
|
|
200
|
+
serverName: item.serverName,
|
|
201
|
+
message: item.message
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
};
|
|
203
205
|
let sessionId = null;
|
|
204
206
|
let interrupted = false;
|
|
205
207
|
const onInterrupt = () => {
|
|
@@ -215,18 +217,23 @@ ${HEADLESS_HELP}
|
|
|
215
217
|
clientCapabilities: client.capabilities,
|
|
216
218
|
clientInfo: { name: "nova-ai-cli-headless", version: "1" }
|
|
217
219
|
});
|
|
220
|
+
if (options.trustWorkspace) await agent.workspaceTrust({ cwd: options.cwd, trusted: true });
|
|
218
221
|
if (options.resume) {
|
|
219
|
-
await agent.resumeSession({
|
|
222
|
+
const resumed = await agent.resumeSession({
|
|
220
223
|
sessionId: options.resume,
|
|
221
224
|
cwd: options.cwd,
|
|
222
|
-
mcpServers:
|
|
225
|
+
mcpServers: [],
|
|
226
|
+
_meta: sessionMeta
|
|
223
227
|
});
|
|
228
|
+
reportMcpFailures(resumed._meta);
|
|
224
229
|
sessionId = options.resume;
|
|
225
230
|
} else {
|
|
226
231
|
const created = await agent.newSession({
|
|
227
232
|
cwd: options.cwd,
|
|
228
|
-
mcpServers:
|
|
233
|
+
mcpServers: [],
|
|
234
|
+
_meta: sessionMeta
|
|
229
235
|
});
|
|
236
|
+
reportMcpFailures(created._meta);
|
|
230
237
|
sessionId = created.sessionId;
|
|
231
238
|
}
|
|
232
239
|
await agent.setSessionConfigOption({
|
package/dist/index.js
CHANGED
|
@@ -140,7 +140,7 @@ ${MAIN_HELP}
|
|
|
140
140
|
}
|
|
141
141
|
case "print": {
|
|
142
142
|
const update = startUpdateCheck();
|
|
143
|
-
const { default: runHeadless } = await import("./headless-
|
|
143
|
+
const { default: runHeadless } = await import("./headless-C36E4XSQ.js");
|
|
144
144
|
const code = await runHeadless(command.args);
|
|
145
145
|
if (update) await noticeUpdate(update, 500);
|
|
146
146
|
return code;
|
|
@@ -175,7 +175,7 @@ ${MAIN_HELP}
|
|
|
175
175
|
);
|
|
176
176
|
return 2;
|
|
177
177
|
}
|
|
178
|
-
const { default: runChat } = await import("./tui-
|
|
178
|
+
const { default: runChat } = await import("./tui-TRADKND7.js");
|
|
179
179
|
return runChat(options);
|
|
180
180
|
}
|
|
181
181
|
}
|
|
@@ -9,21 +9,14 @@ import {
|
|
|
9
9
|
} from "./chunk-FMHWW3E6.js";
|
|
10
10
|
import {
|
|
11
11
|
TuiAcpClient,
|
|
12
|
-
createStore
|
|
13
|
-
|
|
14
|
-
workspaceNeedsTrust
|
|
15
|
-
} from "./chunk-NHGIUEIR.js";
|
|
12
|
+
createStore
|
|
13
|
+
} from "./chunk-QSNU4H55.js";
|
|
16
14
|
|
|
17
15
|
// src/tui/index.ts
|
|
18
16
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
19
|
-
import {
|
|
20
|
-
readCredentials,
|
|
21
|
-
saveDefaultModel as saveDefaultModel2
|
|
22
|
-
} from "@datalabrotterdam/nova-ai-agent/core/credentials.js";
|
|
23
|
-
import { resolveDefaultModel as resolveDefaultModel2 } from "@datalabrotterdam/nova-ai-agent/core/model-capabilities.js";
|
|
24
|
-
import { createNovaClient as createNovaClient2 } from "@datalabrotterdam/nova-ai-agent/core/nova-client.js";
|
|
17
|
+
import { readCredentials } from "@datalabrotterdam/nova-ai-agent/core/credentials.js";
|
|
25
18
|
import { listStoredSessions as listStoredSessions2, loadStoredSession as loadStoredSession2 } from "@datalabrotterdam/nova-ai-agent/core/sessions.js";
|
|
26
|
-
import { setWorkspaceTrusted
|
|
19
|
+
import { setWorkspaceTrusted } from "@datalabrotterdam/nova-ai-agent/core/nova-home.js";
|
|
27
20
|
|
|
28
21
|
// src/update-install.ts
|
|
29
22
|
import { spawnSync } from "child_process";
|
|
@@ -126,10 +119,6 @@ import { savedPermissionMode } from "@datalabrotterdam/nova-ai-agent/core/policy
|
|
|
126
119
|
|
|
127
120
|
// src/tui/session/session-runner.ts
|
|
128
121
|
import * as acp from "@agentclientprotocol/sdk";
|
|
129
|
-
import {
|
|
130
|
-
NovaAIError
|
|
131
|
-
} from "@datalabrotterdam/nova-sdk";
|
|
132
|
-
import { createNovaClient } from "@datalabrotterdam/nova-ai-agent/core/nova-client.js";
|
|
133
122
|
import { NovaAgent } from "@datalabrotterdam/nova-ai-agent/acp/agent.js";
|
|
134
123
|
import { startInProcessAgent } from "@datalabrotterdam/nova-ai-agent/client/in-process.js";
|
|
135
124
|
import { replayHistory } from "@datalabrotterdam/nova-ai-agent/core/history.js";
|
|
@@ -138,15 +127,6 @@ import {
|
|
|
138
127
|
savePermissionMode
|
|
139
128
|
} from "@datalabrotterdam/nova-ai-agent/core/policy/settings.js";
|
|
140
129
|
import { loadStoredSession } from "@datalabrotterdam/nova-ai-agent/core/sessions.js";
|
|
141
|
-
import { discoverSkills } from "@datalabrotterdam/nova-ai-agent/core/skills.js";
|
|
142
|
-
import {
|
|
143
|
-
resolveModelContextWindow
|
|
144
|
-
} from "@datalabrotterdam/nova-ai-agent/core/context-compaction.js";
|
|
145
|
-
import {
|
|
146
|
-
chatModels,
|
|
147
|
-
resolveDefaultModel,
|
|
148
|
-
resolveModelSupportsImageInput
|
|
149
|
-
} from "@datalabrotterdam/nova-ai-agent/core/model-capabilities.js";
|
|
150
130
|
|
|
151
131
|
// src/tui/files/file-mentions.ts
|
|
152
132
|
import { statSync } from "fs";
|
|
@@ -291,15 +271,6 @@ var SessionRunner = class {
|
|
|
291
271
|
constructor(store, credentials, cwd) {
|
|
292
272
|
this.store = store;
|
|
293
273
|
this.credentials = credentials;
|
|
294
|
-
this.loadMcpConfiguration(cwd);
|
|
295
|
-
this.skills = discoverSkills(cwd).map(
|
|
296
|
-
({ name, description, source, path }) => ({
|
|
297
|
-
name,
|
|
298
|
-
description,
|
|
299
|
-
source,
|
|
300
|
-
path
|
|
301
|
-
})
|
|
302
|
-
);
|
|
303
274
|
this.cwd = cwd;
|
|
304
275
|
this.acpClient = new TuiAcpClient(
|
|
305
276
|
store,
|
|
@@ -314,13 +285,7 @@ var SessionRunner = class {
|
|
|
314
285
|
clientCapabilities: this.acpClient.capabilities,
|
|
315
286
|
clientInfo: { name: "nova-ai-cli-tui", version: "1" }
|
|
316
287
|
});
|
|
317
|
-
this.
|
|
318
|
-
const model = credentials.defaultModel ?? process.env.NOVA_MODEL;
|
|
319
|
-
if (!model)
|
|
320
|
-
throw new Error(
|
|
321
|
-
"No Nova model configured. Re-run authentication or set NOVA_MODEL."
|
|
322
|
-
);
|
|
323
|
-
this.model = model;
|
|
288
|
+
this.model = credentials.defaultModel ?? process.env.NOVA_MODEL ?? "";
|
|
324
289
|
this.sessionId = crypto.randomUUID();
|
|
325
290
|
this.sessionReady = Promise.resolve();
|
|
326
291
|
}
|
|
@@ -337,15 +302,12 @@ var SessionRunner = class {
|
|
|
337
302
|
connection;
|
|
338
303
|
nova;
|
|
339
304
|
initialized;
|
|
340
|
-
novaClient;
|
|
341
|
-
mcpServers = [];
|
|
342
|
-
mcpConfigurationFailures = [];
|
|
343
305
|
mcpStatus = {
|
|
344
306
|
configured: [],
|
|
345
307
|
connected: [],
|
|
346
308
|
failures: []
|
|
347
309
|
};
|
|
348
|
-
skills;
|
|
310
|
+
skills = [];
|
|
349
311
|
sessionReady;
|
|
350
312
|
agentSessionLoaded = false;
|
|
351
313
|
promptActive = false;
|
|
@@ -354,9 +316,6 @@ var SessionRunner = class {
|
|
|
354
316
|
queuedAttachments = /* @__PURE__ */ new Map();
|
|
355
317
|
editingQueuedMessageId = null;
|
|
356
318
|
imageSupportByModel = /* @__PURE__ */ new Map();
|
|
357
|
-
contextWindow = null;
|
|
358
|
-
contextWindowModel = null;
|
|
359
|
-
contextWindowRequest = 0;
|
|
360
319
|
contextUsageRequest = 0;
|
|
361
320
|
modelCheck = null;
|
|
362
321
|
resumeFrom(storedSessionId) {
|
|
@@ -369,15 +328,7 @@ var SessionRunner = class {
|
|
|
369
328
|
this.sessionId = stored.sessionId;
|
|
370
329
|
this.cwd = stored.cwd;
|
|
371
330
|
this.title = stored.title;
|
|
372
|
-
this.
|
|
373
|
-
this.skills = discoverSkills(stored.cwd).map(
|
|
374
|
-
({ name, description, source, path }) => ({
|
|
375
|
-
name,
|
|
376
|
-
description,
|
|
377
|
-
source,
|
|
378
|
-
path
|
|
379
|
-
})
|
|
380
|
-
);
|
|
331
|
+
this.skills = [];
|
|
381
332
|
const messages = restoreSessionMessages(stored.messages);
|
|
382
333
|
this.store.setState({
|
|
383
334
|
messages,
|
|
@@ -396,7 +347,7 @@ var SessionRunner = class {
|
|
|
396
347
|
return await this.nova.loadSession({
|
|
397
348
|
sessionId: stored.sessionId,
|
|
398
349
|
cwd: stored.cwd,
|
|
399
|
-
mcpServers:
|
|
350
|
+
mcpServers: []
|
|
400
351
|
});
|
|
401
352
|
} finally {
|
|
402
353
|
this.acpClient.muteSessionUpdates = false;
|
|
@@ -521,9 +472,6 @@ var SessionRunner = class {
|
|
|
521
472
|
void this.enqueueQueuedPrompt(value, [], [], "steer", true);
|
|
522
473
|
return true;
|
|
523
474
|
}
|
|
524
|
-
configuredMcpServers() {
|
|
525
|
-
return [...this.mcpServers];
|
|
526
|
-
}
|
|
527
475
|
mcpSessionStatus() {
|
|
528
476
|
return {
|
|
529
477
|
configured: this.mcpStatus.configured.map((server) => ({ ...server })),
|
|
@@ -534,27 +482,29 @@ var SessionRunner = class {
|
|
|
534
482
|
skillSessionStatus() {
|
|
535
483
|
return this.skills.map((skill) => ({ ...skill }));
|
|
536
484
|
}
|
|
485
|
+
async trustWorkspace() {
|
|
486
|
+
await this.initialized;
|
|
487
|
+
await this.nova.workspaceTrust({ cwd: this.cwd, trusted: true });
|
|
488
|
+
}
|
|
489
|
+
/** Every skill of the workspace with its on/off state, from the agent. */
|
|
490
|
+
async listSkills() {
|
|
491
|
+
await this.initialized;
|
|
492
|
+
return (await this.nova.skillsList({ cwd: this.cwd })).skills;
|
|
493
|
+
}
|
|
494
|
+
async setSkillEnabled(name, enabled, global) {
|
|
495
|
+
await this.initialized;
|
|
496
|
+
return (await this.nova.skillsSetEnabled({
|
|
497
|
+
cwd: this.cwd,
|
|
498
|
+
name,
|
|
499
|
+
enabled,
|
|
500
|
+
scope: global ? "global" : "project"
|
|
501
|
+
})).skills;
|
|
502
|
+
}
|
|
537
503
|
contextUsageSnapshot() {
|
|
538
504
|
return this.store.getState().contextUsage;
|
|
539
505
|
}
|
|
540
506
|
async refreshContextUsage() {
|
|
541
507
|
await this.ensureSession();
|
|
542
|
-
const sessionId = this.sessionId;
|
|
543
|
-
const model = this.model;
|
|
544
|
-
if (this.contextWindowModel !== model) {
|
|
545
|
-
const request = ++this.contextWindowRequest;
|
|
546
|
-
const contextWindow = await resolveModelContextWindow(
|
|
547
|
-
this.novaClient,
|
|
548
|
-
model
|
|
549
|
-
);
|
|
550
|
-
if (request === this.contextWindowRequest && model === this.model) {
|
|
551
|
-
this.contextWindow = contextWindow;
|
|
552
|
-
this.contextWindowModel = model;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
if (sessionId !== this.sessionId || !this.agentSessionLoaded) {
|
|
556
|
-
return this.refreshContextUsage();
|
|
557
|
-
}
|
|
558
508
|
const usage = await this.updateContextUsage();
|
|
559
509
|
if (!usage)
|
|
560
510
|
throw new Error(
|
|
@@ -570,13 +520,8 @@ var SessionRunner = class {
|
|
|
570
520
|
}
|
|
571
521
|
setModel(model) {
|
|
572
522
|
this.model = model;
|
|
573
|
-
this.contextWindow = null;
|
|
574
|
-
this.contextWindowModel = null;
|
|
575
|
-
this.contextWindowRequest++;
|
|
576
523
|
saveDefaultModel(model);
|
|
577
524
|
if (this.agentSessionLoaded) void this.updateContextUsage();
|
|
578
|
-
void this.refreshContextUsage().catch(() => {
|
|
579
|
-
});
|
|
580
525
|
}
|
|
581
526
|
/**
|
|
582
527
|
* Hands the mode to the agent's permission policy (which decides) and
|
|
@@ -634,27 +579,30 @@ var SessionRunner = class {
|
|
|
634
579
|
}
|
|
635
580
|
/** Chat models only: embedding, speech and transcription models can't run a session. */
|
|
636
581
|
async listModels() {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
}));
|
|
582
|
+
await this.initialized;
|
|
583
|
+
const { models } = await this.nova.modelsList();
|
|
584
|
+
for (const model of models) this.imageSupportByModel.set(model.id, model.imageInput);
|
|
585
|
+
return models.map((model) => ({ id: model.id, name: model.name }));
|
|
642
586
|
}
|
|
643
587
|
/**
|
|
644
|
-
*
|
|
645
|
-
*
|
|
588
|
+
* Takes the account's default model when none is set, and replaces a saved
|
|
589
|
+
* one that cannot chat (older logins saved the first model of the list,
|
|
590
|
+
* e.g. an embedding model); the agent decides, the choice is saved.
|
|
646
591
|
* Runs once; a failed lookup keeps the model and the prompt reports errors.
|
|
647
592
|
*/
|
|
648
593
|
ensureUsableModel() {
|
|
649
594
|
this.modelCheck ??= (async () => {
|
|
650
595
|
try {
|
|
651
|
-
|
|
652
|
-
const
|
|
653
|
-
|
|
596
|
+
await this.initialized;
|
|
597
|
+
const { models, defaultModel } = await this.nova.modelsList(
|
|
598
|
+
this.model ? { preferred: this.model } : {}
|
|
599
|
+
);
|
|
600
|
+
for (const model of models) this.imageSupportByModel.set(model.id, model.imageInput);
|
|
601
|
+
if (!defaultModel || defaultModel === this.model) return;
|
|
654
602
|
const previous = this.model;
|
|
655
|
-
this.setModel(
|
|
603
|
+
this.setModel(defaultModel);
|
|
656
604
|
this.store.setState({
|
|
657
|
-
statusLine: `${previous} is not a chat model; switched to ${
|
|
605
|
+
statusLine: previous ? `${previous} is not a chat model; switched to ${defaultModel} (Ctrl+P to pick another).` : `Using model ${defaultModel} (Ctrl+P switches).`
|
|
658
606
|
});
|
|
659
607
|
} catch {
|
|
660
608
|
}
|
|
@@ -662,14 +610,8 @@ var SessionRunner = class {
|
|
|
662
610
|
return this.modelCheck;
|
|
663
611
|
}
|
|
664
612
|
async supportsImageInput(model = this.model) {
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
const supported = await resolveModelSupportsImageInput(
|
|
668
|
-
this.novaClient,
|
|
669
|
-
model
|
|
670
|
-
);
|
|
671
|
-
this.imageSupportByModel.set(model, supported);
|
|
672
|
-
return supported;
|
|
613
|
+
if (!this.imageSupportByModel.has(model)) await this.listModels();
|
|
614
|
+
return this.imageSupportByModel.get(model) ?? false;
|
|
673
615
|
}
|
|
674
616
|
async compactContext() {
|
|
675
617
|
if (this.store.getState().busy || this.promptActive) {
|
|
@@ -793,7 +735,7 @@ var SessionRunner = class {
|
|
|
793
735
|
});
|
|
794
736
|
}
|
|
795
737
|
} catch (err) {
|
|
796
|
-
const message = err instanceof
|
|
738
|
+
const message = err instanceof Error ? err.message : "Unknown error.";
|
|
797
739
|
pendingToolFailure = `Request failed: ${message}`;
|
|
798
740
|
this.acpClient.appendError(message);
|
|
799
741
|
this.store.setState({ statusLine: "Request failed." });
|
|
@@ -947,11 +889,10 @@ var SessionRunner = class {
|
|
|
947
889
|
];
|
|
948
890
|
}
|
|
949
891
|
async createSession() {
|
|
950
|
-
this.loadMcpConfiguration(this.cwd);
|
|
951
892
|
await this.initialized;
|
|
952
893
|
const { sessionId, _meta } = await this.nova.newSession({
|
|
953
894
|
cwd: this.cwd,
|
|
954
|
-
mcpServers:
|
|
895
|
+
mcpServers: []
|
|
955
896
|
});
|
|
956
897
|
this.sessionId = sessionId;
|
|
957
898
|
this.applyMcpStatus(_meta);
|
|
@@ -975,10 +916,20 @@ var SessionRunner = class {
|
|
|
975
916
|
const value = meta?.["nova-ai-cli/mcp"];
|
|
976
917
|
if (!value || typeof value !== "object") return;
|
|
977
918
|
const record = value;
|
|
919
|
+
const strings = (value2) => Array.isArray(value2) ? value2.filter((entry) => typeof entry === "string") : void 0;
|
|
978
920
|
const configured = Array.isArray(record.configured) ? record.configured.flatMap((entry) => {
|
|
979
921
|
if (!entry || typeof entry !== "object") return [];
|
|
980
922
|
const item = entry;
|
|
981
|
-
return typeof item.name === "string" && typeof item.transport === "string" ? [
|
|
923
|
+
return typeof item.name === "string" && typeof item.transport === "string" ? [
|
|
924
|
+
{
|
|
925
|
+
name: item.name,
|
|
926
|
+
transport: item.transport,
|
|
927
|
+
...typeof item.url === "string" ? { url: item.url } : {},
|
|
928
|
+
...typeof item.command === "string" ? { command: item.command } : {},
|
|
929
|
+
...strings(item.args) ? { args: strings(item.args) } : {},
|
|
930
|
+
...strings(item.env) ? { env: strings(item.env) } : {}
|
|
931
|
+
}
|
|
932
|
+
] : [];
|
|
982
933
|
}) : [];
|
|
983
934
|
const connected = Array.isArray(record.connected) ? record.connected.filter(
|
|
984
935
|
(entry) => typeof entry === "string"
|
|
@@ -988,24 +939,7 @@ var SessionRunner = class {
|
|
|
988
939
|
const item = entry;
|
|
989
940
|
return typeof item.serverName === "string" && typeof item.message === "string" ? [{ serverName: item.serverName, message: item.message }] : [];
|
|
990
941
|
}) : [];
|
|
991
|
-
this.mcpStatus = {
|
|
992
|
-
configured,
|
|
993
|
-
connected,
|
|
994
|
-
failures: [...this.mcpConfigurationFailures, ...failures]
|
|
995
|
-
};
|
|
996
|
-
}
|
|
997
|
-
loadMcpConfiguration(cwd) {
|
|
998
|
-
const configuration = readWorkspaceMcpConfiguration(cwd);
|
|
999
|
-
this.mcpServers = configuration.servers;
|
|
1000
|
-
this.mcpConfigurationFailures = configuration.failures;
|
|
1001
|
-
this.mcpStatus = {
|
|
1002
|
-
configured: this.mcpServers.map((server) => ({
|
|
1003
|
-
name: server.name,
|
|
1004
|
-
transport: "type" in server ? server.type : "stdio"
|
|
1005
|
-
})),
|
|
1006
|
-
connected: [],
|
|
1007
|
-
failures: [...this.mcpConfigurationFailures]
|
|
1008
|
-
};
|
|
942
|
+
this.mcpStatus = { configured, connected, failures };
|
|
1009
943
|
}
|
|
1010
944
|
applySkillStatus(meta) {
|
|
1011
945
|
const value = meta?.["nova-ai-cli/skills"];
|
|
@@ -1023,12 +957,28 @@ var SessionRunner = class {
|
|
|
1023
957
|
] : [];
|
|
1024
958
|
});
|
|
1025
959
|
}
|
|
960
|
+
/**
|
|
961
|
+
* Starts the session ahead of the first prompt, so the agent's MCP and
|
|
962
|
+
* skill status is there for /mcp and the status line. A failure is left to
|
|
963
|
+
* the first prompt, which tries again and reports it.
|
|
964
|
+
*/
|
|
965
|
+
async prepareSession() {
|
|
966
|
+
try {
|
|
967
|
+
await this.ensureSession();
|
|
968
|
+
} catch {
|
|
969
|
+
this.sessionReady = Promise.resolve();
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
get sessionLoaded() {
|
|
973
|
+
return this.agentSessionLoaded;
|
|
974
|
+
}
|
|
1026
975
|
async ensureSession() {
|
|
1027
976
|
await this.ensureUsableModel();
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
977
|
+
for (; ; ) {
|
|
978
|
+
const ready = this.sessionReady;
|
|
979
|
+
await ready;
|
|
980
|
+
if (this.agentSessionLoaded) return;
|
|
981
|
+
if (this.sessionReady === ready) this.sessionReady = this.createSession();
|
|
1032
982
|
}
|
|
1033
983
|
}
|
|
1034
984
|
applyInteractionMode(mode, statusLine) {
|
|
@@ -1044,7 +994,7 @@ var SessionRunner = class {
|
|
|
1044
994
|
try {
|
|
1045
995
|
const usage = await this.nova.sessionContextUsage({
|
|
1046
996
|
sessionId,
|
|
1047
|
-
|
|
997
|
+
...this.model ? { model: this.model } : {},
|
|
1048
998
|
mode: this.interactionMode
|
|
1049
999
|
});
|
|
1050
1000
|
if (request === this.contextUsageRequest && sessionId === this.sessionId) {
|
|
@@ -1853,12 +1803,6 @@ function PlanView({
|
|
|
1853
1803
|
}
|
|
1854
1804
|
|
|
1855
1805
|
// src/tui/ink/controller.ts
|
|
1856
|
-
import { setWorkspaceTrusted } from "@datalabrotterdam/nova-ai-agent/core/nova-home.js";
|
|
1857
|
-
import {
|
|
1858
|
-
disabledSkillNames,
|
|
1859
|
-
discoverSkills as discoverSkills2,
|
|
1860
|
-
setSkillEnabled
|
|
1861
|
-
} from "@datalabrotterdam/nova-ai-agent/core/skills.js";
|
|
1862
1806
|
import { INTERACTION_MODES } from "@datalabrotterdam/nova-ai-agent/core/interaction-modes.js";
|
|
1863
1807
|
|
|
1864
1808
|
// src/tui/commands/builtins.ts
|
|
@@ -1983,8 +1927,8 @@ var builtinCommands = [
|
|
|
1983
1927
|
{
|
|
1984
1928
|
name: "trust",
|
|
1985
1929
|
description: "Trust this workspace: start the MCP servers it declares and apply its .nova-ai allow rules",
|
|
1986
|
-
run(ctx) {
|
|
1987
|
-
ctx.print(ctx.trustWorkspace());
|
|
1930
|
+
async run(ctx) {
|
|
1931
|
+
ctx.print(await ctx.trustWorkspace());
|
|
1988
1932
|
}
|
|
1989
1933
|
},
|
|
1990
1934
|
{
|
|
@@ -2011,16 +1955,16 @@ var builtinCommands = [
|
|
|
2011
1955
|
name: "mcp",
|
|
2012
1956
|
description: "Inspect configured MCP servers",
|
|
2013
1957
|
run(ctx) {
|
|
2014
|
-
ctx.openMcpInspector();
|
|
1958
|
+
return ctx.openMcpInspector();
|
|
2015
1959
|
}
|
|
2016
1960
|
},
|
|
2017
1961
|
{
|
|
2018
1962
|
name: "skills",
|
|
2019
1963
|
description: "List skills, or switch one: /skills on|off <name> [--global]",
|
|
2020
|
-
run(ctx, args) {
|
|
1964
|
+
async run(ctx, args) {
|
|
2021
1965
|
const words = args.trim().split(/\s+/).filter(Boolean);
|
|
2022
1966
|
if (!words.length) {
|
|
2023
|
-
ctx.openSkillInspector();
|
|
1967
|
+
await ctx.openSkillInspector();
|
|
2024
1968
|
return;
|
|
2025
1969
|
}
|
|
2026
1970
|
const global = words.includes("--global");
|
|
@@ -2029,7 +1973,7 @@ var builtinCommands = [
|
|
|
2029
1973
|
ctx.print("Usage: /skills, or /skills on|off <name> [--global]");
|
|
2030
1974
|
return;
|
|
2031
1975
|
}
|
|
2032
|
-
ctx.print(ctx.switchSkill(name, action === "on", global));
|
|
1976
|
+
ctx.print(await ctx.switchSkill(name, action === "on", global));
|
|
2033
1977
|
}
|
|
2034
1978
|
},
|
|
2035
1979
|
{
|
|
@@ -3874,7 +3818,7 @@ var TuiController = class {
|
|
|
3874
3818
|
};
|
|
3875
3819
|
getView = () => this.view;
|
|
3876
3820
|
start() {
|
|
3877
|
-
void this.runner.
|
|
3821
|
+
void this.runner.prepareSession().then(() => this.refresh());
|
|
3878
3822
|
this.syncWindowTitle();
|
|
3879
3823
|
this.animationTimer = setInterval(() => this.syncWindowTitle(true), 120);
|
|
3880
3824
|
this.animationTimer.unref?.();
|
|
@@ -4084,9 +4028,13 @@ ${result.output}${result.truncated ? "\n\n[output truncated]" : ""}`
|
|
|
4084
4028
|
state: new ToolInspectorState(() => this.toolCalls(), this.closeOverlay)
|
|
4085
4029
|
});
|
|
4086
4030
|
};
|
|
4087
|
-
openMcpInspector = () => {
|
|
4088
|
-
|
|
4031
|
+
openMcpInspector = async () => {
|
|
4032
|
+
if (!this.runner.sessionLoaded) {
|
|
4033
|
+
this.showText("MCP servers", "Connecting MCP servers...");
|
|
4034
|
+
await this.runner.prepareSession();
|
|
4035
|
+
}
|
|
4089
4036
|
const connection = this.runner.mcpSessionStatus();
|
|
4037
|
+
const servers = connection.configured;
|
|
4090
4038
|
const configuredNames = new Set(servers.map((server) => server.name));
|
|
4091
4039
|
const configurationFailures = connection.failures.filter((failure) => !configuredNames.has(failure.serverName)).map((failure) => `${failure.serverName}
|
|
4092
4040
|
status: failed - ${failure.message}`);
|
|
@@ -4100,16 +4048,20 @@ status: failed - ${failure.message}`);
|
|
|
4100
4048
|
);
|
|
4101
4049
|
};
|
|
4102
4050
|
/** Every discovered skill with its state; switched-off skills stay listed. */
|
|
4103
|
-
openSkillInspector = () => {
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4051
|
+
openSkillInspector = async () => {
|
|
4052
|
+
let skills;
|
|
4053
|
+
try {
|
|
4054
|
+
skills = await this.runner.listSkills();
|
|
4055
|
+
} catch (error) {
|
|
4056
|
+
this.showText("Skills", errorMessage(error, "Could not list the skills."));
|
|
4057
|
+
return;
|
|
4058
|
+
}
|
|
4059
|
+
const on = skills.filter((skill) => skill.enabled).length;
|
|
4108
4060
|
this.showText(
|
|
4109
4061
|
`Skills (${on} on, ${skills.length - on} off)`,
|
|
4110
4062
|
skills.length ? [
|
|
4111
4063
|
...skills.map(
|
|
4112
|
-
(skill) => `${skill.name} [${skill.source}]${
|
|
4064
|
+
(skill) => `${skill.name} [${skill.source}]${skill.enabled ? "" : " (off)"}
|
|
4113
4065
|
${skill.description}
|
|
4114
4066
|
${skill.path}`
|
|
4115
4067
|
),
|
|
@@ -4118,17 +4070,15 @@ ${skill.path}`
|
|
|
4118
4070
|
);
|
|
4119
4071
|
};
|
|
4120
4072
|
/** /skills on|off <name> [--global]: applies from the next message. */
|
|
4121
|
-
switchSkill = (name, enabled, global) => {
|
|
4122
|
-
|
|
4123
|
-
if (!discoverSkills2(cwd).some((skill) => skill.name === name)) {
|
|
4124
|
-
return `No skill named "${name}". /skills lists them.`;
|
|
4125
|
-
}
|
|
4073
|
+
switchSkill = async (name, enabled, global) => {
|
|
4074
|
+
let skills;
|
|
4126
4075
|
try {
|
|
4127
|
-
|
|
4076
|
+
skills = await this.runner.setSkillEnabled(name, enabled, global);
|
|
4128
4077
|
} catch (error) {
|
|
4129
|
-
|
|
4078
|
+
const message = errorMessage(error, "Could not change the skill.");
|
|
4079
|
+
return message.startsWith("No skill named") ? `${message} /skills lists them.` : message;
|
|
4130
4080
|
}
|
|
4131
|
-
const stillOff = enabled &&
|
|
4081
|
+
const stillOff = enabled && skills.some((skill) => skill.name === name && !skill.enabled);
|
|
4132
4082
|
return stillOff ? `Switched "${name}" on ${global ? "globally" : "in this project"}, but it is still off ${global ? "in this project" : "globally"} (use ${global ? "/skills on " + name : "/skills on " + name + " --global"}).` : `Skill "${name}" is ${enabled ? "on" : "off"} ${global ? "everywhere" : "in this project"} from the next message.`;
|
|
4133
4083
|
};
|
|
4134
4084
|
openUsageInspector = async () => {
|
|
@@ -4232,8 +4182,12 @@ ${skill.path}`
|
|
|
4232
4182
|
getPermissionMode: () => this.store.getState().permissionMode,
|
|
4233
4183
|
setPermissionMode: (mode) => this.setPermissionMode(mode),
|
|
4234
4184
|
openPermissionPicker: this.openPermissionModes,
|
|
4235
|
-
trustWorkspace: () => {
|
|
4236
|
-
|
|
4185
|
+
trustWorkspace: async () => {
|
|
4186
|
+
try {
|
|
4187
|
+
await this.runner.trustWorkspace();
|
|
4188
|
+
} catch (error) {
|
|
4189
|
+
return errorMessage(error, "Could not trust the workspace.");
|
|
4190
|
+
}
|
|
4237
4191
|
return "Workspace trusted: its allow rules apply now; MCP servers it declares start with the next session (/clear).";
|
|
4238
4192
|
},
|
|
4239
4193
|
openToolInspector: this.openToolInspector,
|
|
@@ -4410,14 +4364,12 @@ ${skill.path}`
|
|
|
4410
4364
|
}
|
|
4411
4365
|
};
|
|
4412
4366
|
function formatMcpServer(server, status) {
|
|
4413
|
-
const
|
|
4414
|
-
|
|
4415
|
-
if (
|
|
4416
|
-
if ("command" in server) {
|
|
4367
|
+
const lines = [`${server.name} [${server.transport}]`];
|
|
4368
|
+
if (server.url) lines.push(`url: ${server.url}`);
|
|
4369
|
+
if (server.command) {
|
|
4417
4370
|
lines.push(`command: ${server.command}`);
|
|
4418
|
-
if (server.args
|
|
4419
|
-
if (server.env
|
|
4420
|
-
lines.push(`env: ${server.env.map((entry) => entry.name).join(", ")} (values hidden)`);
|
|
4371
|
+
if (server.args?.length) lines.push(`args: ${server.args.join(" ")}`);
|
|
4372
|
+
if (server.env?.length) lines.push(`env: ${server.env.join(", ")} (values hidden)`);
|
|
4421
4373
|
}
|
|
4422
4374
|
const failure = status.failures.find((item) => item.serverName === server.name);
|
|
4423
4375
|
lines.push(
|
|
@@ -4937,19 +4889,14 @@ function useWorking(view) {
|
|
|
4937
4889
|
}
|
|
4938
4890
|
|
|
4939
4891
|
// src/tui/index.ts
|
|
4892
|
+
import { workspaceNeedsTrust } from "@datalabrotterdam/nova-ai-agent/core/workspace-mcp.js";
|
|
4940
4893
|
async function runChat(options) {
|
|
4941
4894
|
const stored = readCredentials();
|
|
4942
4895
|
if (!stored) {
|
|
4943
4896
|
console.error("nova-ai isn't connected to a Nova AI account yet. Run `nova-ai login` first.");
|
|
4944
4897
|
return 1;
|
|
4945
4898
|
}
|
|
4946
|
-
const model = options.model ?? stored.defaultModel ?? process.env.NOVA_MODEL
|
|
4947
|
-
if (!model) {
|
|
4948
|
-
console.error("nova-ai has no model to use yet. Start it with one, for example:");
|
|
4949
|
-
console.error(" nova-ai --model <model-id> (or set NOVA_MODEL)");
|
|
4950
|
-
console.error("Then Ctrl+P in nova-ai switches and remembers the model.");
|
|
4951
|
-
return 1;
|
|
4952
|
-
}
|
|
4899
|
+
const model = options.model ?? stored.defaultModel ?? process.env.NOVA_MODEL;
|
|
4953
4900
|
const credentials = { ...stored, defaultModel: model };
|
|
4954
4901
|
const updateCheck = checkForUpdate().then((update) => withInstallability(update));
|
|
4955
4902
|
const updatedFrom = process.env[UPDATED_FROM_ENV];
|
|
@@ -4985,19 +4932,6 @@ async function runChat(options) {
|
|
|
4985
4932
|
}
|
|
4986
4933
|
return 0;
|
|
4987
4934
|
}
|
|
4988
|
-
async function pickDefaultModel(credentials) {
|
|
4989
|
-
try {
|
|
4990
|
-
const { data } = await createNovaClient2(credentials.apiKey).models.list({ limit: 100 });
|
|
4991
|
-
const model = resolveDefaultModel2(data, void 0);
|
|
4992
|
-
if (model) {
|
|
4993
|
-
saveDefaultModel2(model);
|
|
4994
|
-
console.log(`Using model ${model} (Ctrl+P switches).`);
|
|
4995
|
-
}
|
|
4996
|
-
return model;
|
|
4997
|
-
} catch {
|
|
4998
|
-
return void 0;
|
|
4999
|
-
}
|
|
5000
|
-
}
|
|
5001
4935
|
async function askToTrust(cwd) {
|
|
5002
4936
|
const prompt = createInterface2({ input: process.stdin, output: process.stdout });
|
|
5003
4937
|
try {
|
|
@@ -5007,7 +4941,7 @@ ${cwd}`);
|
|
|
5007
4941
|
console.log("Trusting it starts those servers and applies those rules. Only trust code you know.");
|
|
5008
4942
|
const answer = (await prompt.question("Trust this workspace? [y/N] ")).trim().toLowerCase();
|
|
5009
4943
|
if (answer === "y" || answer === "yes") {
|
|
5010
|
-
|
|
4944
|
+
setWorkspaceTrusted(cwd, true);
|
|
5011
4945
|
} else {
|
|
5012
4946
|
console.log("Not trusted: its MCP servers stay off and its allow rules are ignored. Use /trust later to change this.");
|
|
5013
4947
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datalabrotterdam/nova-ai-cli",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.6",
|
|
4
4
|
"description": "Agentic coding with DataLab Rotterdam's Nova AI in your terminal: interactive TUI, headless mode, and an ACP agent for editors",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "DataLab Rotterdam",
|
|
@@ -39,8 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@agentclientprotocol/sdk": "^0.29.0",
|
|
42
|
-
"@datalabrotterdam/nova-ai-agent": "^1.0.0-alpha.
|
|
43
|
-
"@datalabrotterdam/nova-sdk": "^1.3.2",
|
|
42
|
+
"@datalabrotterdam/nova-ai-agent": "^1.0.0-alpha.3",
|
|
44
43
|
"chalk": "^5.3.0",
|
|
45
44
|
"cli-highlight": "^2.1.11",
|
|
46
45
|
"diff": "^9.0.0",
|
|
@@ -75,5 +74,8 @@
|
|
|
75
74
|
}
|
|
76
75
|
]
|
|
77
76
|
]
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@datalabrotterdam/nova-sdk": "^1.3.2"
|
|
78
80
|
}
|
|
79
81
|
}
|