@delorenj/pjangler 1.4.2 → 1.4.3
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/dist/index.js +3336 -1359
- package/dist/index.js.map +7 -0
- package/dist/mcp-server.js +1520 -838
- package/dist/mcp-server.js.map +7 -0
- package/dist/prompt.js +2 -1
- package/dist/prompt.js.map +7 -0
- package/package.json +8 -4
- package/templates/hermes-agent/copier.yml +16 -3
- package/templates/hermes-agent/template/.runtime-scaffold/memories/MEMORY.md +7 -4
- package/templates/hermes-agent/template/.scripts/10-hermes-profile.sh +88 -94
- package/templates/hermes-agent/template/.scripts/20-runtime-repo.sh +44 -21
- package/templates/hermes-agent/template/.scripts/30-telegram.sh +182 -171
- package/templates/hermes-agent/template/.scripts/31-slack.sh +260 -165
- package/templates/hermes-agent/template/.scripts/40-plane.sh +45 -36
- package/templates/hermes-agent/template/.scripts/42-ticket-provider.sh +210 -41
- package/templates/hermes-agent/template/.scripts/70-systemd.sh +129 -15
- package/templates/hermes-agent/template/.scripts/80-registry.sh +42 -6
- package/templates/hermes-agent/template/.scripts/99-summary.sh +69 -16
- package/templates/hermes-agent/template/.scripts/_lib.sh +773 -0
- package/templates/hermes-agent/template/.scripts/channel-transaction.py +2340 -0
- package/templates/hermes-agent/template/.scripts/config.example.toml +8 -2
- package/templates/hermes-agent/template/.scripts/credential-launch.sh +5 -1
- package/templates/hermes-agent/template/.scripts/heartbeat.sh +2 -3
- package/templates/hermes-agent/template/.scripts/lib/profile-config-lock.py +182 -0
- package/templates/hermes-agent/template/.scripts/lib/profile-config-seed.py +108 -0
- package/templates/hermes-agent/template/.scripts/lib/ticket-provider.sh +9 -0
- package/templates/hermes-agent/template/.scripts/lib/voice-config.py +546 -0
- package/templates/hermes-agent/template/.scripts/providers/linear.sh +24 -1
- package/templates/hermes-agent/template/.scripts/providers/plane.sh +54 -8
- package/templates/hermes-agent/template/.scripts/providers/trello.sh +25 -2
- package/templates/hermes-agent/template/.scripts/sentinel/bin/issue-autonomous-review.sh +5 -16
- package/templates/hermes-agent/template/.scripts/sentinel/bin/issue-close-gate.sh +2 -16
- package/templates/hermes-agent/template/.scripts/sentinel/docs/autonomous-delegated-review.md +13 -19
- package/templates/hermes-agent/template/.scripts/sentinel/docs/bloodbank-events.md +29 -36
- package/templates/hermes-agent/template/.scripts/sentinel.prompt.md.jinja +7 -8
- package/templates/hermes-agent/template/.scripts/store-onepassword-secret.py +260 -0
- package/templates/hermes-agent/template/SOUL.md.jinja +14 -16
- package/templates/hermes-agent/template/hermes.jinja +1 -1
- package/templates/hermes-agent/template/role.yaml.jinja +11 -4
|
@@ -6,6 +6,20 @@
|
|
|
6
6
|
# or be silently reused by a profile. The non-secret allow-list may be shared.
|
|
7
7
|
INVOCATION_TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN-}"
|
|
8
8
|
INVOCATION_TELEGRAM_ALLOWED_USERS="${TELEGRAM_ALLOWED_USERS-}"
|
|
9
|
+
# Invocation credentials may have arrived as exported variables. Remove them
|
|
10
|
+
# before even resolving/sourcing _lib.sh: that path invokes utilities and loads
|
|
11
|
+
# fleet state, and no child involved in setup should inherit a raw token.
|
|
12
|
+
unset TELEGRAM_BOT_TOKEN TELEGRAM_ALLOWED_USERS
|
|
13
|
+
|
|
14
|
+
# Every channel write lands in the host-global profile root that 10-hermes-profile.sh
|
|
15
|
+
# creates. When that step is deferred the root does not exist yet, so there is no
|
|
16
|
+
# honest Telegram state to record -- not even the disabled state, which is itself a
|
|
17
|
+
# write into the profile. Defer with it. This guard must precede _lib.sh because that
|
|
18
|
+
# library creates the role log and reads fleet configuration.
|
|
19
|
+
if [[ "${SKIP_HOST_STATE:-0}" == "1" ]]; then
|
|
20
|
+
printf '%s\n' '[30] telegram — DEFERRED (SKIP_HOST_STATE=1)' >&2
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
9
23
|
|
|
10
24
|
# shellcheck source=_lib.sh
|
|
11
25
|
source "$(dirname "$0")/_lib.sh"
|
|
@@ -19,60 +33,121 @@ else
|
|
|
19
33
|
fi
|
|
20
34
|
unset INVOCATION_TELEGRAM_BOT_TOKEN INVOCATION_TELEGRAM_ALLOWED_USERS
|
|
21
35
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
PROFILE_HOME="$HOME/.hermes/profiles/$PROFILE_NAME"
|
|
37
|
+
profile_root_require_real "$PROFILE_HOME"
|
|
38
|
+
RUNTIME="$ROLE_DIR/runtime"
|
|
39
|
+
ENVF="$RUNTIME/.env"
|
|
40
|
+
mkdir -p "$RUNTIME"
|
|
41
|
+
[[ ! -L "$ENVF" ]] || die "refusing to write Telegram credentials through symlink: $ENVF"
|
|
28
42
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
rf"(?m)^\s+{re.escape(key)}:\s*.*$", lambda _: replacement, body, count=1
|
|
40
|
-
)
|
|
41
|
-
if count == 0:
|
|
42
|
-
if body and not body.endswith("\n"):
|
|
43
|
-
body += "\n"
|
|
44
|
-
body += replacement + "\n"
|
|
45
|
-
path.write_text(text[: match.start("body")] + body + text[match.end("body") :], encoding="utf-8")
|
|
46
|
-
PYEOF
|
|
43
|
+
telegram_reconcile_existing() {
|
|
44
|
+
# Registry first, then the transaction helper's profile lock. The helper
|
|
45
|
+
# snapshots and validates refs plus role metadata only inside this window.
|
|
46
|
+
local rc=0
|
|
47
|
+
fleet_lock_acquire
|
|
48
|
+
trap 'fleet_lock_release' EXIT
|
|
49
|
+
channel_transaction_telegram_existing "$PROFILE_HOME" "$ENVF" || rc=$?
|
|
50
|
+
fleet_lock_release
|
|
51
|
+
trap - EXIT
|
|
52
|
+
return "$rc"
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
telegram_defer_if_unconfigured() {
|
|
56
|
+
# Prepare and, when already verified, reconcile under one registry-lock
|
|
57
|
+
# window. The helpers independently take the profile lock second.
|
|
58
|
+
local prepare_rc=0 rc=0
|
|
59
|
+
fleet_lock_acquire
|
|
60
|
+
trap 'fleet_lock_release' EXIT
|
|
61
|
+
channel_transaction_telegram_prepare_unconfigured \
|
|
62
|
+
"$PROFILE_HOME" "$ENVF" || prepare_rc=$?
|
|
63
|
+
case "$prepare_rc" in
|
|
64
|
+
0)
|
|
65
|
+
fleet_lock_release
|
|
66
|
+
trap - EXIT
|
|
67
|
+
return 2
|
|
68
|
+
;;
|
|
69
|
+
3)
|
|
70
|
+
channel_transaction_telegram_existing "$PROFILE_HOME" "$ENVF" || rc=$?
|
|
71
|
+
;;
|
|
72
|
+
*)
|
|
73
|
+
die "Telegram deferred-state preparation transaction failed"
|
|
74
|
+
;;
|
|
75
|
+
esac
|
|
76
|
+
case "$rc" in
|
|
77
|
+
0)
|
|
78
|
+
fleet_lock_release
|
|
79
|
+
trap - EXIT
|
|
80
|
+
return 0
|
|
81
|
+
;;
|
|
82
|
+
75)
|
|
83
|
+
die "Telegram 1Password reference validation is temporarily unavailable; preserved existing verified wiring for retry"
|
|
84
|
+
;;
|
|
85
|
+
*)
|
|
86
|
+
die "Telegram verified wiring reconciliation transaction failed"
|
|
87
|
+
;;
|
|
88
|
+
esac
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
telegram_prepare_for_attempt() {
|
|
92
|
+
# A new profile becomes explicitly disabled before remote verification. A
|
|
93
|
+
# verified profile is a no-op so a failed rotation preserves active wiring.
|
|
94
|
+
local rc=0
|
|
95
|
+
fleet_lock_acquire
|
|
96
|
+
trap 'fleet_lock_release' EXIT
|
|
97
|
+
channel_transaction_telegram_prepare_unconfigured \
|
|
98
|
+
"$PROFILE_HOME" "$ENVF" || rc=$?
|
|
99
|
+
fleet_lock_release
|
|
100
|
+
trap - EXIT
|
|
101
|
+
case "$rc" in
|
|
102
|
+
0|3) return 0 ;;
|
|
103
|
+
*) die "Telegram deferred-state preparation transaction failed" ;;
|
|
104
|
+
esac
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# A valid durable mapping survives a transient 1Password outage. If the caller
|
|
108
|
+
# supplied no rotation value, adopt/revalidate the complete current wiring
|
|
109
|
+
# under registry -> profile locks. No ref or identity snapshot escapes that
|
|
110
|
+
# window.
|
|
111
|
+
if [[ -z "${TELEGRAM_BOT_TOKEN:-}" ]]; then
|
|
112
|
+
telegram_reference_rc=0
|
|
113
|
+
telegram_reconcile_existing || telegram_reference_rc=$?
|
|
114
|
+
case "$telegram_reference_rc" in
|
|
115
|
+
0)
|
|
116
|
+
log "[30] telegram — existing verified 1Password wiring reconciled"
|
|
117
|
+
exit 0
|
|
118
|
+
;;
|
|
119
|
+
2) ;;
|
|
120
|
+
75)
|
|
121
|
+
die "Telegram 1Password reference validation is temporarily unavailable; preserved existing verified wiring for retry"
|
|
122
|
+
;;
|
|
123
|
+
*)
|
|
124
|
+
die "Telegram verified wiring reconciliation transaction failed"
|
|
125
|
+
;;
|
|
126
|
+
esac
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
# Establish explicit disabled state for a first-time credential attempt. This
|
|
130
|
+
# recheck is serialized with rotations: existing verified wiring is preserved,
|
|
131
|
+
# while an unconfigured profile is disabled before any fallible remote check.
|
|
132
|
+
if [[ -n "${TELEGRAM_BOT_TOKEN:-}" ]]; then
|
|
133
|
+
telegram_prepare_for_attempt
|
|
134
|
+
fi
|
|
50
135
|
|
|
51
136
|
if [[ "${SKIP_TELEGRAM:-0}" == "1" ]]; then
|
|
52
|
-
|
|
53
|
-
|
|
137
|
+
telegram_defer_rc=0
|
|
138
|
+
telegram_defer_if_unconfigured || telegram_defer_rc=$?
|
|
139
|
+
if [[ $telegram_defer_rc -eq 2 ]]; then
|
|
140
|
+
log "[30] telegram — DEFERRED (SKIP_TELEGRAM=1; no verified 1Password reference)"
|
|
54
141
|
else
|
|
55
|
-
|
|
56
|
-
clear_done 30-telegram
|
|
57
|
-
log "[30] telegram — DEFERRED (SKIP_TELEGRAM=1; completion marker cleared)"
|
|
142
|
+
log "[30] telegram — existing verified 1Password wiring reconciled"
|
|
58
143
|
fi
|
|
59
144
|
exit 0
|
|
60
145
|
fi
|
|
61
146
|
|
|
62
147
|
if already_done 30-telegram; then
|
|
63
|
-
|
|
64
|
-
log "[30] telegram already wired — skipping"
|
|
65
|
-
exit 0
|
|
66
|
-
fi
|
|
67
|
-
clear_done 30-telegram
|
|
68
|
-
log "[30] stale deferred completion marker cleared — reconciling Telegram"
|
|
148
|
+
log "[30] existing completion marker preserved while Telegram is reconciled"
|
|
69
149
|
fi
|
|
70
150
|
|
|
71
|
-
RUNTIME="$ROLE_DIR/runtime"
|
|
72
|
-
ENVF="$RUNTIME/.env"
|
|
73
|
-
mkdir -p "$RUNTIME"
|
|
74
|
-
[[ ! -L "$ENVF" ]] || die "refusing to write Telegram credentials through symlink: $ENVF"
|
|
75
|
-
|
|
76
151
|
cat >&2 <<EOF
|
|
77
152
|
|
|
78
153
|
╭─ BotFather steps for @$BOT_HANDLE ─────────────────────────────────────╮
|
|
@@ -93,19 +168,28 @@ if [[ -z "${TELEGRAM_BOT_TOKEN:-}" && -t 0 ]]; then
|
|
|
93
168
|
fi
|
|
94
169
|
|
|
95
170
|
if [[ -z "${TELEGRAM_BOT_TOKEN:-}" ]]; then
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
171
|
+
telegram_defer_rc=0
|
|
172
|
+
telegram_defer_if_unconfigured || telegram_defer_rc=$?
|
|
173
|
+
if [[ $telegram_defer_rc -eq 2 ]]; then
|
|
174
|
+
warn " no token provided; Telegram step deferred"
|
|
175
|
+
warn " re-run later with a profile-dedicated TELEGRAM_BOT_TOKEN invocation"
|
|
176
|
+
else
|
|
177
|
+
log "[30] telegram — existing verified 1Password wiring reconciled"
|
|
178
|
+
fi
|
|
100
179
|
exit 0
|
|
101
180
|
fi
|
|
102
181
|
|
|
103
182
|
# Sanity check
|
|
104
|
-
if [[ ! "$TELEGRAM_BOT_TOKEN" =~ ^[0-9]
|
|
183
|
+
if [[ ! "$TELEGRAM_BOT_TOKEN" =~ ^[0-9]+:[A-Za-z0-9_-]+$ ]]; then
|
|
105
184
|
die "token doesn't look like a Telegram bot token"
|
|
106
185
|
fi
|
|
107
186
|
log " verifying token..."
|
|
108
|
-
|
|
187
|
+
# Keep the credential out of process argv and environment. curl reads its
|
|
188
|
+
# complete request description from an anonymous stdin pipe.
|
|
189
|
+
info="$({
|
|
190
|
+
printf 'url = "https://api.telegram.org/bot%s/getMe"\n' "$TELEGRAM_BOT_TOKEN"
|
|
191
|
+
printf '%s\n' 'fail' 'silent' 'show-error'
|
|
192
|
+
} | curl --config -)" \
|
|
109
193
|
|| die "Telegram getMe request failed"
|
|
110
194
|
identity=$(printf '%s' "$info" | python3 -c '
|
|
111
195
|
import json, sys
|
|
@@ -138,17 +222,15 @@ fi
|
|
|
138
222
|
# Reject token reuse, token rotation onto an identity owned by another agent,
|
|
139
223
|
# and credentials parked in shared fleet/root env files. The scan, durable
|
|
140
224
|
# identity claim, and profile credential write share one fleet-wide flock.
|
|
141
|
-
export TELEGRAM_BOT_TOKEN TELEGRAM_ALLOWED_USERS
|
|
142
225
|
fleet_lock_acquire
|
|
143
226
|
trap 'fleet_lock_release' EXIT
|
|
144
|
-
python3
|
|
145
|
-
"$ROLE_DIR" "$PROFILE_NAME" "$bot_username"
|
|
146
|
-
|
|
227
|
+
python3 /dev/fd/3 "$REGISTRY_FILE" "$FLEET_ENV" "$ENVF" "$AGENT_ID" "$bot_id" \
|
|
228
|
+
"$ROLE_DIR" "$PROFILE_NAME" "$bot_username" \
|
|
229
|
+
3<<'PYEOF' <<<"$TELEGRAM_BOT_TOKEN"
|
|
147
230
|
import os
|
|
148
231
|
import pathlib
|
|
149
232
|
import re
|
|
150
233
|
import sys
|
|
151
|
-
import tempfile
|
|
152
234
|
try:
|
|
153
235
|
import yaml # type: ignore
|
|
154
236
|
except ImportError:
|
|
@@ -164,7 +246,14 @@ except ImportError:
|
|
|
164
246
|
profile_name,
|
|
165
247
|
bot_username,
|
|
166
248
|
) = sys.argv[1:]
|
|
167
|
-
token =
|
|
249
|
+
token = sys.stdin.read()
|
|
250
|
+
if token.endswith("\n"):
|
|
251
|
+
token = token[:-1]
|
|
252
|
+
if not token:
|
|
253
|
+
raise SystemExit("Telegram ownership scan received no credential")
|
|
254
|
+
for metadata_path in (pathlib.Path("/proc/self/cmdline"), pathlib.Path("/proc/self/environ")):
|
|
255
|
+
if metadata_path.is_file() and token.encode() in metadata_path.read_bytes():
|
|
256
|
+
raise SystemExit("Telegram ownership scan detected credential exposure in process metadata")
|
|
168
257
|
target = pathlib.Path(target_path).resolve(strict=False)
|
|
169
258
|
|
|
170
259
|
def env_token(path):
|
|
@@ -223,130 +312,52 @@ for owner, path in owners:
|
|
|
223
312
|
if env_token(path) == token:
|
|
224
313
|
raise SystemExit(f"Telegram bot token is already assigned to {owner}")
|
|
225
314
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
claim["role_dir"] = role_dir
|
|
230
|
-
claim["profile_name"] = profile_name
|
|
231
|
-
claim["telegram"] = {
|
|
232
|
-
"provisioning_status": "verified",
|
|
233
|
-
"bot_username": bot_username,
|
|
234
|
-
"bot_id": bot_id,
|
|
235
|
-
}
|
|
236
|
-
registry.parent.mkdir(parents=True, exist_ok=True)
|
|
237
|
-
rendered = yaml.safe_dump(data, sort_keys=False)
|
|
238
|
-
|
|
239
|
-
def fsync_parent(target):
|
|
240
|
-
unsupported = {errno.EINVAL, getattr(errno, "ENOTSUP", errno.EINVAL), errno.ENOSYS}
|
|
241
|
-
flags = os.O_RDONLY | getattr(os, "O_DIRECTORY", 0)
|
|
242
|
-
try:
|
|
243
|
-
directory_fd = os.open(target.parent, flags)
|
|
244
|
-
except OSError as exc:
|
|
245
|
-
if exc.errno in unsupported:
|
|
246
|
-
return
|
|
247
|
-
raise
|
|
248
|
-
try:
|
|
249
|
-
try:
|
|
250
|
-
os.fsync(directory_fd)
|
|
251
|
-
except OSError as exc:
|
|
252
|
-
if exc.errno not in unsupported:
|
|
253
|
-
raise
|
|
254
|
-
finally:
|
|
255
|
-
os.close(directory_fd)
|
|
256
|
-
|
|
257
|
-
fd, temporary = tempfile.mkstemp(prefix=f".{registry.name}.telegram-", dir=registry.parent)
|
|
258
|
-
try:
|
|
259
|
-
os.fchmod(fd, 0o600)
|
|
260
|
-
with os.fdopen(fd, "w", encoding="utf-8") as handle:
|
|
261
|
-
handle.write(rendered)
|
|
262
|
-
handle.flush()
|
|
263
|
-
os.fsync(handle.fileno())
|
|
264
|
-
os.replace(temporary, registry)
|
|
265
|
-
os.chmod(registry, 0o600)
|
|
266
|
-
fsync_parent(registry)
|
|
267
|
-
except BaseException:
|
|
268
|
-
try:
|
|
269
|
-
os.unlink(temporary)
|
|
270
|
-
except FileNotFoundError:
|
|
271
|
-
pass
|
|
272
|
-
raise
|
|
315
|
+
# This phase is intentionally read-only. The later channel transaction repeats
|
|
316
|
+
# identity-conflict validation under the same lock before committing registry
|
|
317
|
+
# metadata, so a failed vault stage cannot leave a false ownership claim.
|
|
273
318
|
PYEOF
|
|
274
319
|
|
|
275
|
-
#
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
for key in ("TELEGRAM_BOT_TOKEN", "TELEGRAM_ALLOWED_USERS"):
|
|
292
|
-
text = re.sub(rf"(?m)^\s*(?:export\s+)?#?\s*{key}\s*=.*(?:\n|$)", "", text)
|
|
293
|
-
text = text.rstrip("\n")
|
|
294
|
-
if text:
|
|
295
|
-
text += "\n"
|
|
296
|
-
text += "".join(
|
|
297
|
-
f"{key}={json.dumps(os.environ.get(key, ''))}\n"
|
|
298
|
-
for key in ("TELEGRAM_BOT_TOKEN", "TELEGRAM_ALLOWED_USERS")
|
|
299
|
-
)
|
|
320
|
+
# Stage the credential in a new immutable 1Password item. The helper verifies
|
|
321
|
+
# the staged field and returns the immutable item id plus reference; no local
|
|
322
|
+
# ownership/config state has changed yet.
|
|
323
|
+
ONEPASSWORD_ITEM_PREFIX="${HERMES_ONEPASSWORD_ITEM_PREFIX:-$(config_get fleet.onepassword_item_prefix 'hermes-agent')}"
|
|
324
|
+
telegram_stage="$(stage_onepassword_secret \
|
|
325
|
+
"${ONEPASSWORD_ITEM_PREFIX}-${AGENT_ID}-telegram-bot-token" \
|
|
326
|
+
telegram_bot_token "$TELEGRAM_BOT_TOKEN")" \
|
|
327
|
+
|| die "Telegram credential could not be staged and verified in 1Password"
|
|
328
|
+
if [[ "$telegram_stage" != *$'\n'* ]]; then
|
|
329
|
+
die "Telegram credential staging returned an invalid result"
|
|
330
|
+
fi
|
|
331
|
+
telegram_staged_item_id="${telegram_stage%%$'\n'*}"
|
|
332
|
+
telegram_reference="${telegram_stage#*$'\n'}"
|
|
333
|
+
[[ "$telegram_reference" != *$'\n'* ]] \
|
|
334
|
+
|| die "Telegram credential staging returned too many references"
|
|
335
|
+
unset TELEGRAM_BOT_TOKEN
|
|
300
336
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
finally:
|
|
317
|
-
os.close(directory_fd)
|
|
337
|
+
# From here until commit, any failure archives the staged item by immutable id
|
|
338
|
+
# and releases the fleet lock. The transaction itself keeps the platform
|
|
339
|
+
# disabled while committing refs, role metadata, runtime policy, and registry;
|
|
340
|
+
# activation and the done marker are its final writes.
|
|
341
|
+
telegram_transaction_exit() {
|
|
342
|
+
local rc=$?
|
|
343
|
+
trap - EXIT
|
|
344
|
+
if [[ -n "${telegram_staged_item_id:-}" ]]; then
|
|
345
|
+
delete_staged_onepassword_item "$telegram_staged_item_id" >/dev/null 2>&1 \
|
|
346
|
+
|| warn " staged Telegram item cleanup failed; archive manually by immutable item id"
|
|
347
|
+
fi
|
|
348
|
+
fleet_lock_release
|
|
349
|
+
exit "$rc"
|
|
350
|
+
}
|
|
351
|
+
trap telegram_transaction_exit EXIT
|
|
318
352
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
handle.write(text)
|
|
324
|
-
handle.flush()
|
|
325
|
-
os.fsync(handle.fileno())
|
|
326
|
-
os.replace(temporary, path)
|
|
327
|
-
os.chmod(path, 0o600)
|
|
328
|
-
fsync_parent(path)
|
|
329
|
-
except BaseException:
|
|
330
|
-
try:
|
|
331
|
-
os.unlink(temporary)
|
|
332
|
-
except FileNotFoundError:
|
|
333
|
-
pass
|
|
334
|
-
raise
|
|
335
|
-
PYEOF
|
|
336
|
-
unset TELEGRAM_BOT_TOKEN
|
|
353
|
+
channel_transaction_telegram \
|
|
354
|
+
"$PROFILE_HOME" "$ENVF" "$telegram_reference" \
|
|
355
|
+
"$bot_username" "$bot_id" "${TELEGRAM_ALLOWED_USERS:-}" \
|
|
356
|
+
|| die "Telegram local credential transaction failed"
|
|
337
357
|
|
|
338
|
-
|
|
339
|
-
telegram_yaml_update \
|
|
340
|
-
provisioning_status verified \
|
|
341
|
-
bot_username "$bot_username" \
|
|
342
|
-
bot_id "$bot_id"
|
|
358
|
+
telegram_staged_item_id=""
|
|
343
359
|
|
|
344
360
|
fleet_lock_release
|
|
345
361
|
trap - EXIT
|
|
346
362
|
|
|
347
|
-
|
|
348
|
-
env HERMES_HOME="$RUNTIME" "$HERMES_BIN" tools enable telegram hermes-telegram 2>/dev/null \
|
|
349
|
-
|| warn " couldn't auto-enable telegram toolset; run: $ROLE_DIR/hermes tools"
|
|
350
|
-
|
|
351
|
-
log " wired @$bot_username (id=$bot_id)"
|
|
352
|
-
mark_done 30-telegram
|
|
363
|
+
log " wired @$bot_username (id=$bot_id) through 1Password reference"
|