@tjamescouch/niki 0.5.5 → 0.5.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/bin/niki +63 -12
- package/package.json +1 -1
package/bin/niki
CHANGED
|
@@ -34,6 +34,7 @@ if (SEPARATOR === -1 || SEPARATOR === process.argv.length - 1) {
|
|
|
34
34
|
Usage: niki [options] -- <command> [args...]
|
|
35
35
|
|
|
36
36
|
Options:
|
|
37
|
+
--profile <name> Apply a preset profile (default: none; profiles: longrun)
|
|
37
38
|
--budget <tokens> Max total tokens (input+output) before SIGTERM (default: 1000000)
|
|
38
39
|
--timeout <seconds> Max wall-clock runtime before SIGTERM (default: 3600)
|
|
39
40
|
--max-sends <n> Max agentchat_send calls per minute (default: 10)
|
|
@@ -53,8 +54,10 @@ Options:
|
|
|
53
54
|
--restart Restart the child process when it exits (default: off)
|
|
54
55
|
--max-restarts <n> Max restart attempts, 0=unlimited (default: 0)
|
|
55
56
|
--restart-delay <secs> Delay between restarts with ±30% jitter (default: 5)
|
|
57
|
+
--kill-orphaned-mcp Kill stale agentchat-mcp processes on startup (default: off)
|
|
56
58
|
|
|
57
59
|
Examples:
|
|
60
|
+
niki --profile longrun -- gro --persistent --model sonnet
|
|
58
61
|
niki --budget 500000 -- claude -p "your prompt" --verbose
|
|
59
62
|
niki --timeout 1800 --max-sends 5 -- claude -p "..." --model sonnet --verbose
|
|
60
63
|
niki --restart --max-restarts 10 -- gro --model gpt-5.2 "your prompt"`);
|
|
@@ -65,28 +68,66 @@ const nikiArgs = process.argv.slice(2, SEPARATOR);
|
|
|
65
68
|
const childCmd = process.argv[SEPARATOR + 1];
|
|
66
69
|
const childArgs = process.argv.slice(SEPARATOR + 2);
|
|
67
70
|
|
|
71
|
+
function getProfileArg(args) {
|
|
72
|
+
const i = args.indexOf("--profile");
|
|
73
|
+
if (i === -1) return null;
|
|
74
|
+
const v = args[i + 1];
|
|
75
|
+
return v && !v.startsWith("-") ? v : null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const DEFAULTS = {
|
|
79
|
+
budget: "1000000",
|
|
80
|
+
timeout: "3600",
|
|
81
|
+
"max-sends": "10",
|
|
82
|
+
"max-tool-calls": "30",
|
|
83
|
+
"stall-timeout": "60",
|
|
84
|
+
"startup-timeout": "180",
|
|
85
|
+
"dead-air-timeout": "5",
|
|
86
|
+
"max-nudges": "3",
|
|
87
|
+
cooldown: "5",
|
|
88
|
+
"poll-interval": "1000",
|
|
89
|
+
"max-restarts": "0",
|
|
90
|
+
"restart-delay": "5"
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const PROFILES = {
|
|
94
|
+
longrun: {
|
|
95
|
+
timeout: "86400",
|
|
96
|
+
"stall-timeout": "0",
|
|
97
|
+
"startup-timeout": "0",
|
|
98
|
+
"dead-air-timeout": "0",
|
|
99
|
+
cooldown: "15"
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const profileArg = getProfileArg(nikiArgs);
|
|
104
|
+
const D = profileArg && PROFILES[profileArg] ? { ...DEFAULTS, ...PROFILES[profileArg] } : DEFAULTS;
|
|
105
|
+
|
|
106
|
+
|
|
68
107
|
const { values: opts } = parseArgs({
|
|
69
108
|
args: nikiArgs,
|
|
70
109
|
options: {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
'max-
|
|
75
|
-
'
|
|
76
|
-
'
|
|
77
|
-
'
|
|
78
|
-
'
|
|
110
|
+
profile: { type: 'string' },
|
|
111
|
+
budget: { type: 'string', default: D.budget },
|
|
112
|
+
timeout: { type: 'string', default: D.timeout },
|
|
113
|
+
'max-sends': { type: 'string', default: D["max-sends"] },
|
|
114
|
+
'max-tool-calls': { type: 'string', default: D["max-tool-calls"] },
|
|
115
|
+
'stall-timeout': { type: 'string', default: D["stall-timeout"] },
|
|
116
|
+
'startup-timeout': { type: 'string', default: D["startup-timeout"] },
|
|
117
|
+
'dead-air-timeout': { type: 'string', default: D["dead-air-timeout"] },
|
|
118
|
+
'max-nudges': { type: 'string', default: D["max-nudges"] },
|
|
79
119
|
log: { type: 'string' },
|
|
80
120
|
'log-level': { type: 'string', default: 'info' },
|
|
81
121
|
'log-json': { type: 'boolean', default: false },
|
|
82
122
|
state: { type: 'string' },
|
|
83
123
|
metrics: { type: 'string' },
|
|
84
|
-
cooldown: { type: 'string', default:
|
|
124
|
+
cooldown: { type: 'string', default: D.cooldown },
|
|
85
125
|
'abort-file': { type: 'string' },
|
|
86
|
-
'poll-interval': { type: 'string', default:
|
|
126
|
+
'poll-interval': { type: 'string', default: D["poll-interval"] },
|
|
87
127
|
restart: { type: 'boolean', default: false },
|
|
88
|
-
'max-restarts': { type: 'string', default:
|
|
89
|
-
'restart-delay': { type: 'string', default:
|
|
128
|
+
'max-restarts': { type: 'string', default: D["max-restarts"] },
|
|
129
|
+
'restart-delay': { type: 'string', default: D["restart-delay"] },
|
|
130
|
+
'kill-orphaned-mcp': { type: 'boolean', default: false },
|
|
90
131
|
},
|
|
91
132
|
});
|
|
92
133
|
|
|
@@ -107,6 +148,7 @@ const METRICS_FILE = opts.metrics;
|
|
|
107
148
|
const RESTART = opts.restart;
|
|
108
149
|
const MAX_RESTARTS = parseInt(opts['max-restarts'], 10);
|
|
109
150
|
const RESTART_DELAY_S = parseFloat(opts['restart-delay']);
|
|
151
|
+
const KILL_ORPHANED_MCP = opts['kill-orphaned-mcp'];
|
|
110
152
|
const LOG_JSON = opts['log-json'];
|
|
111
153
|
|
|
112
154
|
// --- Invocation rate limiting (prevent tight crash loops) ---
|
|
@@ -701,6 +743,15 @@ function startChild() {
|
|
|
701
743
|
log(`Restart: enabled | max: ${MAX_RESTARTS || 'unlimited'} | delay: ${RESTART_DELAY_S}s ±30% | restarts so far: ${state.restarts}`, 'debug');
|
|
702
744
|
}
|
|
703
745
|
|
|
746
|
+
// If --kill-orphaned-mcp: kill stale agentchat-mcp procs before spawning.
|
|
747
|
+
// Prevents session_displaced churn from orphaned prior-session MCP processes.
|
|
748
|
+
if (KILL_ORPHANED_MCP) {
|
|
749
|
+
try {
|
|
750
|
+
execSync('pkill -f agentchat-mcp', { stdio: 'ignore' });
|
|
751
|
+
log('Killed orphaned agentchat-mcp processes', 'debug');
|
|
752
|
+
} catch { /* pkill exits non-zero when nothing matched */ }
|
|
753
|
+
}
|
|
754
|
+
|
|
704
755
|
child = spawn(childCmd, childArgs, {
|
|
705
756
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
706
757
|
env: process.env,
|