niahere 0.2.84 → 0.2.85
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 +2 -0
- package/package.json +1 -1
- package/src/chat/engine.ts +22 -2
- package/src/cli/active.ts +36 -0
- package/src/cli/index.ts +14 -0
- package/src/cli/model.ts +29 -0
package/README.md
CHANGED
|
@@ -55,6 +55,8 @@ nia init — interactive setup (db, channels, persona, age
|
|
|
55
55
|
nia start / stop — daemon + OS service (launchd/systemd)
|
|
56
56
|
nia restart — restart daemon (service-aware)
|
|
57
57
|
nia status — show daemon, jobs, channels, chat rooms
|
|
58
|
+
nia active [--full] — show active engine count or details
|
|
59
|
+
nia model [name] — show or set global Claude model
|
|
58
60
|
nia health — check daemon, db, channels, config
|
|
59
61
|
nia chat [-c|-r] [--channel ch] — terminal chat (new by default, -c continue, -r pick)
|
|
60
62
|
nia run <prompt> — one-shot prompt execution
|
package/package.json
CHANGED
package/src/chat/engine.ts
CHANGED
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
import { truncate, formatToolUse } from "../utils/format-activity";
|
|
23
23
|
import { finalizeSession, cancelPending } from "../core/finalizer";
|
|
24
24
|
import { log } from "../utils/log";
|
|
25
|
+
import { getConfig } from "../utils/config";
|
|
25
26
|
import { isRetryableApiError, sleep } from "../utils/retry";
|
|
26
27
|
import { registerActiveHandle, unregisterActiveHandle } from "../core/active-handles";
|
|
27
28
|
import { resolveJobPrompt } from "../core/job-prompt";
|
|
@@ -108,6 +109,11 @@ export function formatChatError(rawError: string | null | undefined): string {
|
|
|
108
109
|
return `[error] ${error}`;
|
|
109
110
|
}
|
|
110
111
|
|
|
112
|
+
export function resolveSdkModel(contextModel?: string | null): string | undefined {
|
|
113
|
+
const model = contextModel || getConfig().model;
|
|
114
|
+
return model && model !== "default" ? model : undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
111
117
|
/**
|
|
112
118
|
* Push-based async iterable for streaming user messages to the SDK.
|
|
113
119
|
* Keeps the query subprocess alive between messages.
|
|
@@ -177,30 +183,40 @@ export async function createChatEngine(opts: EngineOptions): Promise<ChatEngine>
|
|
|
177
183
|
|
|
178
184
|
// Context overrides: employee > agent > job > default
|
|
179
185
|
let cwd = homedir();
|
|
186
|
+
let contextModel: string | null | undefined;
|
|
180
187
|
if (opts.employee) {
|
|
181
188
|
const empPrompt = buildEmployeePrompt(opts.employee);
|
|
182
189
|
if (empPrompt) systemPrompt = empPrompt;
|
|
183
190
|
const emp = getEmployee(opts.employee);
|
|
191
|
+
contextModel = emp?.model;
|
|
184
192
|
if (emp?.repo && existsSync(emp.repo)) cwd = emp.repo;
|
|
185
193
|
} else if (opts.agent) {
|
|
186
194
|
const agents = scanAgents();
|
|
187
195
|
const agentDef = agents.find((a) => a.name === opts.agent);
|
|
188
|
-
if (agentDef)
|
|
196
|
+
if (agentDef) {
|
|
197
|
+
systemPrompt = agentDef.body + "\n\n" + buildContextSuffix("chat");
|
|
198
|
+
contextModel = agentDef.model;
|
|
199
|
+
}
|
|
189
200
|
} else if (opts.job) {
|
|
190
201
|
// Job chat: load job and use its context
|
|
191
202
|
const jobData = await Job.get(opts.job);
|
|
192
203
|
if (jobData) {
|
|
204
|
+
contextModel = jobData.model;
|
|
193
205
|
// If job has an employee, use employee prompt
|
|
194
206
|
if (jobData.employee) {
|
|
195
207
|
const empPrompt = buildEmployeePrompt(jobData.employee);
|
|
196
208
|
if (empPrompt) systemPrompt = empPrompt;
|
|
197
209
|
const emp = getEmployee(jobData.employee);
|
|
210
|
+
if (!contextModel) contextModel = emp?.model;
|
|
198
211
|
if (emp?.repo && existsSync(emp.repo)) cwd = emp.repo;
|
|
199
212
|
} else if (jobData.agent) {
|
|
200
213
|
// If job has an agent, use agent prompt + context
|
|
201
214
|
const agents = scanAgents();
|
|
202
215
|
const agentDef = agents.find((a) => a.name === jobData.agent);
|
|
203
|
-
if (agentDef)
|
|
216
|
+
if (agentDef) {
|
|
217
|
+
systemPrompt = agentDef.body + "\n\n" + buildContextSuffix("chat");
|
|
218
|
+
if (!contextModel) contextModel = agentDef.model;
|
|
219
|
+
}
|
|
204
220
|
}
|
|
205
221
|
const resolvedPrompt = resolveJobPrompt(jobData);
|
|
206
222
|
const source = resolvedPrompt.source === "file" ? ` from ${resolvedPrompt.filePath}` : "";
|
|
@@ -316,6 +332,10 @@ export async function createChatEngine(opts: EngineOptions): Promise<ChatEngine>
|
|
|
316
332
|
settingSources: ["project", "user"],
|
|
317
333
|
skills: [],
|
|
318
334
|
};
|
|
335
|
+
const model = resolveSdkModel(contextModel);
|
|
336
|
+
if (model) {
|
|
337
|
+
options.model = model;
|
|
338
|
+
}
|
|
319
339
|
|
|
320
340
|
if (sessionId) {
|
|
321
341
|
options.resume = sessionId;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ActiveEngine } from "../db/models";
|
|
2
|
+
import { withDb } from "../db/with-db";
|
|
3
|
+
import { errMsg } from "../utils/errors";
|
|
4
|
+
import { dateSortValue, formatTimeLine } from "../utils/format";
|
|
5
|
+
|
|
6
|
+
function hasFullFlag(argv: string[]): boolean {
|
|
7
|
+
return argv.includes("--full");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function activeCommand(argv: string[] = []): Promise<void> {
|
|
11
|
+
const full = hasFullFlag(argv);
|
|
12
|
+
const now = new Date();
|
|
13
|
+
let engines: Awaited<ReturnType<typeof ActiveEngine.list>> = [];
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
await withDb(async () => {
|
|
17
|
+
engines = await ActiveEngine.list();
|
|
18
|
+
});
|
|
19
|
+
} catch (err) {
|
|
20
|
+
console.error(`active engines unavailable: ${errMsg(err)}`);
|
|
21
|
+
process.exitCode = 1;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!full) {
|
|
26
|
+
console.log(String(engines.length));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log(`Active engines: ${engines.length === 0 ? "none" : engines.length}`);
|
|
31
|
+
for (const engine of engines.sort((a, b) => dateSortValue(a.startedAt) - dateSortValue(b.startedAt))) {
|
|
32
|
+
const started = formatTimeLine(engine.startedAt, now);
|
|
33
|
+
const ping = formatTimeLine(engine.lastPing, now);
|
|
34
|
+
console.log(` ${engine.room} (${engine.channel}) • started ${started} • last ping ${ping}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -11,6 +11,8 @@ import { errMsg } from "../utils/errors";
|
|
|
11
11
|
import { fail, ICON_PASS, ICON_WARN } from "../utils/cli";
|
|
12
12
|
import { jobCommand } from "./job";
|
|
13
13
|
import { statusCommand } from "./status";
|
|
14
|
+
import { activeCommand } from "./active";
|
|
15
|
+
import { modelCommand } from "./model";
|
|
14
16
|
import { sendCommand, telegramCommand, slackCommand } from "./channels";
|
|
15
17
|
import { rulesCommand, memoryCommand } from "./self";
|
|
16
18
|
import { watchCommand } from "./watch";
|
|
@@ -127,6 +129,16 @@ switch (command) {
|
|
|
127
129
|
break;
|
|
128
130
|
}
|
|
129
131
|
|
|
132
|
+
case "active": {
|
|
133
|
+
await activeCommand(process.argv.slice(3));
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
case "model": {
|
|
138
|
+
await modelCommand(process.argv.slice(3));
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
|
|
130
142
|
case "health": {
|
|
131
143
|
const { healthCommand } = await import("../commands/health");
|
|
132
144
|
await healthCommand();
|
|
@@ -565,6 +577,8 @@ Daemon:
|
|
|
565
577
|
restart [--wait N] [--force] Restart daemon
|
|
566
578
|
update [--wait N] [--force] Update to latest version
|
|
567
579
|
status [--json --rooms N --all] Show daemon, jobs, channels
|
|
580
|
+
active [--full] Show active engine count or details
|
|
581
|
+
model [name] Show or set global Claude model
|
|
568
582
|
health Check daemon, db, channels, config
|
|
569
583
|
logs [-f] [--channel ch] Daemon logs (filter by channel)
|
|
570
584
|
|
package/src/cli/model.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { isRunning, readPid } from "../core/daemon";
|
|
2
|
+
import { getConfig, resetConfig, updateRawConfig } from "../utils/config";
|
|
3
|
+
|
|
4
|
+
function printUsage(): void {
|
|
5
|
+
console.log("Usage: nia model [default|sonnet|opus|opusplan|haiku|<model-id>]");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function modelCommand(argv: string[] = []): Promise<void> {
|
|
9
|
+
const model = argv[0];
|
|
10
|
+
|
|
11
|
+
if (!model) {
|
|
12
|
+
console.log(`model = ${getConfig().model}`);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (model === "--help" || model === "-h") {
|
|
17
|
+
printUsage();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
updateRawConfig({ model });
|
|
22
|
+
resetConfig();
|
|
23
|
+
console.log(`model = ${model}`);
|
|
24
|
+
|
|
25
|
+
const pid = readPid();
|
|
26
|
+
if (pid && isRunning()) {
|
|
27
|
+
process.kill(pid, "SIGHUP");
|
|
28
|
+
}
|
|
29
|
+
}
|