@buda-ai/connector 0.1.0 → 0.1.2
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 +22 -1
- package/agent-templates/scripts/skill-scanner.sh +125 -0
- package/agent-templates/shared/AGENTS.md +220 -0
- package/agent-templates/shared/BOOTSTRAP.md +15 -0
- package/agent-templates/shared/HEARTBEAT.md +5 -0
- package/agent-templates/shared/IDENTITY.md +29 -0
- package/agent-templates/shared/MEMORY.md +19 -0
- package/agent-templates/shared/SOUL.md +43 -0
- package/agent-templates/shared/TOOLS.md +85 -0
- package/agent-templates/shared/USER.md +23 -0
- package/agent-templates/system-skills/app-builder/README.md +8 -0
- package/agent-templates/system-skills/app-builder/SKILL.md +678 -0
- package/agent-templates/system-skills/app-builder/buda.app.schema.json +165 -0
- package/agent-templates/system-skills/app-builder/evals/evals.json +23 -0
- package/agent-templates/system-skills/app-builder/scripts/check-preview-url.mjs +184 -0
- package/agent-templates/system-skills/buda-automations/SKILL.md +175 -0
- package/agent-templates/system-skills/buda-automations/scripts/create-automation.sh +226 -0
- package/agent-templates/system-skills/buda-automations/scripts/delete-automation.sh +73 -0
- package/agent-templates/system-skills/buda-automations/scripts/list-automations.sh +77 -0
- package/agent-templates/system-skills/buda-far/SKILL.md +221 -0
- package/agent-templates/system-skills/buda-far/far_gen.py +1499 -0
- package/agent-templates/system-skills/buda-machine-info/SKILL.md +67 -0
- package/agent-templates/system-skills/buda-machine-info/scripts/get-info.sh +25 -0
- package/agent-templates/system-skills/buda-machine-info/scripts/send-file.mjs +62 -0
- package/agent-templates/system-skills/buda-web-preview/SKILL.md +462 -0
- package/agent-templates/system-skills/find-skills/SKILL.md +142 -0
- package/agent-templates/system-skills/skill-creator/LICENSE.txt +202 -0
- package/agent-templates/system-skills/skill-creator/SKILL.md +479 -0
- package/agent-templates/system-skills/skill-creator/agents/analyzer.md +274 -0
- package/agent-templates/system-skills/skill-creator/agents/comparator.md +202 -0
- package/agent-templates/system-skills/skill-creator/agents/grader.md +223 -0
- package/agent-templates/system-skills/skill-creator/assets/eval_review.html +146 -0
- package/agent-templates/system-skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/agent-templates/system-skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/agent-templates/system-skills/skill-creator/references/schemas.md +430 -0
- package/agent-templates/system-skills/skill-creator/scripts/__init__.py +0 -0
- package/agent-templates/system-skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/agent-templates/system-skills/skill-creator/scripts/generate_report.py +326 -0
- package/agent-templates/system-skills/skill-creator/scripts/improve_description.py +248 -0
- package/agent-templates/system-skills/skill-creator/scripts/package_skill.py +136 -0
- package/agent-templates/system-skills/skill-creator/scripts/quick_validate.py +103 -0
- package/agent-templates/system-skills/skill-creator/scripts/run_eval.py +310 -0
- package/agent-templates/system-skills/skill-creator/scripts/run_loop.py +332 -0
- package/agent-templates/system-skills/skill-creator/scripts/utils.py +47 -0
- package/dist/cli.js +2747 -180
- package/package.json +14 -6
- package/src/bunny-daemon.ts +0 -52
- package/src/cli.ts +0 -97
- package/src/cloud-client.ts +0 -134
- package/src/config.ts +0 -122
- package/src/logger.ts +0 -36
- package/src/relay.ts +0 -96
- package/src/runtime.ts +0 -59
- package/src/types.ts +0 -37
- package/tsconfig.json +0 -44
- package/tsup.config.ts +0 -20
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Create a new scheduled automation in the current agent's workspace.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# bash create-automation.sh \
|
|
6
|
+
# --name "Task Name" \
|
|
7
|
+
# --prompt "What the agent should do" \
|
|
8
|
+
# --schedule-type interval \
|
|
9
|
+
# --interval-minutes 60
|
|
10
|
+
#
|
|
11
|
+
# bash create-automation.sh \
|
|
12
|
+
# --name "Task Name" \
|
|
13
|
+
# --prompt "What the agent should do" \
|
|
14
|
+
# --schedule-type cron \
|
|
15
|
+
# --cron "0 9 * * 1-5"
|
|
16
|
+
#
|
|
17
|
+
# bash create-automation.sh \
|
|
18
|
+
# --name "Task Name" \
|
|
19
|
+
# --prompt "What the agent should do" \
|
|
20
|
+
# --schedule-type once \
|
|
21
|
+
# --scheduled-at "2026-03-25T10:00:00.000Z"
|
|
22
|
+
#
|
|
23
|
+
# Options:
|
|
24
|
+
# --name Required. Automation display name.
|
|
25
|
+
# --prompt Required. The prompt the agent receives when triggered.
|
|
26
|
+
# --schedule-type Required. One of: interval, cron, once.
|
|
27
|
+
# --interval-minutes Required for interval type.
|
|
28
|
+
# --cron Required for cron type. Cron expression string.
|
|
29
|
+
# --scheduled-at Required for once type. ISO 8601 datetime.
|
|
30
|
+
# --max-runs Optional. Maximum number of executions.
|
|
31
|
+
# --disabled Optional flag. Create in disabled state.
|
|
32
|
+
# --delivery-mode Optional. One of: session, notify (default: session).
|
|
33
|
+
# --channel-id Optional. Target channel ID for notify mode.
|
|
34
|
+
# --timezone Optional. IANA timezone for cron schedules (e.g. Asia/Shanghai, America/New_York).
|
|
35
|
+
# The cron time is interpreted in this local timezone.
|
|
36
|
+
#
|
|
37
|
+
# Requires: AGENT_KEY, NEXT_PUBLIC_APP_URL (auto-injected in Buda sandbox)
|
|
38
|
+
|
|
39
|
+
set -euo pipefail
|
|
40
|
+
|
|
41
|
+
NAME=""
|
|
42
|
+
PROMPT=""
|
|
43
|
+
SCHEDULE_TYPE=""
|
|
44
|
+
INTERVAL_MINUTES=""
|
|
45
|
+
CRON_EXPR=""
|
|
46
|
+
SCHEDULED_AT=""
|
|
47
|
+
MAX_RUNS=""
|
|
48
|
+
IS_ENABLED="true"
|
|
49
|
+
DELIVERY_MODE="session"
|
|
50
|
+
CHANNEL_ID=""
|
|
51
|
+
TIMEZONE=""
|
|
52
|
+
POSITIONAL_ARGS=()
|
|
53
|
+
|
|
54
|
+
while [[ $# -gt 0 ]]; do
|
|
55
|
+
case "$1" in
|
|
56
|
+
--name) NAME="$2"; shift 2 ;;
|
|
57
|
+
--prompt) PROMPT="$2"; shift 2 ;;
|
|
58
|
+
--schedule-type) SCHEDULE_TYPE="$2"; shift 2 ;;
|
|
59
|
+
--interval-minutes) INTERVAL_MINUTES="$2"; shift 2 ;;
|
|
60
|
+
--cron) CRON_EXPR="$2"; shift 2 ;;
|
|
61
|
+
--scheduled-at) SCHEDULED_AT="$2"; shift 2 ;;
|
|
62
|
+
--max-runs) MAX_RUNS="$2"; shift 2 ;;
|
|
63
|
+
--delivery-mode) DELIVERY_MODE="$2"; shift 2 ;;
|
|
64
|
+
--channel-id) CHANNEL_ID="$2"; shift 2 ;;
|
|
65
|
+
--timezone) TIMEZONE="$2"; shift 2 ;;
|
|
66
|
+
--disabled) IS_ENABLED="false"; shift ;;
|
|
67
|
+
# Be tolerant: free-text arguments are treated as prompt fallback.
|
|
68
|
+
*) POSITIONAL_ARGS+=("$1"); shift ;;
|
|
69
|
+
esac
|
|
70
|
+
done
|
|
71
|
+
|
|
72
|
+
# Fallback for accidental natural-language invocation:
|
|
73
|
+
# bash create-automation.sh remind me tomorrow 9am
|
|
74
|
+
if [[ -z "${PROMPT}" && ${#POSITIONAL_ARGS[@]} -gt 0 ]]; then
|
|
75
|
+
PROMPT="${POSITIONAL_ARGS[*]}"
|
|
76
|
+
fi
|
|
77
|
+
if [[ -z "${NAME}" && -n "${PROMPT}" ]]; then
|
|
78
|
+
NAME="Automation $(date +%s)"
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# Validate required fields
|
|
82
|
+
if [[ -z "${NAME}" ]]; then
|
|
83
|
+
echo "ERROR: --name is required." >&2; exit 1
|
|
84
|
+
fi
|
|
85
|
+
if [[ -z "${PROMPT}" ]]; then
|
|
86
|
+
echo "ERROR: --prompt is required." >&2; exit 1
|
|
87
|
+
fi
|
|
88
|
+
if [[ -z "${SCHEDULE_TYPE}" ]]; then
|
|
89
|
+
echo "ERROR: --schedule-type is required (interval, cron, or once)." >&2
|
|
90
|
+
echo "Hint: pass schedule flags explicitly, e.g. --schedule-type cron --cron \"0 9 * * *\"" >&2
|
|
91
|
+
exit 1
|
|
92
|
+
fi
|
|
93
|
+
if [[ -z "${AGENT_KEY:-}" ]]; then
|
|
94
|
+
echo "ERROR: AGENT_KEY environment variable is not set." >&2; exit 1
|
|
95
|
+
fi
|
|
96
|
+
if [[ -z "${NEXT_PUBLIC_APP_URL:-}" ]]; then
|
|
97
|
+
echo "ERROR: NEXT_PUBLIC_APP_URL environment variable is not set." >&2; exit 1
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# Validate schedule-type-specific fields
|
|
101
|
+
case "${SCHEDULE_TYPE}" in
|
|
102
|
+
interval)
|
|
103
|
+
if [[ -z "${INTERVAL_MINUTES}" ]]; then
|
|
104
|
+
echo "ERROR: --interval-minutes is required for interval schedule type." >&2; exit 1
|
|
105
|
+
fi
|
|
106
|
+
;;
|
|
107
|
+
cron)
|
|
108
|
+
if [[ -z "${CRON_EXPR}" ]]; then
|
|
109
|
+
echo "ERROR: --cron is required for cron schedule type." >&2; exit 1
|
|
110
|
+
fi
|
|
111
|
+
if [[ -z "${TIMEZONE}" ]]; then
|
|
112
|
+
echo "ERROR: --timezone is required for cron schedule type." >&2
|
|
113
|
+
echo "Hint: pass the user's IANA timezone, e.g. --timezone \"Asia/Shanghai\"" >&2
|
|
114
|
+
exit 1
|
|
115
|
+
fi
|
|
116
|
+
;;
|
|
117
|
+
once)
|
|
118
|
+
if [[ -z "${SCHEDULED_AT}" ]]; then
|
|
119
|
+
echo "ERROR: --scheduled-at is required for once schedule type." >&2; exit 1
|
|
120
|
+
fi
|
|
121
|
+
;;
|
|
122
|
+
*)
|
|
123
|
+
echo "ERROR: Invalid schedule type '${SCHEDULE_TYPE}'. Must be interval, cron, or once." >&2
|
|
124
|
+
exit 1
|
|
125
|
+
;;
|
|
126
|
+
esac
|
|
127
|
+
|
|
128
|
+
# Validate delivery fields
|
|
129
|
+
if [[ "${DELIVERY_MODE}" != "session" && "${DELIVERY_MODE}" != "notify" ]]; then
|
|
130
|
+
echo "ERROR: --delivery-mode must be 'session' or 'notify'." >&2; exit 1
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# Channel-originated automation must deliver back to the same channel.
|
|
134
|
+
# BUDA_SOURCE_CHANNEL_ID is injected by Buda when chat originates from a channel.
|
|
135
|
+
# BUDA_SESSION_ID is the current session ID (maps to a specific group/DM via chatSessions).
|
|
136
|
+
if [[ -n "${BUDA_SOURCE_CHANNEL_ID:-}" ]]; then
|
|
137
|
+
DELIVERY_MODE="notify"
|
|
138
|
+
CHANNEL_ID="${BUDA_SOURCE_CHANNEL_ID}"
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
SESSION_ID="${BUDA_SESSION_ID:-}"
|
|
142
|
+
|
|
143
|
+
if [[ "${DELIVERY_MODE}" == "notify" && -z "${CHANNEL_ID}" ]]; then
|
|
144
|
+
echo "ERROR: --channel-id is required when --delivery-mode notify is used." >&2; exit 1
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# Build JSON payload using jq for proper escaping
|
|
148
|
+
JSON_PAYLOAD=$(jq -n \
|
|
149
|
+
--arg name "${NAME}" \
|
|
150
|
+
--arg prompt "${PROMPT}" \
|
|
151
|
+
--arg scheduleType "${SCHEDULE_TYPE}" \
|
|
152
|
+
--arg cronExpression "${CRON_EXPR}" \
|
|
153
|
+
--arg intervalMinutes "${INTERVAL_MINUTES}" \
|
|
154
|
+
--arg scheduledAt "${SCHEDULED_AT}" \
|
|
155
|
+
--arg maxRuns "${MAX_RUNS}" \
|
|
156
|
+
--arg deliveryMode "${DELIVERY_MODE}" \
|
|
157
|
+
--arg channelId "${CHANNEL_ID}" \
|
|
158
|
+
--arg sessionId "${SESSION_ID}" \
|
|
159
|
+
--arg timezone "${TIMEZONE}" \
|
|
160
|
+
--argjson isEnabled "${IS_ENABLED}" \
|
|
161
|
+
'{
|
|
162
|
+
json: {
|
|
163
|
+
name: $name,
|
|
164
|
+
taskTemplate: {
|
|
165
|
+
title: $name,
|
|
166
|
+
prompt: $prompt,
|
|
167
|
+
delivery: {
|
|
168
|
+
mode: $deliveryMode
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
scheduleType: $scheduleType,
|
|
172
|
+
isEnabled: $isEnabled
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
| if $deliveryMode == "notify" then .json.taskTemplate.delivery.channelId = $channelId else . end
|
|
176
|
+
| if $sessionId != "" then .json.taskTemplate.delivery.sessionId = $sessionId else . end
|
|
177
|
+
| if $scheduleType == "cron" then .json.cronExpression = $cronExpression else . end
|
|
178
|
+
| if $scheduleType == "interval" then .json.intervalMinutes = ($intervalMinutes | tonumber) else . end
|
|
179
|
+
| if $scheduleType == "once" then .json.scheduledAt = $scheduledAt else . end
|
|
180
|
+
| if $maxRuns != "" then .json.maxRuns = ($maxRuns | tonumber) else . end
|
|
181
|
+
| if $timezone != "" then .json.timezone = $timezone else . end
|
|
182
|
+
')
|
|
183
|
+
|
|
184
|
+
RESPONSE=$(curl -s \
|
|
185
|
+
"${NEXT_PUBLIC_APP_URL}/api/rpc/agent/automations/create" \
|
|
186
|
+
-X POST \
|
|
187
|
+
-H "Authorization: Bearer ${AGENT_KEY}" \
|
|
188
|
+
-H "Content-Type: application/json" \
|
|
189
|
+
-w '\n%{http_code}' \
|
|
190
|
+
-d "${JSON_PAYLOAD}" 2>/dev/null) || {
|
|
191
|
+
echo "ERROR: Failed to connect to API." >&2
|
|
192
|
+
exit 1
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
HTTP_CODE=$(echo "${RESPONSE}" | tail -1)
|
|
196
|
+
BODY=$(echo "${RESPONSE}" | sed '$d')
|
|
197
|
+
|
|
198
|
+
if [[ "${HTTP_CODE}" -ge 400 ]]; then
|
|
199
|
+
# Extract the human-readable error message from oRPC JSON error response
|
|
200
|
+
ERROR_MSG=$(echo "${BODY}" | jq -r '
|
|
201
|
+
(.json.message // "") as $msg |
|
|
202
|
+
(.json.code // "") as $code |
|
|
203
|
+
(if ($msg | type) == "string" and ($msg | startswith("[")) then
|
|
204
|
+
(try ($msg | fromjson | map((.path | join(".")) + ": " + .message) | join("; ")) catch $msg)
|
|
205
|
+
else
|
|
206
|
+
$msg
|
|
207
|
+
end) as $parsed_msg |
|
|
208
|
+
if $code != "" and $parsed_msg != "" then
|
|
209
|
+
"[\($code)] \($parsed_msg)"
|
|
210
|
+
elif $parsed_msg != "" then
|
|
211
|
+
$parsed_msg
|
|
212
|
+
else
|
|
213
|
+
empty
|
|
214
|
+
end
|
|
215
|
+
' 2>/dev/null) || ERROR_MSG=""
|
|
216
|
+
|
|
217
|
+
if [[ -n "${ERROR_MSG}" ]]; then
|
|
218
|
+
echo "ERROR: ${ERROR_MSG}" >&2
|
|
219
|
+
else
|
|
220
|
+
echo "ERROR: API call failed (HTTP ${HTTP_CODE})." >&2
|
|
221
|
+
echo "${BODY}" >&2
|
|
222
|
+
fi
|
|
223
|
+
exit 1
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
echo "${BODY}" | jq -r '.json'
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Delete a scheduled automation by ID.
|
|
3
|
+
#
|
|
4
|
+
# Usage: bash delete-automation.sh --id "sch_xxxxxxxxxxxxxxxx"
|
|
5
|
+
#
|
|
6
|
+
# Requires: AGENT_KEY, NEXT_PUBLIC_APP_URL (auto-injected in Buda sandbox)
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
AUTOMATION_ID=""
|
|
11
|
+
|
|
12
|
+
while [[ $# -gt 0 ]]; do
|
|
13
|
+
case "$1" in
|
|
14
|
+
--id) AUTOMATION_ID="$2"; shift 2 ;;
|
|
15
|
+
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
16
|
+
esac
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
if [[ -z "${AUTOMATION_ID}" ]]; then
|
|
20
|
+
echo "ERROR: --id is required." >&2; exit 1
|
|
21
|
+
fi
|
|
22
|
+
if [[ -z "${AGENT_KEY:-}" ]]; then
|
|
23
|
+
echo "ERROR: AGENT_KEY environment variable is not set." >&2; exit 1
|
|
24
|
+
fi
|
|
25
|
+
if [[ -z "${NEXT_PUBLIC_APP_URL:-}" ]]; then
|
|
26
|
+
echo "ERROR: NEXT_PUBLIC_APP_URL environment variable is not set." >&2; exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
PAYLOAD=$(jq -n --arg id "${AUTOMATION_ID}" '{ json: { id: $id } }')
|
|
30
|
+
|
|
31
|
+
RESPONSE=$(curl -s \
|
|
32
|
+
"${NEXT_PUBLIC_APP_URL}/api/rpc/agent/automations/delete" \
|
|
33
|
+
-X POST \
|
|
34
|
+
-H "Authorization: Bearer ${AGENT_KEY}" \
|
|
35
|
+
-H "Content-Type: application/json" \
|
|
36
|
+
-w '\n%{http_code}' \
|
|
37
|
+
-d "${PAYLOAD}" 2>/dev/null) || {
|
|
38
|
+
echo "ERROR: Failed to connect to API." >&2
|
|
39
|
+
exit 1
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
HTTP_CODE=$(echo "${RESPONSE}" | tail -1)
|
|
43
|
+
BODY=$(echo "${RESPONSE}" | sed '$d')
|
|
44
|
+
|
|
45
|
+
if [[ "${HTTP_CODE}" -ge 400 ]]; then
|
|
46
|
+
# Extract the human-readable error message from oRPC JSON error response
|
|
47
|
+
ERROR_MSG=$(echo "${BODY}" | jq -r '
|
|
48
|
+
(.json.message // "") as $msg |
|
|
49
|
+
(.json.code // "") as $code |
|
|
50
|
+
(if ($msg | type) == "string" and ($msg | startswith("[")) then
|
|
51
|
+
(try ($msg | fromjson | map((.path | join(".")) + ": " + .message) | join("; ")) catch $msg)
|
|
52
|
+
else
|
|
53
|
+
$msg
|
|
54
|
+
end) as $parsed_msg |
|
|
55
|
+
if $code != "" and $parsed_msg != "" then
|
|
56
|
+
"[\($code)] \($parsed_msg)"
|
|
57
|
+
elif $parsed_msg != "" then
|
|
58
|
+
$parsed_msg
|
|
59
|
+
else
|
|
60
|
+
empty
|
|
61
|
+
end
|
|
62
|
+
' 2>/dev/null) || ERROR_MSG=""
|
|
63
|
+
|
|
64
|
+
if [[ -n "${ERROR_MSG}" ]]; then
|
|
65
|
+
echo "ERROR: ${ERROR_MSG}" >&2
|
|
66
|
+
else
|
|
67
|
+
echo "ERROR: API call failed (HTTP ${HTTP_CODE})." >&2
|
|
68
|
+
echo "${BODY}" >&2
|
|
69
|
+
fi
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
echo "${BODY}" | jq -r '.json'
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# List all automations in the current agent's workspace.
|
|
3
|
+
# Usage: bash list-automations.sh [--limit N] [--offset N]
|
|
4
|
+
#
|
|
5
|
+
# Requires: AGENT_KEY, NEXT_PUBLIC_APP_URL (auto-injected in Buda sandbox)
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
LIMIT=20
|
|
10
|
+
OFFSET=0
|
|
11
|
+
|
|
12
|
+
while [[ $# -gt 0 ]]; do
|
|
13
|
+
case "$1" in
|
|
14
|
+
--limit) LIMIT="$2"; shift 2 ;;
|
|
15
|
+
--offset) OFFSET="$2"; shift 2 ;;
|
|
16
|
+
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
17
|
+
esac
|
|
18
|
+
done
|
|
19
|
+
|
|
20
|
+
if [[ -z "${AGENT_KEY:-}" ]]; then
|
|
21
|
+
echo "ERROR: AGENT_KEY environment variable is not set." >&2
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
if [[ -z "${NEXT_PUBLIC_APP_URL:-}" ]]; then
|
|
25
|
+
echo "ERROR: NEXT_PUBLIC_APP_URL environment variable is not set." >&2
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
PAYLOAD=$(cat <<EOF
|
|
30
|
+
{"json":{"limit":${LIMIT},"offset":${OFFSET}}}
|
|
31
|
+
EOF
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
RESPONSE=$(curl -s \
|
|
35
|
+
"${NEXT_PUBLIC_APP_URL}/api/rpc/agent/automations/list" \
|
|
36
|
+
-X POST \
|
|
37
|
+
-H "Authorization: Bearer ${AGENT_KEY}" \
|
|
38
|
+
-H "Content-Type: application/json" \
|
|
39
|
+
-w '\n%{http_code}' \
|
|
40
|
+
-d "${PAYLOAD}" 2>/dev/null) || {
|
|
41
|
+
echo "ERROR: Failed to connect to API." >&2
|
|
42
|
+
exit 1
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
HTTP_CODE=$(echo "${RESPONSE}" | tail -1)
|
|
46
|
+
BODY=$(echo "${RESPONSE}" | sed '$d')
|
|
47
|
+
|
|
48
|
+
if [[ "${HTTP_CODE}" -ge 400 ]]; then
|
|
49
|
+
# Extract the human-readable error message from oRPC JSON error response
|
|
50
|
+
ERROR_MSG=$(echo "${BODY}" | jq -r '
|
|
51
|
+
(.json.message // "") as $msg |
|
|
52
|
+
(.json.code // "") as $code |
|
|
53
|
+
(if ($msg | type) == "string" and ($msg | startswith("[")) then
|
|
54
|
+
(try ($msg | fromjson | map((.path | join(".")) + ": " + .message) | join("; ")) catch $msg)
|
|
55
|
+
else
|
|
56
|
+
$msg
|
|
57
|
+
end) as $parsed_msg |
|
|
58
|
+
if $code != "" and $parsed_msg != "" then
|
|
59
|
+
"[\($code)] \($parsed_msg)"
|
|
60
|
+
elif $parsed_msg != "" then
|
|
61
|
+
$parsed_msg
|
|
62
|
+
else
|
|
63
|
+
empty
|
|
64
|
+
end
|
|
65
|
+
' 2>/dev/null) || ERROR_MSG=""
|
|
66
|
+
|
|
67
|
+
if [[ -n "${ERROR_MSG}" ]]; then
|
|
68
|
+
echo "ERROR: ${ERROR_MSG}" >&2
|
|
69
|
+
else
|
|
70
|
+
echo "ERROR: API call failed (HTTP ${HTTP_CODE})." >&2
|
|
71
|
+
echo "${BODY}" >&2
|
|
72
|
+
fi
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
# Extract the result data
|
|
77
|
+
echo "${BODY}" | jq -r '.json'
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: far
|
|
3
|
+
description: File-Augmented Retrieval — generate persistent .meta sidecar files for PDFs, images, spreadsheets, videos and more, making every binary file readable to AI coding agents.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Bash, Glob
|
|
5
|
+
user-invocable: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# 📄 /far - File-Augmented Retrieval
|
|
9
|
+
|
|
10
|
+
> **"Making Every File Readable to Coding Agents via Persistent .meta Sidecars"**
|
|
11
|
+
|
|
12
|
+
FAR is a file augmentation protocol that generates persistent `.meta` sidecar files for binary documents (PDF, DOCX, XLSX, PPTX, Images, etc.). This allows AI coding agents (like OpenClaw, Cursor, GitHub Copilot) to "read" non-text files directly from the file system without requiring external RAG infrastructure.
|
|
13
|
+
|
|
14
|
+
**Current Version:** 1.0.0
|
|
15
|
+
**Author:** Kelly Peilin Chan
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 🚀 The Problem: AI "Blindness"
|
|
20
|
+
|
|
21
|
+
AI agents operating in a repository can read code (`.py`, `.js`, `.md`), but they are blind to 30-40% of critical context stored in binary formats:
|
|
22
|
+
- **Product Specs**: `requirements.docx`
|
|
23
|
+
- **Design Mocks**: `architecture.png`
|
|
24
|
+
- **Financial Data**: `budget.xlsx`
|
|
25
|
+
- **Contracts**: `agreement.pdf`
|
|
26
|
+
|
|
27
|
+
When an agent encounters `budget.xlsx`, it sees opaque bytes. It cannot reason about the content.
|
|
28
|
+
|
|
29
|
+
## 💡 The Solution: Persistent Sidecars
|
|
30
|
+
|
|
31
|
+
FAR solves this by generating a human-readable and machine-readable `.meta` file next to every binary file.
|
|
32
|
+
|
|
33
|
+
**Example:**
|
|
34
|
+
```
|
|
35
|
+
project/
|
|
36
|
+
├── budget.xlsx (Binary, opaque)
|
|
37
|
+
└── budget.xlsx.meta (Markdown, readable)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The agent simply reads `budget.xlsx.meta` to understand the spreadsheet. No vector database, no API calls, no runtime overhead.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## ✨ Features
|
|
45
|
+
|
|
46
|
+
### 1. Broad Format Support
|
|
47
|
+
FAR extracts text and structure from a wide range of formats:
|
|
48
|
+
* **📄 PDF** (`.pdf`): Full text extraction with layout preservation. **OCR fallback** for scanned PDFs. **Embedded image extraction** (pdfimages + OCR/Vision).
|
|
49
|
+
* **📝 Word** (`.docx`, `.doc`): Text extraction.
|
|
50
|
+
* **📊 Excel** (`.xlsx`): Sheet data converted to Markdown tables.
|
|
51
|
+
* **📽️ PowerPoint** (`.pptx`): Slide text extraction.
|
|
52
|
+
* **🖼️ Images** (`.png`, `.jpg`, `.jpeg`, `.gif`, `.bmp`, `.webp`): **OCR** (Tesseract) + **Apple Vision (on-device, macOS)** + **AI Vision** (GPT-4o) if configured.
|
|
53
|
+
* **🎬 Media** (`.mp4`, `.mov`, `.mp3`, `.wav`, `.m4a`, `.flac`): Technical metadata (FFprobe) + **Apple Vision video-frame analysis (macOS)** + **AI Transcription** through Buda's `whisper` tool when available.
|
|
54
|
+
* **📋 CSV** (`.csv`): Data rendered as Markdown tables (up to 100 rows).
|
|
55
|
+
* **📓 Jupyter Notebook** (`.ipynb`): Markdown cells, code cells, and outputs.
|
|
56
|
+
* **📚 EPUB** (`.epub`): Full text extracted from all chapters in spine order.
|
|
57
|
+
* **📦 Tar** (`.tar`, `.tar.gz`, `.tgz`, `.bz2`, `.xz`): File listing with sizes.
|
|
58
|
+
* **📧 Email** (`.eml`, `.msg`): Headers, body text, and attachment list.
|
|
59
|
+
* **📝 RTF** (`.rtf`): Plain text extraction via control word stripping.
|
|
60
|
+
* **🗄️ SQLite** (`.db`, `.sqlite`, `.sqlite3`): Table schemas + latest 20 rows per table as Markdown tables.
|
|
61
|
+
* **📊 Parquet** (`.parquet`): Schema and row count via `pyarrow` (optional). *[Metadata only]*
|
|
62
|
+
* **🎨 Design** (`.fig`, `.sketch`, `.xd`): File size and page/canvas count. *[Metadata only]*
|
|
63
|
+
* **💻 Code/Text** (`.py`, `.js`, `.ts`, `.go`, `.rs`, `.md`, `.json`, ...): Direct content mirroring.
|
|
64
|
+
|
|
65
|
+
### 2. Intelligent Caching (Incremental Build)
|
|
66
|
+
FAR is designed for speed. It uses a **two-layer caching mechanism**:
|
|
67
|
+
1. **Fast Check (mtime & size)**: If the file hasn't been modified, it skips processing instantly (0.003s).
|
|
68
|
+
2. **Content Check (SHA256)**: If mtime changed, it calculates the file hash. If the content is identical, it updates the timestamp but skips re-extraction.
|
|
69
|
+
|
|
70
|
+
For directory summaries, FAR also avoids noisy rewrites: if `.dir.meta` summary content is unchanged, it keeps the file untouched (no `extracted_at` churn).
|
|
71
|
+
|
|
72
|
+
### 3. Directory Summaries (`.dir.meta`)
|
|
73
|
+
FAR generates a `.dir.meta` file in every directory, providing a high-level summary of all files within. This allows agents to "browse" a folder and understand its contents without reading every single file.
|
|
74
|
+
|
|
75
|
+
### 4. Git LFS Support
|
|
76
|
+
FAR automatically handles Git LFS pointer files. It will attempt to pull the real content before processing, ensuring you don't get empty metadata for LFS-tracked files.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 🛠️ Usage
|
|
81
|
+
|
|
82
|
+
### Installation
|
|
83
|
+
The skill is pre-installed in the OpenClaw workspace:
|
|
84
|
+
```bash
|
|
85
|
+
~/.openclaw/workspace/.agents/skills/far/far_gen.py
|
|
86
|
+
```
|
|
87
|
+
A symlink is available as `far`:
|
|
88
|
+
```bash
|
|
89
|
+
far [directory_or_file]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Commands
|
|
93
|
+
|
|
94
|
+
**Scan current directory (Recursive):**
|
|
95
|
+
```bash
|
|
96
|
+
far
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Scan specific directory:**
|
|
100
|
+
```bash
|
|
101
|
+
far ~/Documents/projects/files
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Process single file:**
|
|
105
|
+
```bash
|
|
106
|
+
far report.pdf
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Force regeneration (Ignore cache):**
|
|
110
|
+
```bash
|
|
111
|
+
far . --force
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Configuration (AI Features)
|
|
115
|
+
In Buda sandboxes, audio/video transcription uses the Buda-native `whisper` tool through the auto-injected `AGENT_KEY` and `NEXT_PUBLIC_APP_URL` environment variables. No user OpenAI key is required inside the sandbox for transcription.
|
|
116
|
+
|
|
117
|
+
For standalone FAR usage outside Buda, or for AI Vision image description fallback, create a `.env` file in the skill directory or your home folder `~/.far.env`.
|
|
118
|
+
Copy `.env.example` as a template:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
OPENAI_API_KEY=sk-your-key-here
|
|
122
|
+
# Optional
|
|
123
|
+
OPENAI_BASE_URL=https://api.openai.com/v1
|
|
124
|
+
# Optional (macOS default: enabled)
|
|
125
|
+
FAR_USE_APPLE_VISION=1
|
|
126
|
+
# Optional (macOS default: enabled)
|
|
127
|
+
FAR_USE_MACOS_METADATA=1
|
|
128
|
+
# Optional tuning
|
|
129
|
+
FAR_APPLE_VISION_TIMEOUT=25
|
|
130
|
+
FAR_APPLE_VISION_MAX_FRAMES=6
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
If API keys are missing, FAR gracefully falls back to local tools (Tesseract, FFprobe),
|
|
134
|
+
and on macOS it also uses Apple Vision + Spotlight metadata on-device by default.
|
|
135
|
+
|
|
136
|
+
### Configuration (Ignore)
|
|
137
|
+
Create a `.farignore` file in your project root to exclude files or directories from scanning.
|
|
138
|
+
```gitignore
|
|
139
|
+
# .farignore
|
|
140
|
+
node_modules
|
|
141
|
+
.git
|
|
142
|
+
secrets/
|
|
143
|
+
*.tmp
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 🤖 Agent Integration
|
|
149
|
+
|
|
150
|
+
Add one rule to `AGENTS.md` or your system prompt:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
## File Reading with FAR
|
|
154
|
+
|
|
155
|
+
When navigating a directory, first read `.dir.meta` for a high-level overview
|
|
156
|
+
before opening individual files.
|
|
157
|
+
|
|
158
|
+
When you encounter a binary file you cannot read directly
|
|
159
|
+
(.png, .pdf, .xlsx, .mp4, .fig, .db, etc.), check for a .meta file
|
|
160
|
+
beside it — it contains the extracted content as Markdown.
|
|
161
|
+
|
|
162
|
+
Navigation strategy:
|
|
163
|
+
1. Read <dir>/.dir.meta to understand directory structure
|
|
164
|
+
2. Read <file>.meta to access binary file content
|
|
165
|
+
3. Fall back to reading the source file only if no .meta exists
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 🏗️ Protocol Specification (v1)
|
|
171
|
+
|
|
172
|
+
Each `.meta` file follows a strict format with a YAML frontmatter header and Markdown body.
|
|
173
|
+
|
|
174
|
+
```markdown
|
|
175
|
+
--far_version: 1
|
|
176
|
+
source:
|
|
177
|
+
sha256: 5a1cc2b8d...
|
|
178
|
+
mime: application/pdf
|
|
179
|
+
size: 129509
|
|
180
|
+
mtime: 1708845210.5
|
|
181
|
+
extract:
|
|
182
|
+
pipeline: far_gen_v14
|
|
183
|
+
extracted_at: 2026-02-27T10:00:00Z
|
|
184
|
+
deterministic: true
|
|
185
|
+
layout:
|
|
186
|
+
pages: 24
|
|
187
|
+
---
|
|
188
|
+
# filename.pdf
|
|
189
|
+
|
|
190
|
+
[Extracted Content Here...]
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Cache invalidation: a `.meta` is stale if `sha256` or `pipeline` version has changed.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 🤝 Contributing
|
|
198
|
+
|
|
199
|
+
**Roadmap:**
|
|
200
|
+
- [x] PDF/Word/Excel/PowerPoint Support
|
|
201
|
+
- [x] Incremental Caching (SHA-256 + mtime)
|
|
202
|
+
- [x] Directory Summaries (`.dir.meta`)
|
|
203
|
+
- [x] OCR for Images/Scanned PDFs (Tesseract)
|
|
204
|
+
- [x] Media Metadata (FFprobe) + Transcription (Whisper)
|
|
205
|
+
- [x] AI Vision for Images (GPT-4o)
|
|
206
|
+
- [x] Apple Vision for Images (on-device, macOS)
|
|
207
|
+
- [x] Apple Vision for Video Frames (on-device labels/OCR/faces/barcodes/pose)
|
|
208
|
+
- [x] Apple Vision Feature Print fingerprint (on-device embedding hash)
|
|
209
|
+
- [x] macOS Spotlight metadata enrichment (mdls)
|
|
210
|
+
- [x] PDF Embedded Image Extraction
|
|
211
|
+
- [x] CSV → Markdown Tables
|
|
212
|
+
- [x] Jupyter Notebook Support
|
|
213
|
+
- [x] EPUB, ZIP/TAR, Email, RTF
|
|
214
|
+
- [x] SQLite Schema + Data Preview
|
|
215
|
+
- [x] Parquet Schema, Design File Metadata
|
|
216
|
+
- [x] Pipeline version stale detection
|
|
217
|
+
- [x] Layout frontmatter (pages/sheets/slides)
|
|
218
|
+
- [x] Full glob support in `.farignore`
|
|
219
|
+
- [ ] MCP resource server integration
|
|
220
|
+
- [ ] PII redaction rules
|
|
221
|
+
- [ ] Rust implementation for CI/CD speed
|