@tjamescouch/niki 0.5.6 → 0.5.7
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/bin/niki +38 -1
- package/package.json +1 -1
package/bin/niki
CHANGED
|
@@ -55,12 +55,16 @@ Options:
|
|
|
55
55
|
--max-restarts <n> Max restart attempts, 0=unlimited (default: 0)
|
|
56
56
|
--restart-delay <secs> Delay between restarts with ±30% jitter (default: 5)
|
|
57
57
|
--kill-orphaned-mcp Kill stale agentchat-mcp processes on startup (default: off)
|
|
58
|
+
--on-kill <command> Run shell command when agent is killed (receives context via env vars)
|
|
59
|
+
Env vars: NIKI_KILL_REASON, NIKI_TOKENS, NIKI_BUDGET, NIKI_DURATION,
|
|
60
|
+
NIKI_SENDS, NIKI_TOOL_CALLS, NIKI_PID, NIKI_CMD
|
|
58
61
|
|
|
59
62
|
Examples:
|
|
60
63
|
niki --profile longrun -- gro --persistent --model sonnet
|
|
61
64
|
niki --budget 500000 -- claude -p "your prompt" --verbose
|
|
62
65
|
niki --timeout 1800 --max-sends 5 -- claude -p "..." --model sonnet --verbose
|
|
63
|
-
niki --restart --max-restarts 10 -- gro --model gpt-5.2 "your prompt"
|
|
66
|
+
niki --restart --max-restarts 10 -- gro --model gpt-5.2 "your prompt"
|
|
67
|
+
niki --on-kill 'curl -d "$NIKI_KILL_REASON: $NIKI_TOKENS tokens" ntfy.sh/my-agents' -- claude -p "..."`);
|
|
64
68
|
process.exit(1);
|
|
65
69
|
}
|
|
66
70
|
|
|
@@ -128,6 +132,7 @@ const { values: opts } = parseArgs({
|
|
|
128
132
|
'max-restarts': { type: 'string', default: D["max-restarts"] },
|
|
129
133
|
'restart-delay': { type: 'string', default: D["restart-delay"] },
|
|
130
134
|
'kill-orphaned-mcp': { type: 'boolean', default: false },
|
|
135
|
+
'on-kill': { type: 'string' },
|
|
131
136
|
},
|
|
132
137
|
});
|
|
133
138
|
|
|
@@ -149,6 +154,7 @@ const RESTART = opts.restart;
|
|
|
149
154
|
const MAX_RESTARTS = parseInt(opts['max-restarts'], 10);
|
|
150
155
|
const RESTART_DELAY_S = parseFloat(opts['restart-delay']);
|
|
151
156
|
const KILL_ORPHANED_MCP = opts['kill-orphaned-mcp'];
|
|
157
|
+
const ON_KILL = opts['on-kill'];
|
|
152
158
|
const LOG_JSON = opts['log-json'];
|
|
153
159
|
|
|
154
160
|
// --- Invocation rate limiting (prevent tight crash loops) ---
|
|
@@ -417,6 +423,35 @@ function checkRateLimits() {
|
|
|
417
423
|
}
|
|
418
424
|
return null;
|
|
419
425
|
}
|
|
426
|
+
// --- Kill notification hook ---
|
|
427
|
+
|
|
428
|
+
function fireOnKill(reason) {
|
|
429
|
+
if (!ON_KILL) return;
|
|
430
|
+
const duration = Math.round((Date.now() - new Date(state.startedAt).getTime()) / 1000);
|
|
431
|
+
const env = {
|
|
432
|
+
...process.env,
|
|
433
|
+
NIKI_KILL_REASON: reason,
|
|
434
|
+
NIKI_TOKENS: String(state.tokensTotal),
|
|
435
|
+
NIKI_BUDGET: String(BUDGET),
|
|
436
|
+
NIKI_DURATION: String(duration),
|
|
437
|
+
NIKI_SENDS: String(state.sendCalls),
|
|
438
|
+
NIKI_TOOL_CALLS: String(state.toolCalls),
|
|
439
|
+
NIKI_PID: String(state.pid),
|
|
440
|
+
NIKI_CMD: childCmd,
|
|
441
|
+
};
|
|
442
|
+
try {
|
|
443
|
+
const hook = spawn('sh', ['-c', ON_KILL], {
|
|
444
|
+
env,
|
|
445
|
+
stdio: 'ignore',
|
|
446
|
+
detached: true,
|
|
447
|
+
});
|
|
448
|
+
hook.unref();
|
|
449
|
+
log(`on-kill hook fired: ${ON_KILL}`, 'debug');
|
|
450
|
+
} catch (err) {
|
|
451
|
+
log(`on-kill hook failed: ${err.message}`, 'warn');
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
420
455
|
// --- Kill logic ---
|
|
421
456
|
|
|
422
457
|
let child = null;
|
|
@@ -443,6 +478,8 @@ function killChild(reason) {
|
|
|
443
478
|
}
|
|
444
479
|
log(`KILL — reason: ${reason} | tokens: ${state.tokensTotal}/${BUDGET} | sends: ${state.sendCallsThisMinute}/min | tools: ${state.toolCallsThisMinute}/min`, 'error');
|
|
445
480
|
|
|
481
|
+
fireOnKill(reason);
|
|
482
|
+
|
|
446
483
|
child.kill('SIGTERM');
|
|
447
484
|
|
|
448
485
|
// Grace period, then SIGKILL
|