openclaw-opencode-bridge 2.0.9 → 2.1.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/package.json
CHANGED
package/plugin/index.ts
CHANGED
|
@@ -12,7 +12,8 @@ const SCRIPT_MAP: Record<string, string> = {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
const REQUIRES_ARG = new Set(["cc", "ccn"]);
|
|
15
|
-
const EXEC_TIMEOUT =
|
|
15
|
+
const EXEC_TIMEOUT = 15_000;
|
|
16
|
+
const SUPPRESSION_WINDOW = 15_000;
|
|
16
17
|
|
|
17
18
|
const DELIVERY_MSG = "🔗 OpenCode will reply shortly.";
|
|
18
19
|
|
|
@@ -57,15 +58,6 @@ export default function register(api: OpenClawPluginApi) {
|
|
|
57
58
|
const script = SCRIPT_MAP[command];
|
|
58
59
|
if (!script) return;
|
|
59
60
|
|
|
60
|
-
// Set flag for before_prompt_build to consume
|
|
61
|
-
pendingBridgeCommand = true;
|
|
62
|
-
// Also set suppression timer as safety net
|
|
63
|
-
bridgeSuppressUntil = Date.now() + EXEC_TIMEOUT + 5_000;
|
|
64
|
-
|
|
65
|
-
api.logger.debug?.(
|
|
66
|
-
`[opencode-bridge] message_received: command=${command}, pendingBridgeCommand=true`,
|
|
67
|
-
);
|
|
68
|
-
|
|
69
61
|
const arg = match[2].trim();
|
|
70
62
|
|
|
71
63
|
if (REQUIRES_ARG.has(command) && !arg) {
|
|
@@ -73,17 +65,32 @@ export default function register(api: OpenClawPluginApi) {
|
|
|
73
65
|
return;
|
|
74
66
|
}
|
|
75
67
|
|
|
68
|
+
// Set flag for before_prompt_build to consume
|
|
69
|
+
pendingBridgeCommand = true;
|
|
70
|
+
// Keep suppression narrow to this turn to avoid cross-message blocking.
|
|
71
|
+
bridgeSuppressUntil = Date.now() + SUPPRESSION_WINDOW;
|
|
72
|
+
|
|
73
|
+
api.logger.debug?.(
|
|
74
|
+
`[opencode-bridge] message_received: command=${command}, pendingBridgeCommand=true`,
|
|
75
|
+
);
|
|
76
|
+
|
|
76
77
|
const scriptPath = `${scriptsDir}/${script}`;
|
|
77
78
|
const args = arg ? [arg] : [];
|
|
79
|
+
const startedAt = Date.now();
|
|
78
80
|
|
|
79
81
|
execFile(
|
|
80
82
|
scriptPath,
|
|
81
83
|
args,
|
|
82
84
|
{ timeout: EXEC_TIMEOUT },
|
|
83
85
|
(error, _stdout, stderr) => {
|
|
86
|
+
const elapsedMs = Date.now() - startedAt;
|
|
84
87
|
if (error) {
|
|
85
88
|
api.logger.error?.(
|
|
86
|
-
`[opencode-bridge] ${script} failed: ${stderr?.trim() || error.message}`,
|
|
89
|
+
`[opencode-bridge] ${script} failed after ${elapsedMs}ms: ${stderr?.trim() || error.message}`,
|
|
90
|
+
);
|
|
91
|
+
} else {
|
|
92
|
+
api.logger.debug?.(
|
|
93
|
+
`[opencode-bridge] ${script} queued/completed in ${elapsedMs}ms`,
|
|
87
94
|
);
|
|
88
95
|
}
|
|
89
96
|
},
|
|
@@ -102,7 +109,7 @@ export default function register(api: OpenClawPluginApi) {
|
|
|
102
109
|
|
|
103
110
|
if (shouldSuppress) {
|
|
104
111
|
pendingBridgeCommand = false;
|
|
105
|
-
bridgeSuppressUntil = Date.now() +
|
|
112
|
+
bridgeSuppressUntil = Date.now() + SUPPRESSION_WINDOW;
|
|
106
113
|
return { systemPrompt: SILENT_PROMPT, prependContext: SILENT_PROMPT };
|
|
107
114
|
} else {
|
|
108
115
|
// Clear suppression for non-bridge messages
|
|
@@ -119,6 +126,8 @@ export default function register(api: OpenClawPluginApi) {
|
|
|
119
126
|
);
|
|
120
127
|
|
|
121
128
|
if (suppressing) {
|
|
129
|
+
// One-shot override for the intercepted bridge message.
|
|
130
|
+
bridgeSuppressUntil = 0;
|
|
122
131
|
return { content: DELIVERY_MSG, cancel: false };
|
|
123
132
|
}
|
|
124
133
|
});
|
|
@@ -1,22 +1,84 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# bridge-version:
|
|
3
|
-
# Start fresh session and send instruction
|
|
2
|
+
# bridge-version: 4
|
|
3
|
+
# Start fresh session asynchronously and send instruction
|
|
4
4
|
MSG="$1"
|
|
5
5
|
OPENCODE="{{OPENCODE_BIN}}"
|
|
6
6
|
CHANNEL="{{CHANNEL}}"
|
|
7
7
|
TARGET="{{TARGET_ID}}"
|
|
8
8
|
WORKSPACE="{{WORKSPACE}}"
|
|
9
|
+
RUN_TIMEOUT_SEC=45
|
|
10
|
+
LOG_FILE="/tmp/opencode-bridge-send.log"
|
|
9
11
|
|
|
10
12
|
if [ -z "$MSG" ]; then
|
|
11
13
|
echo "ERROR: No message provided"
|
|
12
14
|
exit 1
|
|
13
15
|
fi
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
trim_text() {
|
|
18
|
+
local value="$1"
|
|
19
|
+
value="${value#"${value%%[![:space:]]*}"}"
|
|
20
|
+
value="${value%"${value##*[![:space:]]}"}"
|
|
21
|
+
printf '%s' "$value"
|
|
22
|
+
}
|
|
17
23
|
|
|
18
|
-
|
|
24
|
+
normalize_text() {
|
|
25
|
+
printf '%s' "$1" \
|
|
26
|
+
| tr '[:upper:]' '[:lower:]' \
|
|
27
|
+
| sed -E 's/^🔗[[:space:]]*//; s/^["'\''`]+|["'\''`]+$//g; s/[[:space:]]+/ /g; s/^[[:space:]]+|[[:space:]]+$//g'
|
|
28
|
+
}
|
|
19
29
|
|
|
20
|
-
|
|
30
|
+
is_trivial_echo() {
|
|
31
|
+
local output_norm message_norm
|
|
32
|
+
output_norm="$(normalize_text "$1")"
|
|
33
|
+
message_norm="$(normalize_text "$2")"
|
|
34
|
+
[ -n "$message_norm" ] && [ "$output_norm" = "$message_norm" ]
|
|
35
|
+
}
|
|
21
36
|
|
|
22
|
-
|
|
37
|
+
run_with_timeout() {
|
|
38
|
+
local mode="$1"
|
|
39
|
+
local prompt="$2"
|
|
40
|
+
local output rc
|
|
41
|
+
|
|
42
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
43
|
+
output=$(timeout "${RUN_TIMEOUT_SEC}s" "$OPENCODE" run "$mode" "$prompt" 2>&1)
|
|
44
|
+
rc=$?
|
|
45
|
+
elif command -v gtimeout >/dev/null 2>&1; then
|
|
46
|
+
output=$(gtimeout "${RUN_TIMEOUT_SEC}s" "$OPENCODE" run "$mode" "$prompt" 2>&1)
|
|
47
|
+
rc=$?
|
|
48
|
+
else
|
|
49
|
+
output=$("$OPENCODE" run "$mode" "$prompt" 2>&1)
|
|
50
|
+
rc=$?
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
printf '%s\n' "$rc"
|
|
54
|
+
printf '%s' "$output"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
(
|
|
58
|
+
started_at=$(date +%s)
|
|
59
|
+
cd "$WORKSPACE" || exit 1
|
|
60
|
+
FULL_MSG="[${CHANNEL}:${TARGET}] $MSG"
|
|
61
|
+
|
|
62
|
+
run_result="$(run_with_timeout --fork "$FULL_MSG")"
|
|
63
|
+
rc="$(printf '%s' "$run_result" | head -n 1)"
|
|
64
|
+
output="$(printf '%s' "$run_result" | tail -n +2)"
|
|
65
|
+
|
|
66
|
+
output="$(printf '%s\n' "$output" | sed -E '/^[[:space:]]*openclaw message send --channel[[:space:]]+/d')"
|
|
67
|
+
output="$(trim_text "$output")"
|
|
68
|
+
|
|
69
|
+
if [ "$rc" -eq 124 ] || [ "$rc" -eq 137 ]; then
|
|
70
|
+
output="OpenCode timed out after ${RUN_TIMEOUT_SEC}s. Please retry with a narrower request."
|
|
71
|
+
elif [ -z "$output" ]; then
|
|
72
|
+
output="OpenCode finished, but returned an empty response."
|
|
73
|
+
elif is_trivial_echo "$output" "$MSG"; then
|
|
74
|
+
output="OpenCode ran, but returned a non-informative echo. Please retry with a more specific prompt."
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
openclaw message send --channel "$CHANNEL" --target "$TARGET" -m "$output"
|
|
78
|
+
|
|
79
|
+
ended_at=$(date +%s)
|
|
80
|
+
elapsed=$((ended_at - started_at))
|
|
81
|
+
printf '[%s] /ccn completed in %ss (exit=%s)\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$elapsed" "$rc"
|
|
82
|
+
) >>"$LOG_FILE" 2>&1 &
|
|
83
|
+
|
|
84
|
+
echo "✅ OpenCode new session queued."
|
|
@@ -1,22 +1,84 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# bridge-version:
|
|
3
|
-
#
|
|
2
|
+
# bridge-version: 4
|
|
3
|
+
# Dispatch instruction to OpenCode asynchronously and relay response
|
|
4
4
|
MSG="$1"
|
|
5
5
|
OPENCODE="{{OPENCODE_BIN}}"
|
|
6
6
|
CHANNEL="{{CHANNEL}}"
|
|
7
7
|
TARGET="{{TARGET_ID}}"
|
|
8
8
|
WORKSPACE="{{WORKSPACE}}"
|
|
9
|
+
RUN_TIMEOUT_SEC=45
|
|
10
|
+
LOG_FILE="/tmp/opencode-bridge-send.log"
|
|
9
11
|
|
|
10
12
|
if [ -z "$MSG" ]; then
|
|
11
13
|
echo "ERROR: No message provided"
|
|
12
14
|
exit 1
|
|
13
15
|
fi
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
trim_text() {
|
|
18
|
+
local value="$1"
|
|
19
|
+
value="${value#"${value%%[![:space:]]*}"}"
|
|
20
|
+
value="${value%"${value##*[![:space:]]}"}"
|
|
21
|
+
printf '%s' "$value"
|
|
22
|
+
}
|
|
17
23
|
|
|
18
|
-
|
|
24
|
+
normalize_text() {
|
|
25
|
+
printf '%s' "$1" \
|
|
26
|
+
| tr '[:upper:]' '[:lower:]' \
|
|
27
|
+
| sed -E 's/^🔗[[:space:]]*//; s/^["'\''`]+|["'\''`]+$//g; s/[[:space:]]+/ /g; s/^[[:space:]]+|[[:space:]]+$//g'
|
|
28
|
+
}
|
|
19
29
|
|
|
20
|
-
|
|
30
|
+
is_trivial_echo() {
|
|
31
|
+
local output_norm message_norm
|
|
32
|
+
output_norm="$(normalize_text "$1")"
|
|
33
|
+
message_norm="$(normalize_text "$2")"
|
|
34
|
+
[ -n "$message_norm" ] && [ "$output_norm" = "$message_norm" ]
|
|
35
|
+
}
|
|
21
36
|
|
|
22
|
-
|
|
37
|
+
run_with_timeout() {
|
|
38
|
+
local mode="$1"
|
|
39
|
+
local prompt="$2"
|
|
40
|
+
local output rc
|
|
41
|
+
|
|
42
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
43
|
+
output=$(timeout "${RUN_TIMEOUT_SEC}s" "$OPENCODE" run "$mode" "$prompt" 2>&1)
|
|
44
|
+
rc=$?
|
|
45
|
+
elif command -v gtimeout >/dev/null 2>&1; then
|
|
46
|
+
output=$(gtimeout "${RUN_TIMEOUT_SEC}s" "$OPENCODE" run "$mode" "$prompt" 2>&1)
|
|
47
|
+
rc=$?
|
|
48
|
+
else
|
|
49
|
+
output=$("$OPENCODE" run "$mode" "$prompt" 2>&1)
|
|
50
|
+
rc=$?
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
printf '%s\n' "$rc"
|
|
54
|
+
printf '%s' "$output"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
(
|
|
58
|
+
started_at=$(date +%s)
|
|
59
|
+
cd "$WORKSPACE" || exit 1
|
|
60
|
+
FULL_MSG="[${CHANNEL}:${TARGET}] $MSG"
|
|
61
|
+
|
|
62
|
+
run_result="$(run_with_timeout --continue "$FULL_MSG")"
|
|
63
|
+
rc="$(printf '%s' "$run_result" | head -n 1)"
|
|
64
|
+
output="$(printf '%s' "$run_result" | tail -n +2)"
|
|
65
|
+
|
|
66
|
+
output="$(printf '%s\n' "$output" | sed -E '/^[[:space:]]*openclaw message send --channel[[:space:]]+/d')"
|
|
67
|
+
output="$(trim_text "$output")"
|
|
68
|
+
|
|
69
|
+
if [ "$rc" -eq 124 ] || [ "$rc" -eq 137 ]; then
|
|
70
|
+
output="OpenCode timed out after ${RUN_TIMEOUT_SEC}s. Please retry with a narrower request."
|
|
71
|
+
elif [ -z "$output" ]; then
|
|
72
|
+
output="OpenCode finished, but returned an empty response."
|
|
73
|
+
elif is_trivial_echo "$output" "$MSG"; then
|
|
74
|
+
output="OpenCode ran, but returned a non-informative echo. Please retry with a more specific prompt."
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
openclaw message send --channel "$CHANNEL" --target "$TARGET" -m "$output"
|
|
78
|
+
|
|
79
|
+
ended_at=$(date +%s)
|
|
80
|
+
elapsed=$((ended_at - started_at))
|
|
81
|
+
printf '[%s] /cc completed in %ss (exit=%s)\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$elapsed" "$rc"
|
|
82
|
+
) >>"$LOG_FILE" 2>&1 &
|
|
83
|
+
|
|
84
|
+
echo "✅ OpenCode request queued."
|
|
@@ -17,18 +17,13 @@ The format is: `[CHANNEL:ID] actual message`
|
|
|
17
17
|
### Procedure
|
|
18
18
|
|
|
19
19
|
1. Extract `{CHANNEL}` and `{ID}` from the `[...]` prefix
|
|
20
|
-
2. Ignore everything before `] ` — that is
|
|
20
|
+
2. Ignore everything before `] ` — that is metadata, not user content
|
|
21
21
|
3. Perform the task described in the actual message
|
|
22
|
-
4.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- **Every message MUST receive a response.**
|
|
32
|
-
- Ignore the prefix, only respond to the actual message content.
|
|
33
|
-
- If the task failed, send the failure reason. If it succeeded, send the result.
|
|
34
|
-
- If you did not run `openclaw message send`, the task is **NOT** complete.
|
|
22
|
+
4. Return only the assistant answer text
|
|
23
|
+
|
|
24
|
+
### Output rules
|
|
25
|
+
|
|
26
|
+
- Do not output or suggest `openclaw message send` commands.
|
|
27
|
+
- Do not include the `[channel:id]` prefix in your response.
|
|
28
|
+
- Do not prepend delivery markers such as `🔗` unless explicitly requested by the user.
|
|
29
|
+
- If the task fails, return a direct failure reason in plain text.
|