@misterhuydo/sentinel 1.0.62 → 1.0.64
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/.cairn/.hint-lock +1 -1
- package/.cairn/minify-map.json +7 -1
- package/.cairn/session.json +2 -2
- package/.cairn/views/244a09_generate.js +212 -0
- package/.cairn/views/a348d8_sentinel.js +58 -0
- package/lib/generate.js +2 -2
- package/package.json +1 -1
- package/python/sentinel/sentinel_boss.py +12 -2
package/.cairn/.hint-lock
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2026-03-
|
|
1
|
+
2026-03-22T15:53:29.003Z
|
package/.cairn/minify-map.json
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"J:\\Projects\\Sentinel\\cli\\lib\\upgrade.js": {
|
|
3
3
|
"tempPath": "J:\\Projects\\Sentinel\\cli\\.cairn\\views\\fb78ac_upgrade.js",
|
|
4
|
-
"state": "
|
|
4
|
+
"state": "edit-ready",
|
|
5
5
|
"minifiedAt": 1774129312316.9353,
|
|
6
6
|
"readCount": 1
|
|
7
|
+
},
|
|
8
|
+
"J:\\Projects\\Sentinel\\cli\\bin\\sentinel.js": {
|
|
9
|
+
"tempPath": "J:\\Projects\\Sentinel\\cli\\.cairn\\views\\a348d8_sentinel.js",
|
|
10
|
+
"state": "compressed",
|
|
11
|
+
"minifiedAt": 1774128147034.2527,
|
|
12
|
+
"readCount": 1
|
|
7
13
|
}
|
|
8
14
|
}
|
package/.cairn/session.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"message": "Auto-checkpoint at 2026-03-
|
|
3
|
-
"checkpoint_at": "2026-03-
|
|
2
|
+
"message": "Auto-checkpoint at 2026-03-22T15:44:29.732Z",
|
|
3
|
+
"checkpoint_at": "2026-03-22T15:44:29.733Z",
|
|
4
4
|
"active_files": [],
|
|
5
5
|
"notes": [],
|
|
6
6
|
"mtime_snapshot": {}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
function writeExampleProject(projectDir, codeDir, pythonBin, anthropicKey = '', slackTokens = {}) {
|
|
5
|
+
const configDir = path.join(projectDir, 'config', 'log-configs');
|
|
6
|
+
const repoDir = path.join(projectDir, 'config', 'repo-configs');
|
|
7
|
+
fs.ensureDirSync(configDir);
|
|
8
|
+
fs.ensureDirSync(repoDir);
|
|
9
|
+
const tplDir = path.join(__dirname, '..', 'templates');
|
|
10
|
+
let sentinelProps = fs.readFileSync(path.join(tplDir, 'sentinel.properties'), 'utf8');
|
|
11
|
+
if (anthropicKey) {
|
|
12
|
+
sentinelProps = sentinelProps.replace(/^# ANTHROPIC_API_KEY=.*/m, `ANTHROPIC_API_KEY=${anthropicKey}`);
|
|
13
|
+
}
|
|
14
|
+
if (slackTokens.botToken) {
|
|
15
|
+
sentinelProps = sentinelProps.replace(/^# SLACK_BOT_TOKEN=.*/m, `SLACK_BOT_TOKEN=${slackTokens.botToken}`);
|
|
16
|
+
}
|
|
17
|
+
if (slackTokens.appToken) {
|
|
18
|
+
sentinelProps = sentinelProps.replace(/^# SLACK_APP_TOKEN=.*/m, `SLACK_APP_TOKEN=${slackTokens.appToken}`);
|
|
19
|
+
}
|
|
20
|
+
fs.writeFileSync(path.join(projectDir, 'config', 'sentinel.properties'), sentinelProps);
|
|
21
|
+
fs.copySync(path.join(tplDir, 'log-configs', '_example.properties'), path.join(configDir, '_example.properties'));
|
|
22
|
+
fs.copySync(path.join(tplDir, 'repo-configs', '_example.properties'), path.join(repoDir, '_example.properties'));
|
|
23
|
+
generateProjectScripts(projectDir, codeDir, pythonBin);
|
|
24
|
+
}
|
|
25
|
+
function generateProjectScripts(projectDir, codeDir, pythonBin) {
|
|
26
|
+
const name = path.basename(projectDir);
|
|
27
|
+
fs.writeFileSync(path.join(projectDir, 'start.sh'), `#!/usr/bin/env bash
|
|
28
|
+
# Start this Sentinel instance
|
|
29
|
+
set -euo pipefail
|
|
30
|
+
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
31
|
+
PID_FILE="$DIR/sentinel.pid"
|
|
32
|
+
if [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
|
|
33
|
+
echo "[sentinel] ${name} already running (PID $(cat "$PID_FILE"))"
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
# Kill any orphaned sentinel processes for this project (stale PIDs not in PID file)
|
|
37
|
+
pkill -f "sentinel.main --config ${DIR}/config" 2>/dev/null || true
|
|
38
|
+
rm -f "$PID_FILE"
|
|
39
|
+
# Check Claude Code authentication
|
|
40
|
+
AUTH_OUT=$(claude --print \"hi\" 2>&1 || true)
|
|
41
|
+
if echo "$AUTH_OUT" | grep -Eqi "not logged in|/login"; then
|
|
42
|
+
echo ""
|
|
43
|
+
echo "[sentinel] Claude Code is not authenticated."
|
|
44
|
+
echo " 1. Open a new terminal and run: claude"
|
|
45
|
+
echo " 2. Type /login at the prompt"
|
|
46
|
+
echo " 3. Open the URL in any browser and log in"
|
|
47
|
+
echo " 4. Type /exit when done"
|
|
48
|
+
echo " 5. Re-run this script"
|
|
49
|
+
echo ""
|
|
50
|
+
exit 1
|
|
51
|
+
fi
|
|
52
|
+
mkdir -p "$DIR/logs" "$DIR/workspace/fetched" "$DIR/workspace/patches" "$DIR/issues"
|
|
53
|
+
cd "$DIR"
|
|
54
|
+
PYTHONPATH="${codeDir}" "${pythonBin}" -m sentinel.main --config ./config \\
|
|
55
|
+
>> "$DIR/logs/sentinel.log" 2>&1 &
|
|
56
|
+
echo $! > "$PID_FILE"
|
|
57
|
+
echo "[sentinel] ${name} started (PID $!)"
|
|
58
|
+
`, { mode: 0o755 });
|
|
59
|
+
fs.writeFileSync(path.join(projectDir, 'stop.sh'), `#!/usr/bin/env bash
|
|
60
|
+
# Stop this Sentinel instance
|
|
61
|
+
set -euo pipefail
|
|
62
|
+
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
63
|
+
PID_FILE="$DIR/sentinel.pid"
|
|
64
|
+
if [[ ! -f "$PID_FILE" ]]; then
|
|
65
|
+
echo "[sentinel] ${name} — no PID file, not running"
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
PID=$(cat "$PID_FILE")
|
|
69
|
+
if kill -0 "$PID" 2>/dev/null; then
|
|
70
|
+
kill "$PID"
|
|
71
|
+
echo "[sentinel] ${name} stopped (PID $PID)"
|
|
72
|
+
else
|
|
73
|
+
echo "[sentinel] ${name} — PID $PID not running"
|
|
74
|
+
fi
|
|
75
|
+
rm -f "$PID_FILE"
|
|
76
|
+
`, { mode: 0o755 });
|
|
77
|
+
}
|
|
78
|
+
function generateWorkspaceScripts(workspace, smtpConfig = {}, slackConfig = {}) {
|
|
79
|
+
const workspaceProps = path.join(workspace, 'sentinel.properties');
|
|
80
|
+
if (!fs.existsSync(workspaceProps)) {
|
|
81
|
+
const tplDir = path.join(__dirname, '..', 'templates');
|
|
82
|
+
let tpl = fs.readFileSync(path.join(tplDir, 'workspace-sentinel.properties'), 'utf8');
|
|
83
|
+
if (smtpConfig.host) tpl = tpl.replace('SMTP_HOST=smtp.gmail.com', 'SMTP_HOST=' + smtpConfig.host);
|
|
84
|
+
if (smtpConfig.port) tpl = tpl.replace('SMTP_PORT=587', 'SMTP_PORT=' + smtpConfig.port);
|
|
85
|
+
if (smtpConfig.user) tpl = tpl.replace('SMTP_USER=sentinel@yourdomain.com', 'SMTP_USER=' + smtpConfig.user);
|
|
86
|
+
if (smtpConfig.password) tpl = tpl.replace('SMTP_PASSWORD=<app-password>', 'SMTP_PASSWORD=' + smtpConfig.password);
|
|
87
|
+
fs.writeFileSync(workspaceProps, tpl);
|
|
88
|
+
}
|
|
89
|
+
if (slackConfig.botToken || slackConfig.appToken) {
|
|
90
|
+
let props = fs.readFileSync(workspaceProps, 'utf8');
|
|
91
|
+
if (slackConfig.botToken) {
|
|
92
|
+
const replaced = props.replace(/^#?\s*SLACK_BOT_TOKEN=.*/m, 'SLACK_BOT_TOKEN=' + slackConfig.botToken);
|
|
93
|
+
props = replaced !== props ? replaced : props.trimEnd() + '\nSLACK_BOT_TOKEN=' + slackConfig.botToken + '\n';
|
|
94
|
+
}
|
|
95
|
+
if (slackConfig.appToken) {
|
|
96
|
+
const replaced = props.replace(/^#?\s*SLACK_APP_TOKEN=.*/m, 'SLACK_APP_TOKEN=' + slackConfig.appToken);
|
|
97
|
+
props = replaced !== props ? replaced : props.trimEnd() + '\nSLACK_APP_TOKEN=' + slackConfig.appToken + '\n';
|
|
98
|
+
}
|
|
99
|
+
fs.writeFileSync(workspaceProps, props);
|
|
100
|
+
}
|
|
101
|
+
fs.writeFileSync(path.join(workspace, 'startAll.sh'), `#!/usr/bin/env bash
|
|
102
|
+
# Start all valid Sentinel project instances.
|
|
103
|
+
# A valid project must have config/repo-configs; do
|
|
104
|
+
name=$(basename "$project_dir")
|
|
105
|
+
[[ "$name" == "code" ]] && continue
|
|
106
|
+
# Auto-generate start.sh / stop.sh if missing (codeDir = $WORKSPACE/code)
|
|
107
|
+
if [[ ! -f "$project_dir/start.sh" ]]; then
|
|
108
|
+
code_dir="$WORKSPACE/code"
|
|
109
|
+
python_bin="$code_dir/.venv/bin/python3"
|
|
110
|
+
sed -e "s|__NAME__|$name|g" -e "s|__CODE_DIR__|$code_dir|g" -e "s|__PYTHON_BIN__|$python_bin|g" << 'STARTSH' > "$project_dir/start.sh"
|
|
111
|
+
#!/usr/bin/env bash
|
|
112
|
+
set -euo pipefail
|
|
113
|
+
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
114
|
+
PID_FILE="$DIR/sentinel.pid"
|
|
115
|
+
if [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
|
|
116
|
+
echo "[sentinel] __NAME__ already running (PID $(cat "$PID_FILE"))"
|
|
117
|
+
exit 0
|
|
118
|
+
fi
|
|
119
|
+
pkill -f "sentinel.main --config ${DIR}/config" 2>/dev/null || true
|
|
120
|
+
rm -f "$PID_FILE"
|
|
121
|
+
AUTH_OUT=$(claude --print \"hi\" 2>&1 || true)
|
|
122
|
+
if echo "$AUTH_OUT" | grep -Eqi "not logged in|/login"; then
|
|
123
|
+
echo "[sentinel] Claude Code is not authenticated. Run: claude then /login"
|
|
124
|
+
exit 1
|
|
125
|
+
fi
|
|
126
|
+
mkdir -p "$DIR/logs" "$DIR/workspace/fetched" "$DIR/workspace/patches" "$DIR/issues"
|
|
127
|
+
cd "$DIR"
|
|
128
|
+
PYTHONPATH="__CODE_DIR__" "__PYTHON_BIN__" -m sentinel.main --config ./config \
|
|
129
|
+
>> "$DIR/logs/sentinel.log" 2>&1 &
|
|
130
|
+
echo $! > "$PID_FILE"
|
|
131
|
+
echo "[sentinel] __NAME__ started (PID $!)"
|
|
132
|
+
STARTSH
|
|
133
|
+
chmod +x "$project_dir/start.sh"
|
|
134
|
+
echo "[sentinel] Auto-generated start.sh for $name"
|
|
135
|
+
fi
|
|
136
|
+
if [[ ! -f "$project_dir/stop.sh" ]]; then
|
|
137
|
+
sed -e "s|__NAME__|$name|g" << 'STOPSH' > "$project_dir/stop.sh"
|
|
138
|
+
#!/usr/bin/env bash
|
|
139
|
+
set -euo pipefail
|
|
140
|
+
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
141
|
+
PID_FILE="$DIR/sentinel.pid"
|
|
142
|
+
if [[ ! -f "$PID_FILE" ]]; then
|
|
143
|
+
echo "[sentinel] __NAME__ — no PID file, not running"
|
|
144
|
+
exit 0
|
|
145
|
+
fi
|
|
146
|
+
PID=$(cat "$PID_FILE")
|
|
147
|
+
if kill -0 "$PID" 2>/dev/null; then
|
|
148
|
+
kill "$PID"
|
|
149
|
+
echo "[sentinel] __NAME__ stopped (PID $PID)"
|
|
150
|
+
else
|
|
151
|
+
echo "[sentinel] __NAME__ — PID $PID not running"
|
|
152
|
+
fi
|
|
153
|
+
rm -f "$PID_FILE"
|
|
154
|
+
STOPSH
|
|
155
|
+
chmod +x "$project_dir/stop.sh"
|
|
156
|
+
echo "[sentinel] Auto-generated stop.sh for $name"
|
|
157
|
+
fi
|
|
158
|
+
# Must have at least one repo-config with a valid GitHub REPO_URL
|
|
159
|
+
repo_configs_dir="$project_dir/config/repo-configs"
|
|
160
|
+
if [[ ! -d "$repo_configs_dir" ]]; then
|
|
161
|
+
echo "[sentinel] Skipping $name — config/repo-configs/ directory not found"
|
|
162
|
+
skipped=$((skipped + 1))
|
|
163
|
+
continue
|
|
164
|
+
fi
|
|
165
|
+
has_config=false
|
|
166
|
+
valid_repo=false
|
|
167
|
+
for props in "$repo_configs_dir/"*.properties; do
|
|
168
|
+
[[ -f "$props" ]] || continue
|
|
169
|
+
[[ "$(basename "$props")" == _* ]] && continue
|
|
170
|
+
has_config=true
|
|
171
|
+
if grep -qE "^REPO_URL[[:space:]]*=[[:space:]]*(git@github\.com:|https://github\.com/)" "$props"; then
|
|
172
|
+
valid_repo=true
|
|
173
|
+
break
|
|
174
|
+
else
|
|
175
|
+
repo_url=$(grep -E "^REPO_URL[[:space:]]*=" "$props" | head -1 | cut -d= -f2- | xargs 2>/dev/null || true)
|
|
176
|
+
if [[ -z "$repo_url" ]]; then
|
|
177
|
+
echo "[sentinel] Skipping $name — REPO_URL not set in $(basename \"$props\")"
|
|
178
|
+
else
|
|
179
|
+
echo "[sentinel] Skipping $name — REPO_URL in $(basename \"$props\") is not a GitHub URL: $repo_url"
|
|
180
|
+
fi
|
|
181
|
+
fi
|
|
182
|
+
done
|
|
183
|
+
if [[ "$has_config" == "false" ]]; then
|
|
184
|
+
echo "[sentinel] Skipping $name — no .properties files in config/repo-configs/ (only _example?)"
|
|
185
|
+
skipped=$((skipped + 1))
|
|
186
|
+
continue
|
|
187
|
+
fi
|
|
188
|
+
if [[ "$valid_repo" == "false" ]]; then
|
|
189
|
+
skipped=$((skipped + 1))
|
|
190
|
+
continue
|
|
191
|
+
fi
|
|
192
|
+
bash "$project_dir/start.sh"
|
|
193
|
+
started=$((started + 1))
|
|
194
|
+
done
|
|
195
|
+
echo "[sentinel] $started project(s) started, $skipped skipped"
|
|
196
|
+
`, { mode: 0o755 });
|
|
197
|
+
// stopAll.sh
|
|
198
|
+
fs.writeFileSync(path.join(workspace, 'stopAll.sh'), `#!/usr/bin/env bash
|
|
199
|
+
# Stop all Sentinel project instances
|
|
200
|
+
WORKSPACE="$(cd "$(dirname "$0")" && pwd)"
|
|
201
|
+
stopped=0
|
|
202
|
+
for project_dir in "$WORKSPACE"/*/; do
|
|
203
|
+
name=$(basename "$project_dir")
|
|
204
|
+
[[ "$name" == "code" ]] && continue
|
|
205
|
+
[[ -f "$project_dir/stop.sh" ]] || continue
|
|
206
|
+
bash "$project_dir/stop.sh"
|
|
207
|
+
stopped=$((stopped + 1))
|
|
208
|
+
done
|
|
209
|
+
echo "[sentinel] $stopped project(s) stopped"
|
|
210
|
+
`, { mode: 0o755 });
|
|
211
|
+
}
|
|
212
|
+
module.exports = { writeExampleProject, generateProjectScripts, generateWorkspaceScripts };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const [,, command = 'help', ...args] = process.argv;
|
|
5
|
+
if (command === '--version' || command === '-v') {
|
|
6
|
+
const { version } = require('../package.json');
|
|
7
|
+
console.log(version);
|
|
8
|
+
process.exit(0);
|
|
9
|
+
}
|
|
10
|
+
if (command === '--help' || command === '-h') {
|
|
11
|
+
printUsage();
|
|
12
|
+
process.exit(0);
|
|
13
|
+
}
|
|
14
|
+
const BANNER = `
|
|
15
|
+
${chalk.cyan('███████╗███████╗███╗ ██╗████████╗██╗███╗ ██╗███████╗██╗')}
|
|
16
|
+
${chalk.cyan('██╔════╝██╔════╝████╗ ██║╚══██╔══╝██║████╗ ██║██╔════╝██║')}
|
|
17
|
+
${chalk.cyan('███████╗█████╗ ██╔██╗ ██║ ██║ ██║██╔██╗ ██║█████╗ ██║')}
|
|
18
|
+
${chalk.cyan('╚════██║██╔══╝ ██║╚██╗██║ ██║ ██║██║╚██╗██║██╔══╝ ██║')}
|
|
19
|
+
${chalk.cyan('███████║███████╗██║ ╚████║ ██║ ██║██║ ╚████║███████╗███████╗')}
|
|
20
|
+
${chalk.cyan('╚══════╝╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝')}
|
|
21
|
+
${chalk.gray(' Autonomous DevOps Agent')}
|
|
22
|
+
`;
|
|
23
|
+
async function main() {
|
|
24
|
+
console.log(BANNER);
|
|
25
|
+
switch (command) {
|
|
26
|
+
case 'init':
|
|
27
|
+
await require('../lib/init')();
|
|
28
|
+
break;
|
|
29
|
+
case 'add':
|
|
30
|
+
await require('../lib/add')(args[0]);
|
|
31
|
+
break;
|
|
32
|
+
case 'upgrade':
|
|
33
|
+
await require('../lib/upgrade')();
|
|
34
|
+
break;
|
|
35
|
+
case 'help':
|
|
36
|
+
default:
|
|
37
|
+
printUsage();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function printUsage() {
|
|
41
|
+
const { version } = require('../package.json');
|
|
42
|
+
console.log(`${chalk.bold('sentinel')} v${version} — Autonomous DevOps Agent
|
|
43
|
+
${chalk.bold('Usage:')}
|
|
44
|
+
sentinel init Interactive setup — install everything and create workspace
|
|
45
|
+
sentinel add <name> Add a blank project (fill config manually)
|
|
46
|
+
sentinel add <git-url> Add a project pre-configured for a GitHub repo
|
|
47
|
+
sentinel add <project.json> Add a project from a local JSON config file
|
|
48
|
+
sentinel add <https://host/cfg.json> Add a project from a remote JSON config URL
|
|
49
|
+
sentinel upgrade Pull latest version and hot-deploy Python source
|
|
50
|
+
${chalk.bold('Options:')}
|
|
51
|
+
--version, -v Print version
|
|
52
|
+
--help, -h Print this help
|
|
53
|
+
`);
|
|
54
|
+
}
|
|
55
|
+
main().catch(err => {
|
|
56
|
+
console.error(chalk.red('Error:'), err.message);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
});
|
package/lib/generate.js
CHANGED
|
@@ -46,7 +46,7 @@ if [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
|
|
|
46
46
|
fi
|
|
47
47
|
|
|
48
48
|
# Kill any orphaned sentinel processes for this project (stale PIDs not in PID file)
|
|
49
|
-
pkill -f "sentinel.main --config $
|
|
49
|
+
pkill -f "sentinel.main --config $DIR/config" 2>/dev/null || true
|
|
50
50
|
rm -f "$PID_FILE"
|
|
51
51
|
|
|
52
52
|
# Check Claude Code authentication
|
|
@@ -144,7 +144,7 @@ if [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
|
|
|
144
144
|
echo "[sentinel] __NAME__ already running (PID $(cat "$PID_FILE"))"
|
|
145
145
|
exit 0
|
|
146
146
|
fi
|
|
147
|
-
pkill -f "sentinel.main --config $
|
|
147
|
+
pkill -f "sentinel.main --config $DIR/config" 2>/dev/null || true
|
|
148
148
|
rm -f "$PID_FILE"
|
|
149
149
|
AUTH_OUT=$(claude --print \"hi\" 2>&1 || true)
|
|
150
150
|
if echo "$AUTH_OUT" | grep -Eqi "not logged in|/login"; then
|
package/package.json
CHANGED
|
@@ -986,6 +986,14 @@ async def _handle_with_cli(
|
|
|
986
986
|
capture_output=True, text=True, timeout=180, env=env,
|
|
987
987
|
)
|
|
988
988
|
output = (result.stdout or "").strip()
|
|
989
|
+
if result.returncode != 0 or not output:
|
|
990
|
+
stderr = (result.stderr or "").strip()
|
|
991
|
+
logger.error(
|
|
992
|
+
"Boss CLI call failed (rc=%d): stdout=%r stderr=%r",
|
|
993
|
+
result.returncode, output[:200], stderr[:200],
|
|
994
|
+
)
|
|
995
|
+
if result.returncode != 0 and not output:
|
|
996
|
+
return f":warning: `claude --print` failed (exit {result.returncode}): {(result.stderr or '').strip()[:300]}", True
|
|
989
997
|
except Exception as e:
|
|
990
998
|
logger.error("Boss CLI call failed: %s", e)
|
|
991
999
|
return f":warning: Boss unavailable: {e}", True
|
|
@@ -1004,7 +1012,8 @@ async def _handle_with_cli(
|
|
|
1004
1012
|
is_done = "[DONE]" in reply
|
|
1005
1013
|
reply = reply.replace("[DONE]", "").strip()
|
|
1006
1014
|
if not reply:
|
|
1007
|
-
|
|
1015
|
+
greeting = f"Hi {user_name}! " if user_name else "Hi! "
|
|
1016
|
+
reply = f"{greeting}I'm Sentinel, your autonomous DevOps agent. How can I help you?"
|
|
1008
1017
|
|
|
1009
1018
|
history.append({"role": "user", "content": message})
|
|
1010
1019
|
history.append({"role": "assistant", "content": reply})
|
|
@@ -1090,7 +1099,8 @@ async def handle_message(
|
|
|
1090
1099
|
is_done = "[DONE]" in reply
|
|
1091
1100
|
reply = reply.replace("[DONE]", "").strip()
|
|
1092
1101
|
if not reply:
|
|
1093
|
-
|
|
1102
|
+
greeting = f"Hi {user_name}! " if user_name else "Hi! "
|
|
1103
|
+
reply = f"{greeting}I'm Sentinel, your autonomous DevOps agent. How can I help you?"
|
|
1094
1104
|
history.append({"role": "assistant", "content": response.content})
|
|
1095
1105
|
return reply, is_done
|
|
1096
1106
|
|