u-foo 1.0.6 → 1.2.0
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 +247 -23
- package/SKILLS/ufoo/SKILL.md +17 -2
- package/SKILLS/uinit/SKILL.md +8 -3
- package/bin/ucode-core.js +15 -0
- package/bin/ucode.js +125 -0
- package/bin/ufoo-assistant-agent.js +5 -0
- package/bin/ufoo-engine.js +25 -0
- package/bin/ufoo.js +4 -0
- package/modules/AGENTS.template.md +14 -4
- package/modules/bus/README.md +8 -5
- package/modules/bus/SKILLS/ubus/SKILL.md +5 -4
- package/modules/context/SKILLS/uctx/SKILL.md +3 -1
- package/modules/online/SKILLS/ufoo-online/SKILL.md +144 -0
- package/package.json +12 -3
- package/scripts/import-pi-mono.js +124 -0
- package/scripts/postinstall.js +20 -49
- package/scripts/sync-claude-skills.sh +21 -0
- package/src/agent/cliRunner.js +524 -31
- package/src/agent/internalRunner.js +76 -9
- package/src/agent/launcher.js +97 -45
- package/src/agent/normalizeOutput.js +1 -1
- package/src/agent/notifier.js +144 -4
- package/src/agent/ptyRunner.js +480 -10
- package/src/agent/ptyWrapper.js +28 -3
- package/src/agent/readyDetector.js +16 -0
- package/src/agent/ucode.js +443 -0
- package/src/agent/ucodeBootstrap.js +113 -0
- package/src/agent/ucodeBuild.js +67 -0
- package/src/agent/ucodeDoctor.js +184 -0
- package/src/agent/ucodeRuntimeConfig.js +129 -0
- package/src/agent/ufooAgent.js +168 -28
- package/src/assistant/agent.js +260 -0
- package/src/assistant/bridge.js +172 -0
- package/src/assistant/engine.js +252 -0
- package/src/assistant/stdio.js +58 -0
- package/src/assistant/ufooEngineCli.js +306 -0
- package/src/bus/activate.js +27 -11
- package/src/bus/daemon.js +133 -5
- package/src/bus/index.js +137 -80
- package/src/bus/inject.js +47 -17
- package/src/bus/message.js +145 -17
- package/src/bus/nickname.js +3 -1
- package/src/bus/queue.js +6 -1
- package/src/bus/store.js +189 -0
- package/src/bus/subscriber.js +20 -4
- package/src/bus/utils.js +9 -3
- package/src/chat/agentBar.js +117 -0
- package/src/chat/agentDirectory.js +88 -0
- package/src/chat/agentSockets.js +225 -0
- package/src/chat/agentViewController.js +298 -0
- package/src/chat/chatLogController.js +115 -0
- package/src/chat/commandExecutor.js +700 -0
- package/src/chat/commands.js +132 -0
- package/src/chat/completionController.js +414 -0
- package/src/chat/cronScheduler.js +160 -0
- package/src/chat/daemonConnection.js +166 -0
- package/src/chat/daemonCoordinator.js +64 -0
- package/src/chat/daemonMessageRouter.js +257 -0
- package/src/chat/daemonReconnect.js +41 -0
- package/src/chat/daemonTransport.js +36 -0
- package/src/chat/daemonTransportDefaults.js +10 -0
- package/src/chat/dashboardKeyController.js +480 -0
- package/src/chat/dashboardView.js +157 -0
- package/src/chat/index.js +938 -2910
- package/src/chat/inputHistoryController.js +105 -0
- package/src/chat/inputListenerController.js +304 -0
- package/src/chat/inputMath.js +104 -0
- package/src/chat/inputSubmitHandler.js +171 -0
- package/src/chat/layout.js +165 -0
- package/src/chat/pasteController.js +81 -0
- package/src/chat/rawKeyMap.js +42 -0
- package/src/chat/settingsController.js +133 -0
- package/src/chat/statusLineController.js +177 -0
- package/src/chat/streamTracker.js +138 -0
- package/src/chat/text.js +70 -0
- package/src/chat/transport.js +61 -0
- package/src/cli/busCoreCommands.js +59 -0
- package/src/cli/ctxCoreCommands.js +199 -0
- package/src/cli/onlineCoreCommands.js +379 -0
- package/src/cli.js +741 -238
- package/src/code/README.md +29 -0
- package/src/code/UCODE_PROMPT.md +32 -0
- package/src/code/agent.js +1651 -0
- package/src/code/cli.js +158 -0
- package/src/code/config +0 -0
- package/src/code/dispatch.js +42 -0
- package/src/code/index.js +70 -0
- package/src/code/nativeRunner.js +1213 -0
- package/src/code/runtime.js +154 -0
- package/src/code/sessionStore.js +162 -0
- package/src/code/taskDecomposer.js +269 -0
- package/src/code/tools/bash.js +53 -0
- package/src/code/tools/common.js +42 -0
- package/src/code/tools/edit.js +70 -0
- package/src/code/tools/read.js +44 -0
- package/src/code/tools/write.js +35 -0
- package/src/code/tui.js +1587 -0
- package/src/config.js +50 -2
- package/src/context/decisions.js +12 -2
- package/src/context/index.js +18 -1
- package/src/context/sync.js +127 -0
- package/src/daemon/agentProcessManager.js +74 -0
- package/src/daemon/cronOps.js +241 -0
- package/src/daemon/index.js +662 -489
- package/src/daemon/ipcServer.js +99 -0
- package/src/daemon/ops.js +417 -179
- package/src/daemon/promptLoop.js +319 -0
- package/src/daemon/promptRequest.js +101 -0
- package/src/daemon/providerSessions.js +32 -17
- package/src/daemon/reporting.js +90 -0
- package/src/daemon/run.js +2 -5
- package/src/daemon/status.js +24 -1
- package/src/init/index.js +68 -14
- package/src/online/bridge.js +663 -0
- package/src/online/client.js +245 -0
- package/src/online/runner.js +253 -0
- package/src/online/server.js +992 -0
- package/src/online/tokens.js +103 -0
- package/src/report/store.js +331 -0
- package/src/shared/eventContract.js +35 -0
- package/src/shared/ptySocketContract.js +21 -0
- package/src/status/index.js +50 -17
- package/src/terminal/adapterContract.js +87 -0
- package/src/terminal/adapterRouter.js +84 -0
- package/src/terminal/adapters/externalAdapter.js +14 -0
- package/src/terminal/adapters/internalAdapter.js +13 -0
- package/src/terminal/adapters/internalPtyAdapter.js +42 -0
- package/src/terminal/adapters/internalQueueAdapter.js +37 -0
- package/src/terminal/adapters/terminalAdapter.js +31 -0
- package/src/terminal/adapters/tmuxAdapter.js +30 -0
- package/src/ufoo/agentsStore.js +69 -3
- package/src/utils/banner.js +5 -2
- package/scripts/.archived/bash-to-js-migration/README.md +0 -46
- package/scripts/.archived/bash-to-js-migration/banner.sh +0 -89
- package/scripts/.archived/bash-to-js-migration/bus-alert.sh +0 -6
- package/scripts/.archived/bash-to-js-migration/bus-autotrigger.sh +0 -6
- package/scripts/.archived/bash-to-js-migration/bus-daemon.sh +0 -231
- package/scripts/.archived/bash-to-js-migration/bus-inject.sh +0 -176
- package/scripts/.archived/bash-to-js-migration/bus-listen.sh +0 -6
- package/scripts/.archived/bash-to-js-migration/bus.sh +0 -986
- package/scripts/.archived/bash-to-js-migration/context-decisions.sh +0 -167
- package/scripts/.archived/bash-to-js-migration/context-doctor.sh +0 -72
- package/scripts/.archived/bash-to-js-migration/context-lint.sh +0 -110
- package/scripts/.archived/bash-to-js-migration/doctor.sh +0 -22
- package/scripts/.archived/bash-to-js-migration/init.sh +0 -247
- package/scripts/.archived/bash-to-js-migration/skills.sh +0 -113
- package/scripts/.archived/bash-to-js-migration/status.sh +0 -125
- package/scripts/banner.sh +0 -2
- package/src/bus/API_DESIGN.md +0 -204
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
# bus-daemon.sh
|
|
5
|
-
# Daemon that watches for new messages and injects /bus into target terminals.
|
|
6
|
-
#
|
|
7
|
-
# Usage:
|
|
8
|
-
# bash scripts/bus-daemon.sh [--interval <seconds>] [--daemon]
|
|
9
|
-
#
|
|
10
|
-
# This script monitors ALL subscribers' pending queues and injects /bus
|
|
11
|
-
# into their corresponding Terminal.app tabs when new messages arrive.
|
|
12
|
-
#
|
|
13
|
-
# Requirements:
|
|
14
|
-
# - macOS Accessibility permission for Terminal.app
|
|
15
|
-
# - Each subscriber's terminal should have title containing [bus:<instance-id>]
|
|
16
|
-
|
|
17
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
18
|
-
BUS_DIR=".ufoo/bus"
|
|
19
|
-
INTERVAL=2
|
|
20
|
-
DAEMON_MODE=0
|
|
21
|
-
PID_FILE="$BUS_DIR/.daemon.pid"
|
|
22
|
-
LOG_FILE="$BUS_DIR/logs/daemon.log"
|
|
23
|
-
|
|
24
|
-
usage() {
|
|
25
|
-
cat <<'USAGE'
|
|
26
|
-
bus-daemon.sh - Watch for new messages and inject /bus
|
|
27
|
-
|
|
28
|
-
Usage:
|
|
29
|
-
bash scripts/bus-daemon.sh [options]
|
|
30
|
-
|
|
31
|
-
Options:
|
|
32
|
-
--interval <n> Poll interval in seconds (default: 2)
|
|
33
|
-
--daemon Run in background
|
|
34
|
-
--stop Stop running daemon
|
|
35
|
-
--status Check daemon status
|
|
36
|
-
-h, --help Show this help
|
|
37
|
-
|
|
38
|
-
Notes:
|
|
39
|
-
- Requires macOS Accessibility permission
|
|
40
|
-
- Terminals must have title containing [bus:<instance-id>]
|
|
41
|
-
- Use `ufoo bus join` to set terminal title
|
|
42
|
-
USAGE
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
# Handle --help before other parsing
|
|
46
|
-
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
47
|
-
usage
|
|
48
|
-
exit 0
|
|
49
|
-
fi
|
|
50
|
-
|
|
51
|
-
while [[ $# -gt 0 ]]; do
|
|
52
|
-
case "$1" in
|
|
53
|
-
--interval)
|
|
54
|
-
INTERVAL="$2"
|
|
55
|
-
shift 2
|
|
56
|
-
;;
|
|
57
|
-
--daemon)
|
|
58
|
-
DAEMON_MODE=1
|
|
59
|
-
shift
|
|
60
|
-
;;
|
|
61
|
-
--stop)
|
|
62
|
-
if [[ -f "$PID_FILE" ]]; then
|
|
63
|
-
pid="$(cat "$PID_FILE" 2>/dev/null || true)"
|
|
64
|
-
if [[ -n "$pid" ]]; then
|
|
65
|
-
kill "$pid" 2>/dev/null && echo "[daemon] Stopped (pid=$pid)" || echo "[daemon] Not running"
|
|
66
|
-
fi
|
|
67
|
-
rm -f "$PID_FILE"
|
|
68
|
-
else
|
|
69
|
-
echo "[daemon] Not running"
|
|
70
|
-
fi
|
|
71
|
-
exit 0
|
|
72
|
-
;;
|
|
73
|
-
--status)
|
|
74
|
-
if [[ -f "$PID_FILE" ]]; then
|
|
75
|
-
pid="$(cat "$PID_FILE" 2>/dev/null || true)"
|
|
76
|
-
if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
|
|
77
|
-
echo "[daemon] Running (pid=$pid)"
|
|
78
|
-
else
|
|
79
|
-
echo "[daemon] Not running (stale pid file)"
|
|
80
|
-
rm -f "$PID_FILE"
|
|
81
|
-
fi
|
|
82
|
-
else
|
|
83
|
-
echo "[daemon] Not running"
|
|
84
|
-
fi
|
|
85
|
-
exit 0
|
|
86
|
-
;;
|
|
87
|
-
*)
|
|
88
|
-
echo "Unknown option: $1" >&2
|
|
89
|
-
usage >&2
|
|
90
|
-
exit 1
|
|
91
|
-
;;
|
|
92
|
-
esac
|
|
93
|
-
done
|
|
94
|
-
|
|
95
|
-
if [[ ! -d "$BUS_DIR" ]]; then
|
|
96
|
-
echo "[daemon] Error: $BUS_DIR not found. Run ufoo init first." >&2
|
|
97
|
-
exit 1
|
|
98
|
-
fi
|
|
99
|
-
|
|
100
|
-
mkdir -p "$BUS_DIR/logs"
|
|
101
|
-
|
|
102
|
-
if [[ "$DAEMON_MODE" == "1" ]]; then
|
|
103
|
-
# Check if already running
|
|
104
|
-
if [[ -f "$PID_FILE" ]]; then
|
|
105
|
-
existing="$(cat "$PID_FILE" 2>/dev/null || true)"
|
|
106
|
-
if [[ -n "$existing" ]] && kill -0 "$existing" 2>/dev/null; then
|
|
107
|
-
echo "[daemon] Already running (pid=$existing)"
|
|
108
|
-
exit 0
|
|
109
|
-
fi
|
|
110
|
-
fi
|
|
111
|
-
|
|
112
|
-
echo "[daemon] Starting in background (log: $LOG_FILE)"
|
|
113
|
-
nohup bash "$0" --interval "$INTERVAL" >> "$LOG_FILE" 2>&1 &
|
|
114
|
-
echo $! > "$PID_FILE"
|
|
115
|
-
echo "[daemon] Started (pid=$!)"
|
|
116
|
-
exit 0
|
|
117
|
-
fi
|
|
118
|
-
|
|
119
|
-
# Record PID
|
|
120
|
-
echo $$ > "$PID_FILE"
|
|
121
|
-
trap 'rm -f "$PID_FILE" 2>/dev/null || true; rm -rf "$COUNTS_DIR" 2>/dev/null || true' EXIT
|
|
122
|
-
|
|
123
|
-
echo "[daemon] Started (pid=$$, interval=${INTERVAL}s)"
|
|
124
|
-
echo "[daemon] Watching: $BUS_DIR/queues/*/pending.jsonl"
|
|
125
|
-
|
|
126
|
-
# Use temp directory to track last known counts (bash 3.x compatible)
|
|
127
|
-
COUNTS_DIR="$BUS_DIR/.daemon-counts.$$"
|
|
128
|
-
mkdir -p "$COUNTS_DIR"
|
|
129
|
-
|
|
130
|
-
get_last_count() {
|
|
131
|
-
local safe_name="$1"
|
|
132
|
-
local count_file="$COUNTS_DIR/$safe_name"
|
|
133
|
-
if [[ -f "$count_file" ]]; then
|
|
134
|
-
cat "$count_file"
|
|
135
|
-
else
|
|
136
|
-
echo "0"
|
|
137
|
-
fi
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
set_last_count() {
|
|
141
|
-
local safe_name="$1"
|
|
142
|
-
local count="$2"
|
|
143
|
-
echo "$count" > "$COUNTS_DIR/$safe_name"
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
# Cleanup dead agents (check PID and mark offline)
|
|
147
|
-
cleanup_dead_agents() {
|
|
148
|
-
if [[ ! -f "$BUS_DIR/bus.json" ]]; then
|
|
149
|
-
return
|
|
150
|
-
fi
|
|
151
|
-
|
|
152
|
-
local changed=0
|
|
153
|
-
local tmp_file
|
|
154
|
-
tmp_file=$(mktemp)
|
|
155
|
-
|
|
156
|
-
# Get all active subscribers with their PIDs
|
|
157
|
-
while IFS='|' read -r subscriber pid; do
|
|
158
|
-
[[ -z "$subscriber" || -z "$pid" ]] && continue
|
|
159
|
-
|
|
160
|
-
# Check if PID is still running AND is a claude/codex/node process
|
|
161
|
-
local proc_cmd
|
|
162
|
-
proc_cmd=$(ps -p "$pid" -o comm= 2>/dev/null || true)
|
|
163
|
-
if [[ -z "$proc_cmd" ]] || ! echo "$proc_cmd" | grep -qiE "(claude|codex|node)"; then
|
|
164
|
-
echo "[daemon] $(date '+%H:%M:%S') Agent $subscriber (pid=$pid) is dead, marking offline"
|
|
165
|
-
|
|
166
|
-
# Mark as offline in bus.json
|
|
167
|
-
jq --arg name "$subscriber" \
|
|
168
|
-
'.subscribers[$name].status = "offline"' \
|
|
169
|
-
"$BUS_DIR/bus.json" > "$tmp_file" && mv "$tmp_file" "$BUS_DIR/bus.json"
|
|
170
|
-
tmp_file=$(mktemp)
|
|
171
|
-
|
|
172
|
-
# Clean up queue directory
|
|
173
|
-
local safe_name="${subscriber//:/_}"
|
|
174
|
-
rm -rf "$BUS_DIR/queues/${safe_name}" 2>/dev/null || true
|
|
175
|
-
rm -f "$BUS_DIR/offsets/${safe_name}.offset" 2>/dev/null || true
|
|
176
|
-
|
|
177
|
-
changed=1
|
|
178
|
-
fi
|
|
179
|
-
done < <(jq -r '.subscribers | to_entries[] | select(.value.status == "active") | "\(.key)|\(.value.pid)"' "$BUS_DIR/bus.json" 2>/dev/null)
|
|
180
|
-
|
|
181
|
-
rm -f "$tmp_file" 2>/dev/null || true
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
CLEANUP_COUNTER=0
|
|
185
|
-
CLEANUP_INTERVAL=5 # Run cleanup every 5 cycles (10 seconds with default interval)
|
|
186
|
-
|
|
187
|
-
while true; do
|
|
188
|
-
# Periodic cleanup of dead agents
|
|
189
|
-
((CLEANUP_COUNTER++)) || true
|
|
190
|
-
if [[ $CLEANUP_COUNTER -ge $CLEANUP_INTERVAL ]]; then
|
|
191
|
-
cleanup_dead_agents
|
|
192
|
-
CLEANUP_COUNTER=0
|
|
193
|
-
fi
|
|
194
|
-
# Find all subscriber queue files
|
|
195
|
-
for queue_file in "$BUS_DIR/queues"/*/pending.jsonl; do
|
|
196
|
-
if [[ ! -f "$queue_file" ]]; then
|
|
197
|
-
continue
|
|
198
|
-
fi
|
|
199
|
-
|
|
200
|
-
# Extract subscriber from path: .ufoo/bus/queues/claude-code_abc123/pending.jsonl
|
|
201
|
-
dir_name="$(basename "$(dirname "$queue_file")")"
|
|
202
|
-
# Convert back to subscriber format: claude-code_abc123 -> claude-code:abc123
|
|
203
|
-
subscriber="${dir_name/_/:}"
|
|
204
|
-
|
|
205
|
-
# Get current count
|
|
206
|
-
if [[ -s "$queue_file" ]]; then
|
|
207
|
-
count="$(wc -l < "$queue_file" | tr -d ' ')"
|
|
208
|
-
else
|
|
209
|
-
count=0
|
|
210
|
-
fi
|
|
211
|
-
|
|
212
|
-
# Get last known count
|
|
213
|
-
last="$(get_last_count "$dir_name")"
|
|
214
|
-
|
|
215
|
-
# If count increased, inject /bus
|
|
216
|
-
if [[ "$count" -gt "$last" ]]; then
|
|
217
|
-
echo "[daemon] $(date '+%H:%M:%S') New message for $subscriber ($last -> $count)"
|
|
218
|
-
|
|
219
|
-
# Try to inject
|
|
220
|
-
if bash "$SCRIPT_DIR/bus-inject.sh" "$subscriber" 2>&1; then
|
|
221
|
-
echo "[daemon] Injected /bus into $subscriber"
|
|
222
|
-
else
|
|
223
|
-
echo "[daemon] Failed to inject (window not found or no permission)"
|
|
224
|
-
fi
|
|
225
|
-
fi
|
|
226
|
-
|
|
227
|
-
set_last_count "$dir_name" "$count"
|
|
228
|
-
done
|
|
229
|
-
|
|
230
|
-
sleep "$INTERVAL"
|
|
231
|
-
done
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
# bus-inject.sh
|
|
5
|
-
# Inject `/ubus` command into Terminal.app tab.
|
|
6
|
-
#
|
|
7
|
-
# Usage:
|
|
8
|
-
# bash scripts/bus-inject.sh <subscriber-id>
|
|
9
|
-
#
|
|
10
|
-
# Method:
|
|
11
|
-
# - Clipboard paste (Cmd+V) to bypass IME
|
|
12
|
-
# - Accessibility (System Events) for Escape + Paste + Enter
|
|
13
|
-
#
|
|
14
|
-
# Requirements:
|
|
15
|
-
# - macOS Accessibility permission for Terminal (first run will prompt)
|
|
16
|
-
# - Subscriber must have joined via `claude-bus` wrapper
|
|
17
|
-
|
|
18
|
-
BUS_DIR=".ufoo/bus"
|
|
19
|
-
SUBSCRIBER="${1:-}"
|
|
20
|
-
|
|
21
|
-
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
22
|
-
echo "Usage: bus-inject.sh <subscriber-id>"
|
|
23
|
-
echo ""
|
|
24
|
-
echo "Injects '/ubus' + Enter into Terminal.app tab by tty device."
|
|
25
|
-
echo "The subscriber must have joined from that terminal first."
|
|
26
|
-
exit 0
|
|
27
|
-
fi
|
|
28
|
-
|
|
29
|
-
if [[ -z "$SUBSCRIBER" ]]; then
|
|
30
|
-
echo "Error: subscriber ID required" >&2
|
|
31
|
-
echo "Usage: bus-inject.sh <subscriber-id>" >&2
|
|
32
|
-
exit 1
|
|
33
|
-
fi
|
|
34
|
-
|
|
35
|
-
# Convert subscriber to safe name
|
|
36
|
-
safe_name="${SUBSCRIBER//:/_}"
|
|
37
|
-
tty_file="$BUS_DIR/queues/${safe_name}/tty"
|
|
38
|
-
|
|
39
|
-
if [[ ! -f "$tty_file" ]]; then
|
|
40
|
-
echo "[inject] Error: No tty recorded for $SUBSCRIBER" >&2
|
|
41
|
-
echo "[inject] Make sure to run 'ufoo bus join' from the target terminal first" >&2
|
|
42
|
-
exit 1
|
|
43
|
-
fi
|
|
44
|
-
|
|
45
|
-
TARGET_TTY=$(cat "$tty_file")
|
|
46
|
-
echo "[inject] Looking for Terminal tab with tty: $TARGET_TTY"
|
|
47
|
-
|
|
48
|
-
# Determine command based on agent type
|
|
49
|
-
# codex uses "ubus" without slash, claude-code uses "/ubus"
|
|
50
|
-
if [[ "$SUBSCRIBER" == codex:* ]]; then
|
|
51
|
-
INJECT_CMD="ubus"
|
|
52
|
-
else
|
|
53
|
-
INJECT_CMD="/ubus"
|
|
54
|
-
fi
|
|
55
|
-
|
|
56
|
-
# Detect terminal type from tty path or environment
|
|
57
|
-
# Priority: 1) tmux (via stored pane ID), 2) iTerm2, 3) Terminal.app
|
|
58
|
-
|
|
59
|
-
# Try to get tmux pane info from bus.json (more reliable than tty lookup)
|
|
60
|
-
TMUX_PANE=""
|
|
61
|
-
if command -v jq &>/dev/null && [[ -f "$BUS_DIR/bus.json" ]]; then
|
|
62
|
-
TMUX_PANE=$(jq -r --arg id "$SUBSCRIBER" '.subscribers[$id].tmux_pane // ""' "$BUS_DIR/bus.json" 2>/dev/null || echo "")
|
|
63
|
-
fi
|
|
64
|
-
|
|
65
|
-
# Check if this is a tmux session
|
|
66
|
-
USE_TMUX=0
|
|
67
|
-
if [[ -n "$TMUX_PANE" ]] && command -v tmux &>/dev/null; then
|
|
68
|
-
# Verify the pane still exists
|
|
69
|
-
if tmux list-panes -a -F '#{pane_id}' 2>/dev/null | grep -q "^${TMUX_PANE}$"; then
|
|
70
|
-
USE_TMUX=1
|
|
71
|
-
else
|
|
72
|
-
echo "[inject] Warning: tmux pane $TMUX_PANE no longer exists" >&2
|
|
73
|
-
# Fallback: try to find pane by tty
|
|
74
|
-
TMUX_PANE=$(tmux list-panes -a -F '#{pane_id} #{pane_tty}' 2>/dev/null | grep " ${TARGET_TTY}$" | awk '{print $1}' | head -1)
|
|
75
|
-
if [[ -n "$TMUX_PANE" ]]; then
|
|
76
|
-
USE_TMUX=1
|
|
77
|
-
echo "[inject] Found alternative tmux pane by tty: $TMUX_PANE" >&2
|
|
78
|
-
fi
|
|
79
|
-
fi
|
|
80
|
-
fi
|
|
81
|
-
|
|
82
|
-
# Check if iTerm2 is running and has this tty
|
|
83
|
-
USE_ITERM=0
|
|
84
|
-
if [[ "$USE_TMUX" == "0" ]] && pgrep -q "iTerm2"; then
|
|
85
|
-
# Check if iTerm2 has a session with this tty
|
|
86
|
-
if osascript -e 'tell application "iTerm2" to get tty of current session of current window' 2>/dev/null | grep -q "$TARGET_TTY"; then
|
|
87
|
-
USE_ITERM=1
|
|
88
|
-
fi
|
|
89
|
-
fi
|
|
90
|
-
|
|
91
|
-
if [[ "$USE_TMUX" == "1" ]]; then
|
|
92
|
-
echo "[inject] Using tmux send-keys method for pane: $TMUX_PANE"
|
|
93
|
-
# Only send C-c if UFOO_INJECT_INTERRUPT is set (to avoid disrupting running agents)
|
|
94
|
-
if [[ "${UFOO_INJECT_INTERRUPT:-0}" == "1" ]]; then
|
|
95
|
-
echo "[inject] Sending interrupt (C-c) before command"
|
|
96
|
-
tmux send-keys -t "$TMUX_PANE" C-c 2>/dev/null || true
|
|
97
|
-
sleep 0.1
|
|
98
|
-
fi
|
|
99
|
-
tmux send-keys -t "$TMUX_PANE" "$INJECT_CMD" Enter
|
|
100
|
-
elif [[ "$USE_ITERM" == "1" ]]; then
|
|
101
|
-
echo "[inject] Using iTerm2 write text method"
|
|
102
|
-
osascript <<EOF
|
|
103
|
-
tell application "iTerm2"
|
|
104
|
-
activate
|
|
105
|
-
repeat with w in windows
|
|
106
|
-
repeat with t in tabs of w
|
|
107
|
-
repeat with s in sessions of t
|
|
108
|
-
try
|
|
109
|
-
if tty of s is "$TARGET_TTY" then
|
|
110
|
-
select s
|
|
111
|
-
-- Use write text which includes newline automatically
|
|
112
|
-
write text "$INJECT_CMD" to s
|
|
113
|
-
return
|
|
114
|
-
end if
|
|
115
|
-
end try
|
|
116
|
-
end repeat
|
|
117
|
-
end repeat
|
|
118
|
-
end repeat
|
|
119
|
-
error "No iTerm2 session found with tty: $TARGET_TTY"
|
|
120
|
-
end tell
|
|
121
|
-
EOF
|
|
122
|
-
else
|
|
123
|
-
echo "[inject] Using Terminal.app keystroke method (for interactive apps like Codex)"
|
|
124
|
-
osascript <<EOF
|
|
125
|
-
tell application "Terminal"
|
|
126
|
-
set targetWindow to missing value
|
|
127
|
-
set targetTab to missing value
|
|
128
|
-
|
|
129
|
-
repeat with w in windows
|
|
130
|
-
repeat with t in tabs of w
|
|
131
|
-
try
|
|
132
|
-
if tty of t is "$TARGET_TTY" then
|
|
133
|
-
set targetWindow to w
|
|
134
|
-
set targetTab to t
|
|
135
|
-
exit repeat
|
|
136
|
-
end if
|
|
137
|
-
end try
|
|
138
|
-
end repeat
|
|
139
|
-
if targetTab is not missing value then exit repeat
|
|
140
|
-
end repeat
|
|
141
|
-
|
|
142
|
-
if targetTab is missing value then
|
|
143
|
-
error "No Terminal tab found with tty: $TARGET_TTY"
|
|
144
|
-
end if
|
|
145
|
-
|
|
146
|
-
-- Activate and bring to front
|
|
147
|
-
activate
|
|
148
|
-
set selected tab of targetWindow to targetTab
|
|
149
|
-
set index of targetWindow to 1
|
|
150
|
-
end tell
|
|
151
|
-
|
|
152
|
-
-- Save current clipboard, set /ubus, paste, restore
|
|
153
|
-
set oldClipboard to the clipboard
|
|
154
|
-
|
|
155
|
-
set the clipboard to "$INJECT_CMD"
|
|
156
|
-
delay 0.1
|
|
157
|
-
|
|
158
|
-
tell application "System Events"
|
|
159
|
-
tell process "Terminal"
|
|
160
|
-
-- Escape to ensure input mode
|
|
161
|
-
key code 53
|
|
162
|
-
delay 0.1
|
|
163
|
-
-- Cmd+V to paste
|
|
164
|
-
keystroke "v" using command down
|
|
165
|
-
delay 0.2
|
|
166
|
-
-- Enter (Return key)
|
|
167
|
-
keystroke return
|
|
168
|
-
end tell
|
|
169
|
-
end tell
|
|
170
|
-
|
|
171
|
-
delay 0.2
|
|
172
|
-
set the clipboard to oldClipboard
|
|
173
|
-
EOF
|
|
174
|
-
fi
|
|
175
|
-
|
|
176
|
-
echo "[inject] Done"
|