@larktask/aamp-feishu-task-agent 0.1.0-dev.174 → 0.1.1-dev.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 +114 -18
- package/bin/agent-metadata.mjs +54 -0
- package/bin/feishu-task-agent-controller.mjs +1903 -340
- package/bin/runtime-concurrency.mjs +71 -0
- package/bin/runtime-network.mjs +13 -0
- package/bin/runtime-package-executable.mjs +254 -0
- package/bin/traecode-readiness.mjs +173 -0
- package/bootstrap/aamp-feishu-task-agent-bootstrap.sh +1426 -47
- package/package.json +1 -1
|
@@ -7,6 +7,9 @@ APP_ID=""
|
|
|
7
7
|
APP_SECRET=""
|
|
8
8
|
BOT_NAME=""
|
|
9
9
|
LARK_CLI_PROFILE=""
|
|
10
|
+
AGENT_EXECUTION_LOCATION=""
|
|
11
|
+
AGENT_ATTACHMENT_POLICY=""
|
|
12
|
+
AGENT_TASK_DISPATCH_CONCURRENCY=""
|
|
10
13
|
AAMP_HOST="https://meshmail.ai"
|
|
11
14
|
DEBUG_MODE="false"
|
|
12
15
|
AAMP_TASK_START_MODE="install"
|
|
@@ -15,6 +18,7 @@ AAMP_TASK_ENTRY="${AAMP_TASK_ENTRY:-}"
|
|
|
15
18
|
AAMP_TASK_INTERNAL="${AAMP_TASK_INTERNAL:-false}"
|
|
16
19
|
AAMP_TASK_INTERNAL_RESULT_FD="${AAMP_TASK_INTERNAL_RESULT_FD:-3}"
|
|
17
20
|
AAMP_TASK_INTERNAL_INPUT_FD="${AAMP_TASK_INTERNAL_INPUT_FD:-4}"
|
|
21
|
+
AAMP_TASK_INTERNAL_EXECUTION_LOCATION="${AAMP_TASK_INTERNAL_EXECUTION_LOCATION:-}"
|
|
18
22
|
NPM_REGISTRY="${NPM_REGISTRY:-https://registry.npmjs.org/}"
|
|
19
23
|
NPM_CACHE_DIR="${NPM_CONFIG_CACHE:-${npm_config_cache:-${TMPDIR:-/tmp}/aamp-one-click-npm-cache}}"
|
|
20
24
|
NPM_GLOBAL_PREFIX="${NPM_GLOBAL_PREFIX:-$HOME/.aamp/npm-global}"
|
|
@@ -51,22 +55,63 @@ CODEX_CHATGPT_APP_CLI="/Applications/ChatGPT.app/Contents/Resources/codex"
|
|
|
51
55
|
CODEX_APP_CLI="/Applications/Codex.app/Contents/Resources/codex"
|
|
52
56
|
CODEX_AUTO_UPDATE="${CODEX_AUTO_UPDATE:-true}"
|
|
53
57
|
CODEX_NPM_PACKAGE="${CODEX_NPM_PACKAGE:-@openai/codex}"
|
|
58
|
+
CODEX_UPDATE_CACHE_FILE="${CODEX_UPDATE_CACHE_FILE:-$HOME/.aamp/feishu-task-agent/codex-update-cache.json}"
|
|
59
|
+
CODEX_UPDATE_CACHE_TTL_SECONDS="${CODEX_UPDATE_CACHE_TTL_SECONDS:-86400}"
|
|
54
60
|
CODEX_ACP_PKG="${CODEX_ACP_PKG:-@agentclientprotocol/codex-acp@1.0.2}"
|
|
61
|
+
AAMP_TRAE_CLI_BIN="${AAMP_TRAE_CLI_BIN:-}"
|
|
62
|
+
AAMP_TRAE_LOGIN_STATUS_TIMEOUT_SECONDS="${AAMP_TRAE_LOGIN_STATUS_TIMEOUT_SECONDS:-10}"
|
|
63
|
+
AAMP_TRAECODE_CLI_BIN="${AAMP_TRAECODE_CLI_BIN:-}"
|
|
64
|
+
AAMP_TRAECODE_CHECK_TIMEOUT_SECONDS="${AAMP_TRAECODE_CHECK_TIMEOUT_SECONDS:-10}"
|
|
65
|
+
AAMP_TRAECODE_READINESS_HELPER="${AAMP_TRAECODE_READINESS_HELPER:-}"
|
|
66
|
+
AAMP_TRAECODE_UPDATE_TTY="${AAMP_TRAECODE_UPDATE_TTY:-/dev/tty}"
|
|
67
|
+
TRAEX_INSTALLER_URL="${TRAEX_INSTALLER_URL:-https://code.byted.org/api/tos-proxy/download/traex_install.sh}"
|
|
68
|
+
WORKBUDDY_APP_CLI="/Applications/WorkBuddy.app/Contents/Resources/app.asar.unpacked/cli/bin/codebuddy"
|
|
69
|
+
WORKBUDDY_AI_APP_CLI="/Applications/WorkBuddy AI.app/Contents/Resources/app.asar.unpacked/cli/bin/codebuddy"
|
|
55
70
|
LARK_REGISTER_APP_SDK="${LARK_REGISTER_APP_SDK:-@larksuiteoapi/node-sdk@1.68.0}"
|
|
56
71
|
LARK_CLI_MIN_VERSION="${LARK_CLI_MIN_VERSION:-1.0.64}"
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
AAMP_FEISHU_APP_SCOPES_TENANT_OVERRIDE="${FEISHU_APP_SCOPES_TENANT-}"
|
|
73
|
+
AAMP_FEISHU_APP_SCOPES_USER_OVERRIDE="${FEISHU_APP_SCOPES_USER-}"
|
|
74
|
+
AAMP_FEISHU_USER_AUTH_REQUIRED_SCOPES_OVERRIDE="${FEISHU_USER_AUTH_REQUIRED_SCOPES-}"
|
|
75
|
+
AAMP_FEISHU_USER_AUTH_REQUESTED_SCOPES_OVERRIDE="${FEISHU_USER_AUTH_REQUESTED_SCOPES-}"
|
|
76
|
+
AAMP_FEISHU_USER_AUTH_EXCLUDES_OVERRIDE="${FEISHU_USER_AUTH_EXCLUDES-}"
|
|
77
|
+
FEISHU_APP_SCOPES_TENANT="$AAMP_FEISHU_APP_SCOPES_TENANT_OVERRIDE"
|
|
78
|
+
FEISHU_APP_SCOPES_USER="$AAMP_FEISHU_APP_SCOPES_USER_OVERRIDE"
|
|
59
79
|
FEISHU_APP_EVENTS_TENANT="${FEISHU_APP_EVENTS_TENANT:-task.task.update_user_access_v2}"
|
|
60
80
|
FEISHU_APP_EVENTS_USER="${FEISHU_APP_EVENTS_USER:-task.task.update_user_access_v2}"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
81
|
+
FEISHU_USER_AUTH_EXCLUDES="$AAMP_FEISHU_USER_AUTH_EXCLUDES_OVERRIDE"
|
|
82
|
+
FEISHU_USER_AUTH_REQUIRED_SCOPES="$AAMP_FEISHU_USER_AUTH_REQUIRED_SCOPES_OVERRIDE"
|
|
83
|
+
FEISHU_USER_AUTH_REQUESTED_SCOPES="$AAMP_FEISHU_USER_AUTH_REQUESTED_SCOPES_OVERRIDE"
|
|
84
|
+
FEISHU_USER_AUTH_CORE_SCOPES="${FEISHU_USER_AUTH_CORE_SCOPES:-}"
|
|
85
|
+
FEISHU_USER_AUTH_OPTIONAL_SCOPES="${FEISHU_USER_AUTH_OPTIONAL_SCOPES:-}"
|
|
86
|
+
FEISHU_USER_AUTH_MODE="${FEISHU_USER_AUTH_MODE:-optional}"
|
|
87
|
+
FEISHU_TASK_PROFILE_DOMAINS="${FEISHU_TASK_PROFILE_DOMAINS:-task}"
|
|
88
|
+
AAMP_FEISHU_AUTH_STATE_DIR="${AAMP_FEISHU_AUTH_STATE_DIR:-$HOME/.aamp/feishu-bridge/auth-capabilities}"
|
|
89
|
+
AAMP_TASK_ALLOW_PACKAGE_OVERRIDES="${AAMP_TASK_ALLOW_PACKAGE_OVERRIDES:-false}"
|
|
90
|
+
if [ "$AAMP_TASK_INTERNAL" = "true" ] \
|
|
91
|
+
&& [ "${AAMP_TASK_PACKAGE_OVERRIDES_RESOLVED:-false}" = "true" ]; then
|
|
92
|
+
AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG="${AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG:-}"
|
|
93
|
+
AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG="${AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG:-}"
|
|
94
|
+
AAMP_TASK_REQUESTED_AIME_ACP_PKG="${AAMP_TASK_REQUESTED_AIME_ACP_PKG:-}"
|
|
95
|
+
else
|
|
96
|
+
AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG="${ACP_BRIDGE_PKG:-${AAMP_TASK_ACP_BRIDGE_PKG:-}}"
|
|
97
|
+
AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG="${FEISHU_BRIDGE_PKG:-${AAMP_TASK_FEISHU_BRIDGE_PKG:-}}"
|
|
98
|
+
AAMP_TASK_REQUESTED_AIME_ACP_PKG="${AIME_ACP_PKG:-${AAMP_TASK_AIME_ACP_PKG:-}}"
|
|
99
|
+
fi
|
|
100
|
+
unset AAMP_TASK_PACKAGE_OVERRIDES_RESOLVED
|
|
101
|
+
unset ACP_BRIDGE_PKG AAMP_TASK_ACP_BRIDGE_PKG
|
|
102
|
+
unset FEISHU_BRIDGE_PKG AAMP_TASK_FEISHU_BRIDGE_PKG
|
|
103
|
+
unset AIME_ACP_PKG AIME_ACP_REGISTRY AAMP_TASK_AIME_ACP_PKG
|
|
104
|
+
ACP_BRIDGE_PKG="${ACP_BRIDGE_PKG:-@luckyterry/aamp-acp-bridge@0.1.29-dev.0}"
|
|
105
|
+
AIME_ACP_PKG="${AIME_ACP_PKG:-@tengchengwei/aime-acp@0.1.1-dev.1}"
|
|
106
|
+
AIME_ACP_REGISTRY="${AIME_ACP_REGISTRY:-https://bnpm.byted.org}"
|
|
65
107
|
CLI_BRIDGE_PKG="${CLI_BRIDGE_PKG:-@zengxingyuan/aamp-cli-bridge@0.1.7-dev.14}"
|
|
66
|
-
FEISHU_BRIDGE_PKG="${FEISHU_BRIDGE_PKG:-@
|
|
108
|
+
FEISHU_BRIDGE_PKG="${FEISHU_BRIDGE_PKG:-@iluolyx/aamp-feishu-bridge@0.1.52-dev.5}"
|
|
109
|
+
AAMP_TASK_DEFAULT_ACP_BRIDGE_PKG="$ACP_BRIDGE_PKG"
|
|
110
|
+
AAMP_TASK_DEFAULT_FEISHU_BRIDGE_PKG="$FEISHU_BRIDGE_PKG"
|
|
111
|
+
AAMP_TASK_DEFAULT_AIME_ACP_PKG="$AIME_ACP_PKG"
|
|
67
112
|
AAMP_TASK_AGENT_NAME="${AAMP_TASK_AGENT_NAME:-@larktask/aamp-feishu-task-agent}"
|
|
68
113
|
AAMP_TASK_AGENT_LEGACY_NAME="${AAMP_TASK_AGENT_LEGACY_NAME:-@zengxingyuan/aamp-feishu-task-agent}"
|
|
69
|
-
AAMP_TASK_AGENT_VERSION="0.1.
|
|
114
|
+
AAMP_TASK_AGENT_VERSION="0.1.1-dev.2"
|
|
70
115
|
AAMP_TASK_AGENT_CHANNEL="${AAMP_TASK_AGENT_CHANNEL:-dev}"
|
|
71
116
|
AAMP_STALE_PROCESS_CLEANUP="${AAMP_STALE_PROCESS_CLEANUP:-false}"
|
|
72
117
|
AAMP_STALE_PROCESS_SECONDS="${AAMP_STALE_PROCESS_SECONDS:-86400}"
|
|
@@ -86,8 +131,12 @@ CODEM_SERVICE_START_OUTPUT=""
|
|
|
86
131
|
CODEM_AUTO_UPDATE_DONE="false"
|
|
87
132
|
CODEM_FORCE_LOGIN_DONE="false"
|
|
88
133
|
CODEM_PROVIDER_RECOVERY_DONE="false"
|
|
134
|
+
TRAE_CLI_BIN=""
|
|
135
|
+
TRAECODE_CLI_BIN=""
|
|
89
136
|
PAIRING_URL=""
|
|
90
137
|
ACP_AGENT_COMMAND=""
|
|
138
|
+
AGENT_PREPARE_CANCELLED="false"
|
|
139
|
+
AGENT_PREPARE_CANCEL_REASON=""
|
|
91
140
|
STARTED_BRIDGE_PID=""
|
|
92
141
|
ONE_CLICK_RUN_ID="$(date +%s)-$$"
|
|
93
142
|
BOT_RESERVED="false"
|
|
@@ -116,6 +165,13 @@ sanitize_inherited_npm_exec_env() {
|
|
|
116
165
|
done < <(env)
|
|
117
166
|
}
|
|
118
167
|
|
|
168
|
+
remote_internal_helper() {
|
|
169
|
+
[ "$AAMP_TASK_INTERNAL" = "true" ] && {
|
|
170
|
+
[ "$AAMP_TASK_INTERNAL_EXECUTION_LOCATION" = "remote" ] \
|
|
171
|
+
|| [ "$AGENT_EXECUTION_LOCATION" = "remote" ]
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
119
175
|
usage() {
|
|
120
176
|
cat <<'USAGE'
|
|
121
177
|
Usage:
|
|
@@ -132,7 +188,8 @@ Running the standalone one-click script without a subcommand is the same as
|
|
|
132
188
|
"feishu-task-agent install".
|
|
133
189
|
|
|
134
190
|
Options:
|
|
135
|
-
--agent codex|cursor
|
|
191
|
+
--agent codex|cursor|coco|traex|traecli|workbuddy|workbuddy_ai|aime
|
|
192
|
+
Use this Agent for every new binding in the command.
|
|
136
193
|
--aamp-host URL AAMP service URL. Default: https://meshmail.ai
|
|
137
194
|
--debug Enable debug mode for bridge processes
|
|
138
195
|
-h, --help Show this help
|
|
@@ -792,6 +849,7 @@ task_agent_global_install_is_complete() {
|
|
|
792
849
|
[ -x "$NPM_GLOBAL_PREFIX/bin/aamp-logs" ] || return 1
|
|
793
850
|
[ -r "$package_dir/bootstrap/aamp-feishu-task-agent-bootstrap.sh" ] || return 1
|
|
794
851
|
[ -r "$package_dir/bin/feishu-task-agent-controller.mjs" ] || return 1
|
|
852
|
+
[ -r "$package_dir/bin/traecode-readiness.mjs" ] || return 1
|
|
795
853
|
}
|
|
796
854
|
|
|
797
855
|
task_agent_global_install_is_current() {
|
|
@@ -816,7 +874,7 @@ ensure_task_agent_global_install_locked() {
|
|
|
816
874
|
fi
|
|
817
875
|
remove_legacy_task_agent_global_install || return 1
|
|
818
876
|
agent_detail "synchronizing task-agent npm package: current=${installed_version:-missing} expected=$expected_version"
|
|
819
|
-
npm_install_global "$AAMP_TASK_AGENT_NAME@$expected_version" >>"$ONE_CLICK_LOG" 2>&1 || return 1
|
|
877
|
+
npm_install_global "$AAMP_TASK_AGENT_NAME@$expected_version" --force >>"$ONE_CLICK_LOG" 2>&1 || return 1
|
|
820
878
|
task_agent_global_install_is_current "$expected_version"
|
|
821
879
|
}
|
|
822
880
|
|
|
@@ -830,12 +888,16 @@ ensure_task_agent_global_install() {
|
|
|
830
888
|
|
|
831
889
|
short_command_is_current() {
|
|
832
890
|
local target="$1"
|
|
891
|
+
local source_file="${2:-}"
|
|
833
892
|
local installed_name installed_version
|
|
834
893
|
[ -x "$target" ] || return 1
|
|
835
894
|
installed_name="$(script_file_task_agent_name "$target" || true)"
|
|
836
895
|
installed_version="$(script_file_task_agent_version "$target" || true)"
|
|
837
896
|
[ "$installed_name" = "$AAMP_TASK_AGENT_NAME" ] || return 1
|
|
838
|
-
[ "$installed_version" = "$AAMP_TASK_AGENT_VERSION" ]
|
|
897
|
+
[ "$installed_version" = "$AAMP_TASK_AGENT_VERSION" ] || return 1
|
|
898
|
+
if [ -n "$source_file" ] && [ -r "$source_file" ]; then
|
|
899
|
+
cmp -s "$source_file" "$target"
|
|
900
|
+
fi
|
|
839
901
|
}
|
|
840
902
|
|
|
841
903
|
write_task_update_cache() {
|
|
@@ -913,13 +975,13 @@ install_short_command_current() {
|
|
|
913
975
|
local target="$1"
|
|
914
976
|
local source_file="${BASH_SOURCE[0]:-}"
|
|
915
977
|
local tmp="${target}.tmp.$$"
|
|
916
|
-
if short_command_is_current "$target"; then
|
|
917
|
-
agent_detail "short command already current: $target"
|
|
918
|
-
return 0
|
|
919
|
-
fi
|
|
920
978
|
case "$source_file" in
|
|
921
979
|
""|/dev/fd/*|/private/dev/fd/*|/proc/*) source_file="" ;;
|
|
922
980
|
esac
|
|
981
|
+
if short_command_is_current "$target" "$source_file"; then
|
|
982
|
+
agent_detail "short command already current: $target"
|
|
983
|
+
return 0
|
|
984
|
+
fi
|
|
923
985
|
if [ -n "$source_file" ] && [ -f "$source_file" ] && [ -r "$source_file" ]; then
|
|
924
986
|
mkdir -p "$(dirname "$target")"
|
|
925
987
|
cp "$source_file" "$tmp" 2>>"$ONE_CLICK_LOG" || return 1
|
|
@@ -950,6 +1012,7 @@ ensure_selected_agent_for_start() {
|
|
|
950
1012
|
select_agent_interactively
|
|
951
1013
|
fi
|
|
952
1014
|
validate_agent_name "$AGENT"
|
|
1015
|
+
ensure_agent_selection_available "$AGENT"
|
|
953
1016
|
}
|
|
954
1017
|
|
|
955
1018
|
resolve_latest_task_agent_version() {
|
|
@@ -1034,12 +1097,109 @@ run_task_agent_update_command() {
|
|
|
1034
1097
|
install_aamp_logs_bin "$installed_version" || agent_fail "failed to synchronize aamp-logs $installed_version"
|
|
1035
1098
|
printf '当前版本:%s\n' "$installed_version"
|
|
1036
1099
|
}
|
|
1037
|
-
|
|
1100
|
+
classify_remote_npm_cache_error() {
|
|
1101
|
+
node -e '
|
|
1102
|
+
let carry = "";
|
|
1103
|
+
let lineSawEnoent = false;
|
|
1104
|
+
let lineSawTarballData = false;
|
|
1105
|
+
let matched = false;
|
|
1106
|
+
const scan = (part, endOfLine) => {
|
|
1107
|
+
const text = (carry + part).toLowerCase();
|
|
1108
|
+
const sawEnoent = lineSawEnoent || text.includes("enoent");
|
|
1109
|
+
const sawTarballData = lineSawTarballData || text.includes("tarball data");
|
|
1110
|
+
if (text.includes("invalid response body")
|
|
1111
|
+
|| text.includes("_cacache")
|
|
1112
|
+
|| (sawEnoent && text.includes("content-v2"))
|
|
1113
|
+
|| (sawTarballData && text.includes("corrupted"))
|
|
1114
|
+
|| text.includes("tar_bad_archive")
|
|
1115
|
+
|| text.includes("zlib: unexpected end of file")) matched = true;
|
|
1116
|
+
lineSawEnoent = sawEnoent;
|
|
1117
|
+
lineSawTarballData = sawTarballData;
|
|
1118
|
+
carry = text.slice(-64);
|
|
1119
|
+
if (endOfLine) {
|
|
1120
|
+
carry = "";
|
|
1121
|
+
lineSawEnoent = false;
|
|
1122
|
+
lineSawTarballData = false;
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
process.stdin.on("data", (chunk) => {
|
|
1126
|
+
const parts = chunk.toString("latin1").split(/\r?\n/);
|
|
1127
|
+
parts.forEach((part, index) => scan(part, index < parts.length - 1));
|
|
1128
|
+
});
|
|
1129
|
+
process.stdin.on("end", () => { process.exitCode = matched ? 0 : 1; });
|
|
1130
|
+
process.stdin.on("error", () => { process.exitCode = 2; });
|
|
1131
|
+
'
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
REMOTE_NPM_INSTALL_STATUS=1
|
|
1135
|
+
REMOTE_NPM_CACHE_ERROR=false
|
|
1136
|
+
run_remote_npm_install_attempt() {
|
|
1137
|
+
local registry="$1"
|
|
1138
|
+
shift
|
|
1139
|
+
local pipeline_status=()
|
|
1140
|
+
sanitize_inherited_npm_exec_env
|
|
1141
|
+
set +e
|
|
1142
|
+
"$NPM_BIN" install -g \
|
|
1143
|
+
--registry "$registry" \
|
|
1144
|
+
--cache "$NPM_CACHE_DIR" \
|
|
1145
|
+
--prefix "$NPM_GLOBAL_PREFIX" \
|
|
1146
|
+
"$@" 2>&1 | classify_remote_npm_cache_error
|
|
1147
|
+
pipeline_status=("${PIPESTATUS[@]}")
|
|
1148
|
+
set -e
|
|
1149
|
+
REMOTE_NPM_INSTALL_STATUS="${pipeline_status[0]:-1}"
|
|
1150
|
+
REMOTE_NPM_CACHE_ERROR=false
|
|
1151
|
+
if [ "${pipeline_status[1]:-2}" -eq 0 ]; then
|
|
1152
|
+
REMOTE_NPM_CACHE_ERROR=true
|
|
1153
|
+
fi
|
|
1154
|
+
return 0
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
npm_install_global_from_registry_remote() {
|
|
1158
|
+
local registry="$1"
|
|
1159
|
+
shift
|
|
1160
|
+
finish_remote_npm_install() {
|
|
1161
|
+
trap - EXIT HUP INT TERM
|
|
1162
|
+
unset -f finish_remote_npm_install
|
|
1163
|
+
}
|
|
1164
|
+
trap finish_remote_npm_install EXIT
|
|
1165
|
+
trap 'exit 129' HUP
|
|
1166
|
+
trap 'exit 130' INT
|
|
1167
|
+
trap 'exit 143' TERM
|
|
1168
|
+
run_remote_npm_install_attempt "$registry" "$@"
|
|
1169
|
+
if [ "$REMOTE_NPM_INSTALL_STATUS" -eq 0 ]; then
|
|
1170
|
+
hash -r 2>/dev/null || true
|
|
1171
|
+
finish_remote_npm_install
|
|
1172
|
+
return 0
|
|
1173
|
+
fi
|
|
1174
|
+
|
|
1175
|
+
if [ "$REMOTE_NPM_CACHE_ERROR" != "true" ]; then
|
|
1176
|
+
finish_remote_npm_install
|
|
1177
|
+
return 1
|
|
1178
|
+
fi
|
|
1179
|
+
|
|
1180
|
+
reset_npm_cache_for_retry
|
|
1181
|
+
run_remote_npm_install_attempt "$registry" "$@"
|
|
1182
|
+
if [ "$REMOTE_NPM_INSTALL_STATUS" -ne 0 ]; then
|
|
1183
|
+
finish_remote_npm_install
|
|
1184
|
+
return 1
|
|
1185
|
+
fi
|
|
1186
|
+
hash -r 2>/dev/null || true
|
|
1187
|
+
finish_remote_npm_install
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
npm_install_global_from_registry() {
|
|
1191
|
+
if remote_internal_helper; then
|
|
1192
|
+
npm_install_global_from_registry_remote "$@"
|
|
1193
|
+
return $?
|
|
1194
|
+
fi
|
|
1195
|
+
|
|
1196
|
+
local registry="$1"
|
|
1197
|
+
shift
|
|
1038
1198
|
local npm_log
|
|
1039
1199
|
npm_log="$(mktemp "${TMPDIR:-/tmp}/aamp-npm-install.XXXXXX")"
|
|
1040
1200
|
sanitize_inherited_npm_exec_env
|
|
1041
1201
|
if "$NPM_BIN" install -g \
|
|
1042
|
-
--registry "$
|
|
1202
|
+
--registry "$registry" \
|
|
1043
1203
|
--cache "$NPM_CACHE_DIR" \
|
|
1044
1204
|
--prefix "$NPM_GLOBAL_PREFIX" \
|
|
1045
1205
|
"$@" >"$npm_log" 2>&1; then
|
|
@@ -1057,7 +1217,7 @@ npm_install_global() {
|
|
|
1057
1217
|
npm_log="$(mktemp "${TMPDIR:-/tmp}/aamp-npm-install-retry.XXXXXX")"
|
|
1058
1218
|
sanitize_inherited_npm_exec_env
|
|
1059
1219
|
"$NPM_BIN" install -g \
|
|
1060
|
-
--registry "$
|
|
1220
|
+
--registry "$registry" \
|
|
1061
1221
|
--cache "$NPM_CACHE_DIR" \
|
|
1062
1222
|
--prefix "$NPM_GLOBAL_PREFIX" \
|
|
1063
1223
|
"$@" >"$npm_log" 2>&1 || {
|
|
@@ -1068,6 +1228,10 @@ npm_install_global() {
|
|
|
1068
1228
|
hash -r 2>/dev/null || true
|
|
1069
1229
|
}
|
|
1070
1230
|
|
|
1231
|
+
npm_install_global() {
|
|
1232
|
+
npm_install_global_from_registry "$NPM_REGISTRY" "$@"
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1071
1235
|
npx_package() {
|
|
1072
1236
|
local npm_log
|
|
1073
1237
|
local status
|
|
@@ -1177,13 +1341,32 @@ npm_install_register_helper() {
|
|
|
1177
1341
|
}
|
|
1178
1342
|
}
|
|
1179
1343
|
|
|
1344
|
+
aime_internal_network_reachable() {
|
|
1345
|
+
command -v ping >/dev/null 2>&1 || return 1
|
|
1346
|
+
if is_macos; then
|
|
1347
|
+
ping -c 1 -W 1000 aime.bytedance.net >/dev/null 2>&1
|
|
1348
|
+
else
|
|
1349
|
+
ping -c 1 -W 1 aime.bytedance.net >/dev/null 2>&1
|
|
1350
|
+
fi
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1180
1353
|
validate_agent_name() {
|
|
1181
1354
|
case "$1" in
|
|
1182
|
-
codex|cursor) ;;
|
|
1183
|
-
*) agent_fail "--agent must be codex or
|
|
1355
|
+
codex|cursor|coco|traex|traecli|workbuddy|workbuddy_ai|aime) ;;
|
|
1356
|
+
*) agent_fail "--agent must be codex, cursor, coco, traex, traecli, workbuddy, workbuddy_ai, or aime" ;;
|
|
1184
1357
|
esac
|
|
1185
1358
|
}
|
|
1186
1359
|
|
|
1360
|
+
ensure_agent_selection_available() {
|
|
1361
|
+
[ "$1" = "aime" ] || return 0
|
|
1362
|
+
aime_internal_network_reachable \
|
|
1363
|
+
|| agent_fail "AIME 仅在公司内网可用;当前无法 ping 通 aime.bytedance.net。"
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
agent_display_name() {
|
|
1367
|
+
printf '%s' "$1"
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1187
1370
|
read_tty_line() {
|
|
1188
1371
|
local prompt="$1"
|
|
1189
1372
|
local value
|
|
@@ -1203,6 +1386,24 @@ agent_cli_detected() {
|
|
|
1203
1386
|
cursor)
|
|
1204
1387
|
find_cursor_agent_cli >/dev/null 2>&1
|
|
1205
1388
|
;;
|
|
1389
|
+
traex)
|
|
1390
|
+
find_traex_cli >/dev/null 2>&1
|
|
1391
|
+
;;
|
|
1392
|
+
coco)
|
|
1393
|
+
find_legacy_trae_cli >/dev/null 2>&1
|
|
1394
|
+
;;
|
|
1395
|
+
traecli)
|
|
1396
|
+
find_traecode_cli >/dev/null 2>&1
|
|
1397
|
+
;;
|
|
1398
|
+
workbuddy)
|
|
1399
|
+
find_workbuddy_cli >/dev/null 2>&1
|
|
1400
|
+
;;
|
|
1401
|
+
workbuddy_ai)
|
|
1402
|
+
find_workbuddy_ai_cli >/dev/null 2>&1
|
|
1403
|
+
;;
|
|
1404
|
+
aime)
|
|
1405
|
+
aime_internal_network_reachable
|
|
1406
|
+
;;
|
|
1206
1407
|
*)
|
|
1207
1408
|
return 1
|
|
1208
1409
|
;;
|
|
@@ -1217,9 +1418,25 @@ discover_interactive_agents() {
|
|
|
1217
1418
|
if agent_cli_detected cursor; then
|
|
1218
1419
|
DETECTED_AGENTS+=("cursor")
|
|
1219
1420
|
fi
|
|
1421
|
+
if agent_cli_detected traex; then
|
|
1422
|
+
DETECTED_AGENTS+=("traex")
|
|
1423
|
+
elif agent_cli_detected coco; then
|
|
1424
|
+
DETECTED_AGENTS+=("coco")
|
|
1425
|
+
elif agent_cli_detected traecli; then
|
|
1426
|
+
DETECTED_AGENTS+=("traecli")
|
|
1427
|
+
fi
|
|
1428
|
+
if agent_cli_detected workbuddy; then
|
|
1429
|
+
DETECTED_AGENTS+=("workbuddy")
|
|
1430
|
+
fi
|
|
1431
|
+
if agent_cli_detected workbuddy_ai; then
|
|
1432
|
+
DETECTED_AGENTS+=("workbuddy_ai")
|
|
1433
|
+
fi
|
|
1434
|
+
if agent_cli_detected aime; then
|
|
1435
|
+
DETECTED_AGENTS+=("aime")
|
|
1436
|
+
fi
|
|
1220
1437
|
|
|
1221
1438
|
if [ "${#DETECTED_AGENTS[@]}" -eq 0 ]; then
|
|
1222
|
-
agent_fail "
|
|
1439
|
+
agent_fail "暂未检测到本地智能体。请先安装 Codex、Cursor、Trae CLI、WorkBuddy 或 WorkBuddy AI 后重试。"
|
|
1223
1440
|
fi
|
|
1224
1441
|
}
|
|
1225
1442
|
|
|
@@ -1238,9 +1455,9 @@ render_agent_menu() {
|
|
|
1238
1455
|
for index in "${!DETECTED_AGENTS[@]}"; do
|
|
1239
1456
|
printf '\033[2K\r' >&3
|
|
1240
1457
|
if [ "$index" -eq "$selected" ]; then
|
|
1241
|
-
printf ' > %s\n' "${DETECTED_AGENTS[$index]}" >&3
|
|
1458
|
+
printf ' > %s\n' "$(agent_display_name "${DETECTED_AGENTS[$index]}")" >&3
|
|
1242
1459
|
else
|
|
1243
|
-
printf ' %s\n' "${DETECTED_AGENTS[$index]}" >&3
|
|
1460
|
+
printf ' %s\n' "$(agent_display_name "${DETECTED_AGENTS[$index]}")" >&3
|
|
1244
1461
|
fi
|
|
1245
1462
|
done
|
|
1246
1463
|
printf '\033[2K\r使用 ↑/↓ 选择,回车确认。也可按数字键或 j/k。\n' >&3
|
|
@@ -1254,7 +1471,7 @@ select_agent_interactively() {
|
|
|
1254
1471
|
discover_interactive_agents
|
|
1255
1472
|
|
|
1256
1473
|
if ! exec 3<>/dev/tty; then
|
|
1257
|
-
agent_fail "missing --agent and no interactive terminal is available; pass --agent codex|cursor"
|
|
1474
|
+
agent_fail "missing --agent and no interactive terminal is available; pass --agent codex|cursor|coco|traex|traecli|workbuddy|workbuddy_ai|aime"
|
|
1258
1475
|
fi
|
|
1259
1476
|
|
|
1260
1477
|
tty_state="$(stty -g <&3)"
|
|
@@ -1440,7 +1657,7 @@ parse_args() {
|
|
|
1440
1657
|
fi
|
|
1441
1658
|
|
|
1442
1659
|
case "${1:-}" in
|
|
1443
|
-
install|start|list|add|remove|update|help|__discover-agents|__register-binding|__prepare-agent|__ensure-profile)
|
|
1660
|
+
install|start|list|add|remove|update|help|__discover-agents|__register-binding|__prepare-agent|__probe-profile|__ensure-profile)
|
|
1444
1661
|
AAMP_TASK_ACTION="$1"
|
|
1445
1662
|
shift
|
|
1446
1663
|
;;
|
|
@@ -1795,7 +2012,7 @@ save_bot_config() {
|
|
|
1795
2012
|
local profile="$3"
|
|
1796
2013
|
local app_secret="${4:-}"
|
|
1797
2014
|
mkdir -p "$(dirname "$BOT_CONFIG_FILE")"
|
|
1798
|
-
BOT_CONFIG_FILE="$BOT_CONFIG_FILE" BOT_NAME="$bot_name" BOT_APP_ID="$app_id" BOT_PROFILE="$profile" BOT_APP_SECRET="$app_secret"
|
|
2015
|
+
BOT_CONFIG_FILE="$BOT_CONFIG_FILE" BOT_NAME="$bot_name" BOT_APP_ID="$app_id" BOT_PROFILE="$profile" BOT_APP_SECRET="$app_secret" FEISHU_TASK_PROFILE_DOMAINS="$FEISHU_TASK_PROFILE_DOMAINS" FEISHU_SCOPE_MANIFEST_VERSION="$FEISHU_SCOPE_MANIFEST_VERSION" FEISHU_USER_AUTH_MODE="$FEISHU_USER_AUTH_MODE" node -e '
|
|
1799
2016
|
const fs = require("fs");
|
|
1800
2017
|
const file = process.env.BOT_CONFIG_FILE;
|
|
1801
2018
|
const appSecret = String(process.env.BOT_APP_SECRET || "").trim();
|
|
@@ -1806,7 +2023,9 @@ const next = {
|
|
|
1806
2023
|
profile: process.env.BOT_PROFILE,
|
|
1807
2024
|
auth_mode: "lark-cli",
|
|
1808
2025
|
capabilities: ["im", "task"],
|
|
1809
|
-
domains: String(process.env.
|
|
2026
|
+
domains: String(process.env.FEISHU_TASK_PROFILE_DOMAINS || "task").split(",").map((item) => item.trim()).filter(Boolean),
|
|
2027
|
+
scope_manifest_version: Number(process.env.FEISHU_SCOPE_MANIFEST_VERSION || 0),
|
|
2028
|
+
user_auth_mode: process.env.FEISHU_USER_AUTH_MODE || "optional",
|
|
1810
2029
|
updated_at: new Date().toISOString(),
|
|
1811
2030
|
};
|
|
1812
2031
|
let parsed = { version: 1, profiles: [] };
|
|
@@ -2039,15 +2258,65 @@ ensure_lark_cli_min_version() {
|
|
|
2039
2258
|
select_lark_cli_bin
|
|
2040
2259
|
}
|
|
2041
2260
|
|
|
2261
|
+
feishu_scope_manifest_json() {
|
|
2262
|
+
cat <<'JSON'
|
|
2263
|
+
{"version":2,"app":{"tenant":["im:message","im:message:send_as_bot","im:message:readonly","im:resource","cardkit:card:write","task:task","task:comment","task:task:readonly","task:comment:readonly","task:attachment:read","task:attachment:write","task:comment:read","task:comment:write","task:task:read","task:task:write","task:tasklist:read","task:tasklist:write","task:custom_field:read","task:custom_field:write","task:section:read","task:section:write"],"user":["im:message","im:message:readonly","im:resource","cardkit:card:write","task:task","task:comment","task:task:readonly","task:comment:readonly","task:attachment:read","task:attachment:write","task:comment:read","task:comment:write","task:task:read","task:task:write","task:tasklist:read","task:tasklist:write","task:custom_field:read","task:custom_field:write","task:section:read","task:section:write"]},"userAuth":{"core":[],"optional":["task:task","task:comment","task:task:readonly","task:comment:readonly","task:attachment:read","task:attachment:write","task:comment:read","task:comment:write","task:task:read","task:task:write","task:tasklist:read","task:tasklist:write","task:custom_field:read","task:custom_field:write","task:section:read","task:section:write"]}}
|
|
2264
|
+
JSON
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
initialize_feishu_scope_manifest() {
|
|
2268
|
+
local manifest_values=()
|
|
2269
|
+
local value
|
|
2270
|
+
|
|
2271
|
+
while IFS= read -r value; do
|
|
2272
|
+
manifest_values+=("$value")
|
|
2273
|
+
done < <(feishu_scope_manifest_json | node -e '
|
|
2274
|
+
const fs = require("fs");
|
|
2275
|
+
const manifest = JSON.parse(fs.readFileSync(0, "utf8"));
|
|
2276
|
+
console.log(String(manifest.version));
|
|
2277
|
+
console.log(manifest.app.tenant.join(","));
|
|
2278
|
+
console.log(manifest.app.user.join(","));
|
|
2279
|
+
console.log(manifest.userAuth.core.join(" "));
|
|
2280
|
+
console.log(manifest.userAuth.optional.join(" "));
|
|
2281
|
+
')
|
|
2282
|
+
|
|
2283
|
+
[ "${#manifest_values[@]}" -eq 5 ] || agent_fail "invalid embedded Feishu scope manifest"
|
|
2284
|
+
FEISHU_SCOPE_MANIFEST_VERSION="${FEISHU_SCOPE_MANIFEST_VERSION:-${manifest_values[0]}}"
|
|
2285
|
+
FEISHU_APP_SCOPES_TENANT="${FEISHU_APP_SCOPES_TENANT:-${manifest_values[1]}}"
|
|
2286
|
+
FEISHU_APP_SCOPES_USER="${FEISHU_APP_SCOPES_USER:-${manifest_values[2]}}"
|
|
2287
|
+
FEISHU_USER_AUTH_CORE_SCOPES="${FEISHU_USER_AUTH_CORE_SCOPES:-${manifest_values[3]}}"
|
|
2288
|
+
FEISHU_USER_AUTH_OPTIONAL_SCOPES="${FEISHU_USER_AUTH_OPTIONAL_SCOPES:-${manifest_values[4]}}"
|
|
2289
|
+
if [ -n "${FEISHU_USER_AUTH_REQUIRED_SCOPES:-}" ]; then
|
|
2290
|
+
FEISHU_USER_AUTH_CORE_SCOPES="$FEISHU_USER_AUTH_REQUIRED_SCOPES"
|
|
2291
|
+
else
|
|
2292
|
+
FEISHU_USER_AUTH_REQUIRED_SCOPES="$FEISHU_USER_AUTH_CORE_SCOPES"
|
|
2293
|
+
fi
|
|
2294
|
+
if [ -z "${FEISHU_USER_AUTH_REQUESTED_SCOPES:-}" ]; then
|
|
2295
|
+
if [ -n "${AAMP_FEISHU_USER_AUTH_REQUIRED_SCOPES_OVERRIDE:-}" ]; then
|
|
2296
|
+
FEISHU_USER_AUTH_REQUESTED_SCOPES="$FEISHU_USER_AUTH_REQUIRED_SCOPES"
|
|
2297
|
+
else
|
|
2298
|
+
FEISHU_USER_AUTH_REQUESTED_SCOPES="${FEISHU_USER_AUTH_CORE_SCOPES} ${FEISHU_USER_AUTH_OPTIONAL_SCOPES}"
|
|
2299
|
+
fi
|
|
2300
|
+
fi
|
|
2301
|
+
export FEISHU_SCOPE_MANIFEST_VERSION
|
|
2302
|
+
export FEISHU_APP_SCOPES_TENANT FEISHU_APP_SCOPES_USER
|
|
2303
|
+
export FEISHU_USER_AUTH_CORE_SCOPES FEISHU_USER_AUTH_OPTIONAL_SCOPES
|
|
2304
|
+
export FEISHU_USER_AUTH_REQUIRED_SCOPES FEISHU_USER_AUTH_REQUESTED_SCOPES
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2042
2307
|
lark_cli_user_auth_satisfied() {
|
|
2043
2308
|
local profile="$1"
|
|
2309
|
+
local required_scopes="${2:-${FEISHU_USER_AUTH_REQUIRED_SCOPES:-${FEISHU_USER_AUTH_CORE_SCOPES:-}}}"
|
|
2044
2310
|
local status_json auth_check
|
|
2045
2311
|
LARK_CLI_CMD="${LARK_CLI_CMD:-lark-cli}"
|
|
2046
2312
|
|
|
2313
|
+
if [ -z "${required_scopes//[[:space:],]/}" ]; then
|
|
2314
|
+
return 0
|
|
2315
|
+
fi
|
|
2047
2316
|
status_json="$("$LARK_CLI_CMD" --profile "$profile" auth status --json 2>/dev/null)" || return 1
|
|
2048
2317
|
|
|
2049
2318
|
set +e
|
|
2050
|
-
auth_check="$(AUTH_STATUS_JSON="$status_json" FEISHU_USER_AUTH_REQUIRED_SCOPES="$
|
|
2319
|
+
auth_check="$(AUTH_STATUS_JSON="$status_json" FEISHU_USER_AUTH_REQUIRED_SCOPES="$required_scopes" FEISHU_USER_AUTH_EXCLUDES="$FEISHU_USER_AUTH_EXCLUDES" node -e '
|
|
2051
2320
|
function parseJsonOutput(value, label) {
|
|
2052
2321
|
const raw = String(value || "");
|
|
2053
2322
|
const start = raw.indexOf("{");
|
|
@@ -2088,10 +2357,76 @@ console.log("ok");
|
|
|
2088
2357
|
return 1
|
|
2089
2358
|
}
|
|
2090
2359
|
|
|
2360
|
+
write_lark_cli_auth_capability_snapshot() {
|
|
2361
|
+
local profile="$1"
|
|
2362
|
+
local status_json="{}"
|
|
2363
|
+
local state_dir="$AAMP_FEISHU_AUTH_STATE_DIR"
|
|
2364
|
+
local safe_profile state_file temp_file
|
|
2365
|
+
|
|
2366
|
+
set +e
|
|
2367
|
+
status_json="$("$LARK_CLI_CMD" --profile "$profile" auth status --json 2>/dev/null)"
|
|
2368
|
+
local status_code=$?
|
|
2369
|
+
set -e
|
|
2370
|
+
if [ "$status_code" -ne 0 ] || [ -z "$status_json" ]; then
|
|
2371
|
+
status_json="{}"
|
|
2372
|
+
fi
|
|
2373
|
+
|
|
2374
|
+
mkdir -p "$state_dir" || return 1
|
|
2375
|
+
safe_profile="$(PROFILE="$profile" node -e 'process.stdout.write(String(process.env.PROFILE || "profile").replace(/[^A-Za-z0-9._-]/g, "_"))')" || return 1
|
|
2376
|
+
state_file="$state_dir/$safe_profile.json"
|
|
2377
|
+
temp_file="$(mktemp "$state_dir/.auth-capabilities.XXXXXX")" || return 1
|
|
2378
|
+
if ! AUTH_STATUS_JSON="$status_json" \
|
|
2379
|
+
AUTH_PROFILE="$profile" \
|
|
2380
|
+
FEISHU_SCOPE_MANIFEST_VERSION="$FEISHU_SCOPE_MANIFEST_VERSION" \
|
|
2381
|
+
FEISHU_USER_AUTH_CORE_SCOPES="$FEISHU_USER_AUTH_CORE_SCOPES" \
|
|
2382
|
+
FEISHU_USER_AUTH_OPTIONAL_SCOPES="$FEISHU_USER_AUTH_OPTIONAL_SCOPES" \
|
|
2383
|
+
node -e '
|
|
2384
|
+
const raw = String(process.env.AUTH_STATUS_JSON || "{}");
|
|
2385
|
+
const start = raw.indexOf("{");
|
|
2386
|
+
let status = {};
|
|
2387
|
+
try { status = JSON.parse(start >= 0 ? raw.slice(start) : "{}"); } catch {}
|
|
2388
|
+
const user = status?.identities?.user;
|
|
2389
|
+
const usable = Boolean(user?.available) && new Set(["valid", "needs_refresh"]).has(user?.tokenStatus);
|
|
2390
|
+
const split = (value) => [...new Set(String(value || "").split(/[\s,]+/).map((item) => item.trim()).filter(Boolean))];
|
|
2391
|
+
const granted = new Set(split(user?.scope));
|
|
2392
|
+
const core = split(process.env.FEISHU_USER_AUTH_CORE_SCOPES);
|
|
2393
|
+
const optional = split(process.env.FEISHU_USER_AUTH_OPTIONAL_SCOPES);
|
|
2394
|
+
const missingCoreScopes = core.filter((scope) => !granted.has(scope));
|
|
2395
|
+
const missingOptionalScopes = optional.filter((scope) => !granted.has(scope));
|
|
2396
|
+
process.stdout.write(JSON.stringify({
|
|
2397
|
+
schemaVersion: 1,
|
|
2398
|
+
manifestVersion: Number(process.env.FEISHU_SCOPE_MANIFEST_VERSION || 0),
|
|
2399
|
+
profile: process.env.AUTH_PROFILE,
|
|
2400
|
+
tokenStatus: user?.tokenStatus || "missing",
|
|
2401
|
+
grantedScopes: [...granted],
|
|
2402
|
+
missingCoreScopes,
|
|
2403
|
+
missingOptionalScopes,
|
|
2404
|
+
capabilities: { task_user: usable && missingOptionalScopes.length === 0 },
|
|
2405
|
+
checkedAt: new Date().toISOString(),
|
|
2406
|
+
}, null, 2));
|
|
2407
|
+
' >"$temp_file"; then
|
|
2408
|
+
rm -f "$temp_file"
|
|
2409
|
+
return 1
|
|
2410
|
+
fi
|
|
2411
|
+
if ! mv "$temp_file" "$state_file"; then
|
|
2412
|
+
rm -f "$temp_file"
|
|
2413
|
+
return 1
|
|
2414
|
+
fi
|
|
2415
|
+
chmod 600 "$state_file" 2>/dev/null || true
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
persist_lark_cli_auth_capability_snapshot() {
|
|
2419
|
+
local profile="$1"
|
|
2420
|
+
if ! write_lark_cli_auth_capability_snapshot "$profile"; then
|
|
2421
|
+
agent_log "warning: unable to persist optional user capability snapshot for profile: $profile"
|
|
2422
|
+
fi
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2091
2425
|
normalize_lark_cli_auth_excludes() {
|
|
2092
|
-
EXCLUDES="$1" node -e '
|
|
2426
|
+
EXCLUDES="$1" REQUESTED_SCOPES="${2:-${FEISHU_USER_AUTH_REQUESTED_SCOPES:-}}" node -e '
|
|
2093
2427
|
const excludes = String(process.env.EXCLUDES || "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
2094
|
-
|
|
2428
|
+
const requested = new Set(String(process.env.REQUESTED_SCOPES || "").split(/[\s,]+/).map((item) => item.trim()).filter(Boolean));
|
|
2429
|
+
console.log([...new Set(excludes)].filter((scope) => requested.has(scope)).join(","));
|
|
2095
2430
|
'
|
|
2096
2431
|
}
|
|
2097
2432
|
|
|
@@ -2238,12 +2573,14 @@ run_lark_cli_auth_login_with_browser_open() {
|
|
|
2238
2573
|
run_lark_cli_auth_login() {
|
|
2239
2574
|
local profile="$1"
|
|
2240
2575
|
local auth_excludes="$2"
|
|
2576
|
+
local requested_scopes="${FEISHU_USER_AUTH_REQUESTED_SCOPES:-}"
|
|
2241
2577
|
|
|
2242
|
-
|
|
2578
|
+
[ -n "${requested_scopes//[[:space:],]/}" ] || return 0
|
|
2579
|
+
auth_excludes="$(normalize_lark_cli_auth_excludes "$auth_excludes" "$requested_scopes")"
|
|
2243
2580
|
if [ -z "$auth_excludes" ]; then
|
|
2244
|
-
run_lark_cli_auth_login_with_browser_open "$LARK_CLI_CMD" --profile "$profile" auth login --
|
|
2581
|
+
run_lark_cli_auth_login_with_browser_open "$LARK_CLI_CMD" --profile "$profile" auth login --scope "$requested_scopes"
|
|
2245
2582
|
else
|
|
2246
|
-
run_lark_cli_auth_login_with_browser_open "$LARK_CLI_CMD" --profile "$profile" auth login --
|
|
2583
|
+
run_lark_cli_auth_login_with_browser_open "$LARK_CLI_CMD" --profile "$profile" auth login --scope "$requested_scopes" --exclude "$auth_excludes"
|
|
2247
2584
|
fi
|
|
2248
2585
|
}
|
|
2249
2586
|
|
|
@@ -2252,11 +2589,43 @@ ensure_lark_cli_profile() {
|
|
|
2252
2589
|
with_lark_cli_config_lock ensure_lark_cli_profile_locked "$@"
|
|
2253
2590
|
}
|
|
2254
2591
|
|
|
2592
|
+
validate_feishu_user_auth_mode() {
|
|
2593
|
+
case "$FEISHU_USER_AUTH_MODE" in
|
|
2594
|
+
optional|required|disabled) ;;
|
|
2595
|
+
*) agent_fail "FEISHU_USER_AUTH_MODE must be optional, required, or disabled" ;;
|
|
2596
|
+
esac
|
|
2597
|
+
}
|
|
2598
|
+
|
|
2599
|
+
probe_lark_cli_profile_locked() {
|
|
2600
|
+
local profile="$1"
|
|
2601
|
+
local required_scopes
|
|
2602
|
+
|
|
2603
|
+
initialize_feishu_scope_manifest
|
|
2604
|
+
validate_feishu_user_auth_mode
|
|
2605
|
+
"$LARK_CLI_CMD" profile list 2>/dev/null | grep -F "\"$profile\"" >/dev/null 2>&1 \
|
|
2606
|
+
|| return 1
|
|
2607
|
+
if [ "$FEISHU_USER_AUTH_MODE" = "disabled" ]; then
|
|
2608
|
+
persist_lark_cli_auth_capability_snapshot "$profile"
|
|
2609
|
+
return 0
|
|
2610
|
+
fi
|
|
2611
|
+
if [ "$FEISHU_USER_AUTH_MODE" = "required" ]; then
|
|
2612
|
+
required_scopes="$FEISHU_USER_AUTH_REQUESTED_SCOPES"
|
|
2613
|
+
else
|
|
2614
|
+
required_scopes="$FEISHU_USER_AUTH_CORE_SCOPES"
|
|
2615
|
+
fi
|
|
2616
|
+
lark_cli_user_auth_satisfied "$profile" "$required_scopes" || return 1
|
|
2617
|
+
persist_lark_cli_auth_capability_snapshot "$profile"
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2255
2620
|
ensure_lark_cli_profile_locked() {
|
|
2256
2621
|
local app_id="$1"
|
|
2257
2622
|
local app_secret="$2"
|
|
2258
2623
|
local profile="$3"
|
|
2259
2624
|
local auth_excludes
|
|
2625
|
+
local required_scopes
|
|
2626
|
+
|
|
2627
|
+
initialize_feishu_scope_manifest
|
|
2628
|
+
validate_feishu_user_auth_mode
|
|
2260
2629
|
|
|
2261
2630
|
if "$LARK_CLI_CMD" profile list 2>/dev/null | grep -F "\"$profile\"" >/dev/null 2>&1; then
|
|
2262
2631
|
agent_detail "lark-cli profile already exists: $profile"
|
|
@@ -2269,13 +2638,38 @@ ensure_lark_cli_profile_locked() {
|
|
|
2269
2638
|
--app-secret-stdin
|
|
2270
2639
|
fi
|
|
2271
2640
|
|
|
2272
|
-
|
|
2641
|
+
if [ "$FEISHU_USER_AUTH_MODE" = "disabled" ]; then
|
|
2642
|
+
persist_lark_cli_auth_capability_snapshot "$profile"
|
|
2643
|
+
agent_log "Task bridge ready; optional user capabilities disabled for profile: $profile"
|
|
2644
|
+
return 0
|
|
2645
|
+
fi
|
|
2646
|
+
|
|
2273
2647
|
auth_excludes="$FEISHU_USER_AUTH_EXCLUDES"
|
|
2274
|
-
if
|
|
2275
|
-
|
|
2276
|
-
|
|
2648
|
+
if [ "$FEISHU_USER_AUTH_MODE" = "required" ]; then
|
|
2649
|
+
required_scopes="$FEISHU_USER_AUTH_REQUESTED_SCOPES"
|
|
2650
|
+
agent_detail "ensuring explicit lark-cli user auth scopes for profile: $profile"
|
|
2651
|
+
if ! lark_cli_user_auth_satisfied "$profile" "$required_scopes"; then
|
|
2652
|
+
run_lark_cli_auth_login "$profile" "$auth_excludes"
|
|
2653
|
+
lark_cli_user_auth_satisfied "$profile" "$required_scopes" \
|
|
2654
|
+
|| agent_fail "lark-cli user authorization did not grant the required explicit scopes"
|
|
2655
|
+
fi
|
|
2656
|
+
persist_lark_cli_auth_capability_snapshot "$profile"
|
|
2657
|
+
agent_detail "lark-cli user auth has the requested scopes for profile: $profile"
|
|
2658
|
+
return 0
|
|
2659
|
+
fi
|
|
2660
|
+
|
|
2661
|
+
if ! lark_cli_user_auth_satisfied "$profile" "$FEISHU_USER_AUTH_CORE_SCOPES"; then
|
|
2662
|
+
agent_detail "ensuring explicitly configured lark-cli core scopes for profile: $profile"
|
|
2277
2663
|
run_lark_cli_auth_login "$profile" "$auth_excludes"
|
|
2664
|
+
lark_cli_user_auth_satisfied "$profile" "$FEISHU_USER_AUTH_CORE_SCOPES" \
|
|
2665
|
+
|| agent_fail "lark-cli user authorization did not grant the configured core scopes"
|
|
2278
2666
|
fi
|
|
2667
|
+
if lark_cli_user_auth_satisfied "$profile" "$FEISHU_USER_AUTH_OPTIONAL_SCOPES"; then
|
|
2668
|
+
agent_detail "lark-cli optional user capabilities are available for profile: $profile"
|
|
2669
|
+
else
|
|
2670
|
+
agent_log "Task bridge ready; optional user capabilities unavailable for profile: $profile"
|
|
2671
|
+
fi
|
|
2672
|
+
persist_lark_cli_auth_capability_snapshot "$profile"
|
|
2279
2673
|
}
|
|
2280
2674
|
|
|
2281
2675
|
forget_current_bot_after_feishu_start_failure() {
|
|
@@ -2376,6 +2770,8 @@ register_feishu_app() {
|
|
|
2376
2770
|
local default_name
|
|
2377
2771
|
local bot_name
|
|
2378
2772
|
|
|
2773
|
+
initialize_feishu_scope_manifest
|
|
2774
|
+
|
|
2379
2775
|
workdir="$(mktemp -d "${TMPDIR:-/tmp}/aamp-register-feishu-app.XXXXXX")"
|
|
2380
2776
|
register_script="$workdir/register-app.mjs"
|
|
2381
2777
|
if [ -n "$AAMP_RUN_LOG_DIR" ]; then
|
|
@@ -2523,8 +2919,13 @@ NODE
|
|
|
2523
2919
|
|
|
2524
2920
|
bot_name="${bot_name:-$default_name}"
|
|
2525
2921
|
BOT_NAME="$bot_name"
|
|
2526
|
-
|
|
2527
|
-
|
|
2922
|
+
if [ "$AGENT_EXECUTION_LOCATION" = "remote" ]; then
|
|
2923
|
+
LARK_CLI_PROFILE=""
|
|
2924
|
+
else
|
|
2925
|
+
LARK_CLI_PROFILE="$(task_profile_name_for_app_id "$APP_ID")"
|
|
2926
|
+
source_lark_env
|
|
2927
|
+
ensure_lark_cli_profile "$APP_ID" "$APP_SECRET" "$LARK_CLI_PROFILE"
|
|
2928
|
+
fi
|
|
2528
2929
|
if [ "$AAMP_TASK_INTERNAL" = "true" ]; then
|
|
2529
2930
|
agent_detail "Feishu Bot 已授权:$BOT_NAME ($APP_ID)"
|
|
2530
2931
|
return 0
|
|
@@ -2678,6 +3079,338 @@ find_cursor_agent_cli() {
|
|
|
2678
3079
|
return 1
|
|
2679
3080
|
}
|
|
2680
3081
|
|
|
3082
|
+
resolve_trae_cli_candidate() {
|
|
3083
|
+
local candidate
|
|
3084
|
+
candidate="$1"
|
|
3085
|
+
[ -n "$candidate" ] || return 1
|
|
3086
|
+
if command -v "$candidate" >/dev/null 2>&1; then
|
|
3087
|
+
command -v "$candidate"
|
|
3088
|
+
return 0
|
|
3089
|
+
fi
|
|
3090
|
+
if [ -x "$candidate" ]; then
|
|
3091
|
+
printf '%s\n' "$candidate"
|
|
3092
|
+
return 0
|
|
3093
|
+
fi
|
|
3094
|
+
return 1
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
find_traex_cli_candidates() {
|
|
3098
|
+
local candidate resolved emitted=""
|
|
3099
|
+
local candidates=()
|
|
3100
|
+
if [ -n "$TRAE_CLI_BIN" ] && [ "$(basename "$TRAE_CLI_BIN")" = "traex" ]; then candidates+=("$TRAE_CLI_BIN"); fi
|
|
3101
|
+
if [ -n "$AAMP_TRAE_CLI_BIN" ] && [ "$(basename "$AAMP_TRAE_CLI_BIN")" = "traex" ]; then candidates+=("$AAMP_TRAE_CLI_BIN"); fi
|
|
3102
|
+
candidates+=("traex")
|
|
3103
|
+
|
|
3104
|
+
for candidate in "${candidates[@]}"; do
|
|
3105
|
+
resolved="$(resolve_trae_cli_candidate "$candidate" || true)"
|
|
3106
|
+
[ -n "$resolved" ] || continue
|
|
3107
|
+
case "$emitted" in
|
|
3108
|
+
*"|$resolved|"*) continue ;;
|
|
3109
|
+
esac
|
|
3110
|
+
emitted="${emitted}|$resolved|"
|
|
3111
|
+
printf '%s\n' "$resolved"
|
|
3112
|
+
done
|
|
3113
|
+
}
|
|
3114
|
+
|
|
3115
|
+
find_coco_cli_candidates() {
|
|
3116
|
+
local candidate resolved emitted=""
|
|
3117
|
+
local candidates=()
|
|
3118
|
+
if [ -n "$TRAE_CLI_BIN" ] && [ "$(basename "$TRAE_CLI_BIN")" = "coco" ]; then candidates+=("$TRAE_CLI_BIN"); fi
|
|
3119
|
+
if [ -n "$AAMP_TRAE_CLI_BIN" ] && [ "$(basename "$AAMP_TRAE_CLI_BIN")" = "coco" ]; then candidates+=("$AAMP_TRAE_CLI_BIN"); fi
|
|
3120
|
+
candidates+=("coco")
|
|
3121
|
+
for candidate in "${candidates[@]}"; do
|
|
3122
|
+
resolved="$(resolve_trae_cli_candidate "$candidate" || true)"
|
|
3123
|
+
[ -n "$resolved" ] || continue
|
|
3124
|
+
case "$emitted" in *"|$resolved|"*) continue ;; esac
|
|
3125
|
+
emitted="${emitted}|$resolved|"
|
|
3126
|
+
printf '%s\n' "$resolved"
|
|
3127
|
+
done
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3130
|
+
find_traecode_cli_candidates() {
|
|
3131
|
+
local candidate resolved emitted=""
|
|
3132
|
+
local candidates=()
|
|
3133
|
+
[ -n "$TRAECODE_CLI_BIN" ] && candidates+=("$TRAECODE_CLI_BIN")
|
|
3134
|
+
[ -n "$AAMP_TRAECODE_CLI_BIN" ] && candidates+=("$AAMP_TRAECODE_CLI_BIN")
|
|
3135
|
+
candidates+=("traecli")
|
|
3136
|
+
for candidate in "${candidates[@]}"; do
|
|
3137
|
+
resolved="$(resolve_trae_cli_candidate "$candidate" || true)"
|
|
3138
|
+
[ -n "$resolved" ] || continue
|
|
3139
|
+
case "$emitted" in *"|$resolved|"*) continue ;; esac
|
|
3140
|
+
emitted="${emitted}|$resolved|"
|
|
3141
|
+
printf '%s\n' "$resolved"
|
|
3142
|
+
done
|
|
3143
|
+
}
|
|
3144
|
+
|
|
3145
|
+
real_executable_path() {
|
|
3146
|
+
node -e 'const fs=require("fs"); try { process.stdout.write(fs.realpathSync(process.argv[1])); } catch { process.exit(1); }' "$1" 2>/dev/null
|
|
3147
|
+
}
|
|
3148
|
+
|
|
3149
|
+
find_traex_cli() {
|
|
3150
|
+
local candidate
|
|
3151
|
+
candidate="$(find_traex_cli_candidates | head -n 1)"
|
|
3152
|
+
if [ -n "$candidate" ]; then
|
|
3153
|
+
TRAE_CLI_BIN="$candidate"
|
|
3154
|
+
printf '%s\n' "$candidate"
|
|
3155
|
+
return 0
|
|
3156
|
+
fi
|
|
3157
|
+
return 1
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
find_coco_cli() {
|
|
3161
|
+
local candidate
|
|
3162
|
+
candidate="$(find_coco_cli_candidates | head -n 1)"
|
|
3163
|
+
[ -n "$candidate" ] || return 1
|
|
3164
|
+
TRAE_CLI_BIN="$candidate"
|
|
3165
|
+
printf '%s\n' "$candidate"
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
traecode_cli_conflicts_with_coco() {
|
|
3169
|
+
local traecli_bin="$1" coco_bin traecli_real coco_real
|
|
3170
|
+
coco_bin="$(find_coco_cli || true)"
|
|
3171
|
+
[ -n "$coco_bin" ] || return 1
|
|
3172
|
+
traecli_real="$(real_executable_path "$traecli_bin" || true)"
|
|
3173
|
+
coco_real="$(real_executable_path "$coco_bin" || true)"
|
|
3174
|
+
[ -n "$traecli_real" ] && [ "$traecli_real" = "$coco_real" ]
|
|
3175
|
+
}
|
|
3176
|
+
|
|
3177
|
+
find_traecode_cli() {
|
|
3178
|
+
local candidate explicit_candidate="" conflict_found="false"
|
|
3179
|
+
if [ -n "$TRAECODE_CLI_BIN" ]; then explicit_candidate="$TRAECODE_CLI_BIN"; fi
|
|
3180
|
+
if [ -n "$AAMP_TRAECODE_CLI_BIN" ]; then explicit_candidate="$AAMP_TRAECODE_CLI_BIN"; fi
|
|
3181
|
+
if [ -n "$explicit_candidate" ]; then
|
|
3182
|
+
case "$(basename "$explicit_candidate")" in coco|traex) return 2 ;; esac
|
|
3183
|
+
candidate="$(resolve_trae_cli_candidate "$explicit_candidate" || true)"
|
|
3184
|
+
[ -n "$candidate" ] || return 1
|
|
3185
|
+
if traecode_cli_conflicts_with_coco "$candidate"; then return 2; fi
|
|
3186
|
+
TRAECODE_CLI_BIN="$candidate"
|
|
3187
|
+
printf '%s\n' "$candidate"
|
|
3188
|
+
return 0
|
|
3189
|
+
fi
|
|
3190
|
+
while IFS= read -r candidate; do
|
|
3191
|
+
[ -n "$candidate" ] || continue
|
|
3192
|
+
if traecode_cli_conflicts_with_coco "$candidate"; then
|
|
3193
|
+
if [ -n "$explicit_candidate" ] && [ "$candidate" = "$explicit_candidate" ]; then return 2; fi
|
|
3194
|
+
conflict_found="true"
|
|
3195
|
+
continue
|
|
3196
|
+
fi
|
|
3197
|
+
TRAECODE_CLI_BIN="$candidate"
|
|
3198
|
+
printf '%s\n' "$candidate"
|
|
3199
|
+
return 0
|
|
3200
|
+
done < <(find_traecode_cli_candidates)
|
|
3201
|
+
[ "$conflict_found" != "true" ] || return 2
|
|
3202
|
+
return 1
|
|
3203
|
+
}
|
|
3204
|
+
|
|
3205
|
+
find_legacy_trae_cli() {
|
|
3206
|
+
find_coco_cli
|
|
3207
|
+
}
|
|
3208
|
+
|
|
3209
|
+
resolve_trae_cli() {
|
|
3210
|
+
case "${AGENT:-}" in
|
|
3211
|
+
traex) find_traex_cli ;;
|
|
3212
|
+
coco) find_coco_cli ;;
|
|
3213
|
+
traecli) find_traecode_cli ;;
|
|
3214
|
+
*) return 1 ;;
|
|
3215
|
+
esac
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
find_workbuddy_cli() {
|
|
3219
|
+
is_macos || return 1
|
|
3220
|
+
[ -x "$WORKBUDDY_APP_CLI" ] || return 1
|
|
3221
|
+
printf '%s\n' "$WORKBUDDY_APP_CLI"
|
|
3222
|
+
}
|
|
3223
|
+
|
|
3224
|
+
find_workbuddy_ai_cli() {
|
|
3225
|
+
is_macos || return 1
|
|
3226
|
+
[ -x "$WORKBUDDY_AI_APP_CLI" ] || return 1
|
|
3227
|
+
printf '%s\n' "$WORKBUDDY_AI_APP_CLI"
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3230
|
+
aime_acp_package_spec() {
|
|
3231
|
+
local default_spec registry spec
|
|
3232
|
+
spec="${AIME_ACP_PKG:-}"
|
|
3233
|
+
default_spec="$(aime_acp_default_package_spec)"
|
|
3234
|
+
registry="${AIME_ACP_REGISTRY:-https://bnpm.byted.org}"
|
|
3235
|
+
case "$spec" in
|
|
3236
|
+
@*/aime-acp@?*)
|
|
3237
|
+
if [ "${registry%/}" = 'https://bnpm.byted.org' ]; then
|
|
3238
|
+
printf '%s\n' "$spec"
|
|
3239
|
+
else
|
|
3240
|
+
printf '%s\n' "$default_spec"
|
|
3241
|
+
fi
|
|
3242
|
+
;;
|
|
3243
|
+
*.tgz)
|
|
3244
|
+
if [ -f "$spec" ]; then
|
|
3245
|
+
printf '%s\n' "$spec"
|
|
3246
|
+
else
|
|
3247
|
+
printf '%s\n' "$default_spec"
|
|
3248
|
+
fi
|
|
3249
|
+
;;
|
|
3250
|
+
*) printf '%s\n' "$default_spec" ;;
|
|
3251
|
+
esac
|
|
3252
|
+
}
|
|
3253
|
+
|
|
3254
|
+
aime_acp_default_package_spec() {
|
|
3255
|
+
printf '%s\n' "${AAMP_TASK_DEFAULT_AIME_ACP_PKG:-@tengchengwei/aime-acp@0.1.1-dev.1}"
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
aime_acp_local_package_name() {
|
|
3259
|
+
local spec="$1"
|
|
3260
|
+
tar -xOzf "$spec" package/package.json 2>/dev/null | node -e '
|
|
3261
|
+
let raw = "";
|
|
3262
|
+
process.stdin.setEncoding("utf8");
|
|
3263
|
+
process.stdin.on("data", (chunk) => { raw += chunk; });
|
|
3264
|
+
process.stdin.on("end", () => {
|
|
3265
|
+
try {
|
|
3266
|
+
const value = JSON.parse(raw);
|
|
3267
|
+
if (typeof value.name !== "string" || !/^@[^/]+\/aime-acp$/.test(value.name)) process.exit(1);
|
|
3268
|
+
process.stdout.write(value.name);
|
|
3269
|
+
} catch {
|
|
3270
|
+
process.exit(1);
|
|
3271
|
+
}
|
|
3272
|
+
});
|
|
3273
|
+
'
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
aime_acp_package_name() {
|
|
3277
|
+
local local_name spec
|
|
3278
|
+
spec="$(aime_acp_package_spec)"
|
|
3279
|
+
case "$spec" in
|
|
3280
|
+
*.tgz)
|
|
3281
|
+
local_name="$(aime_acp_local_package_name "$spec" || true)"
|
|
3282
|
+
if [ -n "$local_name" ]; then
|
|
3283
|
+
printf '%s\n' "$local_name"
|
|
3284
|
+
return 0
|
|
3285
|
+
fi
|
|
3286
|
+
spec="$(aime_acp_default_package_spec)"
|
|
3287
|
+
;;
|
|
3288
|
+
esac
|
|
3289
|
+
case "$spec" in
|
|
3290
|
+
@*/aime-acp@?*) printf '%s\n' "${spec%@*}" ;;
|
|
3291
|
+
*) return 1 ;;
|
|
3292
|
+
esac
|
|
3293
|
+
}
|
|
3294
|
+
|
|
3295
|
+
aime_acp_package_version() {
|
|
3296
|
+
local spec
|
|
3297
|
+
spec="$(aime_acp_package_spec)"
|
|
3298
|
+
case "$spec" in
|
|
3299
|
+
*.tgz) return 1 ;;
|
|
3300
|
+
*@?*) printf '%s\n' "${spec##*@}" ;;
|
|
3301
|
+
*) return 1 ;;
|
|
3302
|
+
esac
|
|
3303
|
+
}
|
|
3304
|
+
|
|
3305
|
+
aime_acp_package_spec_is_supported() {
|
|
3306
|
+
case "$(aime_acp_package_spec)" in
|
|
3307
|
+
*.tgz) return 0 ;;
|
|
3308
|
+
@*/aime-acp@?*) return 0 ;;
|
|
3309
|
+
*) return 1 ;;
|
|
3310
|
+
esac
|
|
3311
|
+
}
|
|
3312
|
+
|
|
3313
|
+
aime_acp_registry() {
|
|
3314
|
+
printf '%s\n' 'https://bnpm.byted.org'
|
|
3315
|
+
}
|
|
3316
|
+
|
|
3317
|
+
aime_acp_site() {
|
|
3318
|
+
printf '%s\n' 'cn'
|
|
3319
|
+
}
|
|
3320
|
+
|
|
3321
|
+
aime_acp_cli_path() {
|
|
3322
|
+
printf '%s/dist/bin.js\n' "$(aime_acp_package_dir)"
|
|
3323
|
+
}
|
|
3324
|
+
|
|
3325
|
+
remove_legacy_aime_acp() {
|
|
3326
|
+
local legacy_dir legacy_bin canonical_dir resolved_bin
|
|
3327
|
+
legacy_dir="$NPM_GLOBAL_PREFIX/lib/node_modules/aime-acp"
|
|
3328
|
+
legacy_bin="$NPM_GLOBAL_PREFIX/bin/aime-acp"
|
|
3329
|
+
canonical_dir="$(aime_acp_package_dir)"
|
|
3330
|
+
canonical_dir="$(node -e '
|
|
3331
|
+
const fs = require("node:fs");
|
|
3332
|
+
try { process.stdout.write(fs.realpathSync(process.argv[1])); } catch {}
|
|
3333
|
+
' "$canonical_dir")"
|
|
3334
|
+
if [ -e "$legacy_dir" ] || [ -L "$legacy_dir" ]; then
|
|
3335
|
+
rm -rf -- "$legacy_dir"
|
|
3336
|
+
fi
|
|
3337
|
+
if [ -e "$legacy_bin" ] || [ -L "$legacy_bin" ]; then
|
|
3338
|
+
resolved_bin="$(node -e '
|
|
3339
|
+
const fs = require("node:fs");
|
|
3340
|
+
const path = require("node:path");
|
|
3341
|
+
try { process.stdout.write(fs.realpathSync(process.argv[1])); } catch {}
|
|
3342
|
+
' "$legacy_bin")"
|
|
3343
|
+
if [ -n "$canonical_dir" ] && [ -n "$resolved_bin" ]; then
|
|
3344
|
+
case "$resolved_bin" in
|
|
3345
|
+
"$canonical_dir"/*) return 0 ;;
|
|
3346
|
+
esac
|
|
3347
|
+
fi
|
|
3348
|
+
rm -f -- "$legacy_bin"
|
|
3349
|
+
fi
|
|
3350
|
+
}
|
|
3351
|
+
|
|
3352
|
+
aime_acp_package_dir() {
|
|
3353
|
+
printf '%s/lib/node_modules/%s\n' "$NPM_GLOBAL_PREFIX" "$(aime_acp_package_name)"
|
|
3354
|
+
}
|
|
3355
|
+
|
|
3356
|
+
aime_acp_installed_version() {
|
|
3357
|
+
local package_json
|
|
3358
|
+
package_json="$(aime_acp_package_dir)/package.json"
|
|
3359
|
+
[ -r "$package_json" ] || return 1
|
|
3360
|
+
node -e '
|
|
3361
|
+
const fs = require("node:fs");
|
|
3362
|
+
const value = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
|
3363
|
+
if (typeof value.version !== "string") process.exit(1);
|
|
3364
|
+
process.stdout.write(value.version);
|
|
3365
|
+
' "$package_json"
|
|
3366
|
+
}
|
|
3367
|
+
|
|
3368
|
+
aime_acp_installed_name() {
|
|
3369
|
+
local package_json
|
|
3370
|
+
package_json="$(aime_acp_package_dir)/package.json"
|
|
3371
|
+
[ -r "$package_json" ] || return 1
|
|
3372
|
+
node -e '
|
|
3373
|
+
const fs = require("node:fs");
|
|
3374
|
+
const value = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
|
3375
|
+
if (typeof value.name !== "string") process.exit(1);
|
|
3376
|
+
process.stdout.write(value.name);
|
|
3377
|
+
' "$package_json"
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3380
|
+
aime_acp_install_is_valid() {
|
|
3381
|
+
local installed_version installed_name
|
|
3382
|
+
[ -x "$(aime_acp_cli_path)" ] || return 1
|
|
3383
|
+
installed_name="$(aime_acp_installed_name || true)"
|
|
3384
|
+
[ "$installed_name" = "$(aime_acp_package_name)" ] || return 1
|
|
3385
|
+
installed_version="$(aime_acp_installed_version || true)"
|
|
3386
|
+
[ -n "$installed_version" ] || return 1
|
|
3387
|
+
case "$(aime_acp_package_spec)" in
|
|
3388
|
+
*.tgz) return 0 ;;
|
|
3389
|
+
*) [ "$installed_version" = "$(aime_acp_package_version)" ] ;;
|
|
3390
|
+
esac
|
|
3391
|
+
}
|
|
3392
|
+
|
|
3393
|
+
aime_acp_install_is_current() {
|
|
3394
|
+
case "$(aime_acp_package_spec)" in
|
|
3395
|
+
*.tgz) return 1 ;;
|
|
3396
|
+
*) aime_acp_install_is_valid ;;
|
|
3397
|
+
esac
|
|
3398
|
+
}
|
|
3399
|
+
|
|
3400
|
+
ensure_aime_acp_cli() {
|
|
3401
|
+
aime_acp_package_spec_is_supported \
|
|
3402
|
+
|| agent_fail "AIME_ACP_PACKAGE_INVALID: AIME ACP must use a scoped BNPM package or a local tgz."
|
|
3403
|
+
remove_legacy_aime_acp
|
|
3404
|
+
if aime_acp_install_is_current; then
|
|
3405
|
+
return 0
|
|
3406
|
+
fi
|
|
3407
|
+
agent_log "正在安装固定版本 AIME ACP 到隔离运行环境。"
|
|
3408
|
+
npm_install_global_from_registry "$(aime_acp_registry)" "$(aime_acp_package_spec)" \
|
|
3409
|
+
|| agent_fail "AIME_ACP_INSTALL_FAILED: AIME ACP installation failed. Confirm company network access and retry."
|
|
3410
|
+
aime_acp_install_is_valid \
|
|
3411
|
+
|| agent_fail "AIME_ACP_INSTALL_INVALID: AIME ACP installation validation failed."
|
|
3412
|
+
}
|
|
3413
|
+
|
|
2681
3414
|
resolve_cursor_cli_for_acp() {
|
|
2682
3415
|
command -v cursor
|
|
2683
3416
|
}
|
|
@@ -2720,6 +3453,50 @@ ensure_agent_cli() {
|
|
|
2720
3453
|
return 0
|
|
2721
3454
|
fi
|
|
2722
3455
|
|
|
3456
|
+
if [ "$AGENT" = "traex" ]; then
|
|
3457
|
+
find_traex_cli >/dev/null 2>&1 || agent_fail "未检测到 traex CLI。请先安装 Trae CLI Next(内部版)后重新运行脚本。"
|
|
3458
|
+
return 0
|
|
3459
|
+
fi
|
|
3460
|
+
|
|
3461
|
+
if [ "$AGENT" = "traecli" ]; then
|
|
3462
|
+
local traecode_status
|
|
3463
|
+
set +e
|
|
3464
|
+
find_traecode_cli >/dev/null 2>&1
|
|
3465
|
+
traecode_status=$?
|
|
3466
|
+
set -e
|
|
3467
|
+
[ "$traecode_status" -ne 2 ] || agent_fail "检测到的 traecli 属于 Trae CLI(内部版),不能作为 TraeCode CLI 启动。"
|
|
3468
|
+
[ "$traecode_status" -eq 0 ] || agent_fail "未检测到 TraeCode CLI。请先安装 traecli 后重新运行脚本。"
|
|
3469
|
+
return 0
|
|
3470
|
+
fi
|
|
3471
|
+
|
|
3472
|
+
if [ "$AGENT" = "coco" ]; then
|
|
3473
|
+
if find_traex_cli >/dev/null 2>&1; then return 0; fi
|
|
3474
|
+
if find_coco_cli >/dev/null 2>&1; then return 0; fi
|
|
3475
|
+
if find_traecode_cli >/dev/null 2>&1; then return 0; fi
|
|
3476
|
+
agent_fail "未检测到 Trae CLI(内部版)、Trae CLI Next(内部版)或 TraeCode CLI。"
|
|
3477
|
+
fi
|
|
3478
|
+
|
|
3479
|
+
if [ "$AGENT" = "workbuddy" ]; then
|
|
3480
|
+
is_macos || agent_fail "WorkBuddy 一键探测仅支持 macOS。"
|
|
3481
|
+
find_workbuddy_cli >/dev/null 2>&1 \
|
|
3482
|
+
|| agent_fail "未检测到 WorkBuddy。请确认已安装到 /Applications/WorkBuddy.app 后重新运行脚本。"
|
|
3483
|
+
return 0
|
|
3484
|
+
fi
|
|
3485
|
+
|
|
3486
|
+
if [ "$AGENT" = "workbuddy_ai" ]; then
|
|
3487
|
+
is_macos || agent_fail "WorkBuddy AI 一键探测仅支持 macOS。"
|
|
3488
|
+
find_workbuddy_ai_cli >/dev/null 2>&1 \
|
|
3489
|
+
|| agent_fail "未检测到 WorkBuddy AI CLI:${WORKBUDDY_AI_APP_CLI}。请确认该文件存在且可执行后重新运行脚本。"
|
|
3490
|
+
return 0
|
|
3491
|
+
fi
|
|
3492
|
+
|
|
3493
|
+
if [ "$AGENT" = "aime" ]; then
|
|
3494
|
+
aime_internal_network_reachable \
|
|
3495
|
+
|| agent_fail "AIME 仅在公司内网可用;当前无法 ping 通 aime.bytedance.net。"
|
|
3496
|
+
ensure_aime_acp_cli
|
|
3497
|
+
return 0
|
|
3498
|
+
fi
|
|
3499
|
+
|
|
2723
3500
|
command -v "$AGENT" >/dev/null 2>&1 || agent_fail "未检测到 $AGENT CLI。请先安装后重新运行脚本。"
|
|
2724
3501
|
}
|
|
2725
3502
|
|
|
@@ -2867,6 +3644,68 @@ resolve_latest_codex_cli_version() {
|
|
|
2867
3644
|
--prefix "$NPM_GLOBAL_PREFIX" 2>>"$ONE_CLICK_LOG" | tail -n 1 | tr -d '[:space:]'
|
|
2868
3645
|
}
|
|
2869
3646
|
|
|
3647
|
+
codex_update_cache_latest() {
|
|
3648
|
+
local current_version="$1"
|
|
3649
|
+
[ -n "${CODEX_UPDATE_CACHE_FILE:-}" ] || return 1
|
|
3650
|
+
[ -n "$current_version" ] || return 1
|
|
3651
|
+
[ -f "$CODEX_UPDATE_CACHE_FILE" ] || return 1
|
|
3652
|
+
CACHE_FILE="$CODEX_UPDATE_CACHE_FILE" \
|
|
3653
|
+
TTL="${CODEX_UPDATE_CACHE_TTL_SECONDS:-86400}" \
|
|
3654
|
+
CURRENT_VERSION="$current_version" \
|
|
3655
|
+
NPM_PACKAGE="${CODEX_NPM_PACKAGE:-@openai/codex}" \
|
|
3656
|
+
NPM_REGISTRY_VALUE="${NPM_REGISTRY:-https://registry.npmjs.org/}" \
|
|
3657
|
+
node -e '
|
|
3658
|
+
const fs = require("fs");
|
|
3659
|
+
let data;
|
|
3660
|
+
try { data = JSON.parse(fs.readFileSync(process.env.CACHE_FILE, "utf8")); } catch { process.exit(1); }
|
|
3661
|
+
const checkedAt = Number(data.checked_at || 0);
|
|
3662
|
+
const ttl = Number(process.env.TTL || 86400);
|
|
3663
|
+
const age = Math.floor(Date.now() / 1000) - checkedAt;
|
|
3664
|
+
if (data.version !== 1 || typeof data.checked_at !== "number" || !Number.isFinite(checkedAt) || checkedAt <= 0) process.exit(1);
|
|
3665
|
+
if (!Number.isFinite(ttl) || ttl <= 0 || age < 0 || age >= ttl) process.exit(1);
|
|
3666
|
+
if (typeof data.current_version !== "string" || typeof data.latest_version !== "string") process.exit(1);
|
|
3667
|
+
if (typeof data.package !== "string" || typeof data.registry !== "string") process.exit(1);
|
|
3668
|
+
if (String(data.current_version || "") !== String(process.env.CURRENT_VERSION || "")) process.exit(1);
|
|
3669
|
+
if (String(data.package || "") !== String(process.env.NPM_PACKAGE || "")) process.exit(1);
|
|
3670
|
+
if (String(data.registry || "") !== String(process.env.NPM_REGISTRY_VALUE || "")) process.exit(1);
|
|
3671
|
+
const latest = String(data.latest_version || "").trim();
|
|
3672
|
+
if (!/^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(latest)) process.exit(1);
|
|
3673
|
+
process.stdout.write(latest);
|
|
3674
|
+
' 2>>"${ONE_CLICK_LOG:-/dev/null}"
|
|
3675
|
+
}
|
|
3676
|
+
|
|
3677
|
+
write_codex_update_cache() {
|
|
3678
|
+
local current_version="$1"
|
|
3679
|
+
local latest_version="$2"
|
|
3680
|
+
[ -n "${CODEX_UPDATE_CACHE_FILE:-}" ] || return 0
|
|
3681
|
+
[ -n "$current_version" ] && [ -n "$latest_version" ] || return 0
|
|
3682
|
+
mkdir -p "$(dirname "$CODEX_UPDATE_CACHE_FILE")"
|
|
3683
|
+
CACHE_FILE="$CODEX_UPDATE_CACHE_FILE" \
|
|
3684
|
+
CURRENT_VERSION="$current_version" \
|
|
3685
|
+
LATEST_VERSION="$latest_version" \
|
|
3686
|
+
NPM_PACKAGE="${CODEX_NPM_PACKAGE:-@openai/codex}" \
|
|
3687
|
+
NPM_REGISTRY_VALUE="${NPM_REGISTRY:-https://registry.npmjs.org/}" \
|
|
3688
|
+
node -e '
|
|
3689
|
+
const fs = require("fs");
|
|
3690
|
+
const file = process.env.CACHE_FILE;
|
|
3691
|
+
const temporary = `${file}.tmp.${process.pid}`;
|
|
3692
|
+
const payload = {
|
|
3693
|
+
version: 1,
|
|
3694
|
+
checked_at: Math.floor(Date.now() / 1000),
|
|
3695
|
+
current_version: process.env.CURRENT_VERSION || "",
|
|
3696
|
+
latest_version: process.env.LATEST_VERSION || "",
|
|
3697
|
+
package: process.env.NPM_PACKAGE || "",
|
|
3698
|
+
registry: process.env.NPM_REGISTRY_VALUE || "",
|
|
3699
|
+
};
|
|
3700
|
+
try {
|
|
3701
|
+
fs.writeFileSync(temporary, JSON.stringify(payload, null, 2) + "\n", { mode: 0o600 });
|
|
3702
|
+
fs.renameSync(temporary, file);
|
|
3703
|
+
} finally {
|
|
3704
|
+
try { fs.unlinkSync(temporary); } catch {}
|
|
3705
|
+
}
|
|
3706
|
+
' 2>>"${ONE_CLICK_LOG:-/dev/null}" || true
|
|
3707
|
+
}
|
|
3708
|
+
|
|
2870
3709
|
codex_cli_update_available() {
|
|
2871
3710
|
CODEX_CURRENT_VERSION="$1" CODEX_LATEST_VERSION="$2" node -e '
|
|
2872
3711
|
function parse(value) {
|
|
@@ -3005,7 +3844,13 @@ ensure_codex_cli_updated() {
|
|
|
3005
3844
|
codex_bin="$(resolve_codex_cli_for_acp || true)"
|
|
3006
3845
|
[ -n "$codex_bin" ] || return 0
|
|
3007
3846
|
version_before="$(codex_cli_version_number "$codex_bin" || true)"
|
|
3008
|
-
latest_version="$(
|
|
3847
|
+
latest_version="$(codex_update_cache_latest "$version_before" || true)"
|
|
3848
|
+
if [ -n "$latest_version" ]; then
|
|
3849
|
+
agent_detail "Codex CLI update check cache is fresh: current=${version_before:-unknown} latest=$latest_version"
|
|
3850
|
+
else
|
|
3851
|
+
latest_version="$(resolve_latest_codex_cli_version || true)"
|
|
3852
|
+
write_codex_update_cache "$version_before" "$latest_version"
|
|
3853
|
+
fi
|
|
3009
3854
|
version_line="当前 Codex CLI 版本是:${version_before:-未知},最新版本是:${latest_version:-获取失败}"
|
|
3010
3855
|
write_one_click_log "[aamp-one-click] $version_line"
|
|
3011
3856
|
agent_detail "checking Codex CLI update: path=$codex_bin version=${version_before:-unknown}"
|
|
@@ -3016,7 +3861,6 @@ ensure_codex_cli_updated() {
|
|
|
3016
3861
|
comparison_status=$?
|
|
3017
3862
|
fi
|
|
3018
3863
|
if [ "$comparison_status" -eq 1 ]; then
|
|
3019
|
-
printf '%s\n' "$version_line"
|
|
3020
3864
|
agent_detail "Codex CLI is already current: current=${version_before:-unknown} latest=${latest_version:-unknown}"
|
|
3021
3865
|
return 0
|
|
3022
3866
|
fi
|
|
@@ -3105,6 +3949,142 @@ run_codex_login() {
|
|
|
3105
3949
|
return "$status"
|
|
3106
3950
|
}
|
|
3107
3951
|
|
|
3952
|
+
run_quiet_command_with_timeout() {
|
|
3953
|
+
local timeout_seconds="$1"
|
|
3954
|
+
shift
|
|
3955
|
+
node - "$timeout_seconds" "$@" <<'NODE'
|
|
3956
|
+
const { spawn } = require('node:child_process');
|
|
3957
|
+
|
|
3958
|
+
const timeoutSeconds = Number(process.argv[2] || '10');
|
|
3959
|
+
const timeoutMs = Math.max(1, Number.isFinite(timeoutSeconds) ? timeoutSeconds : 10) * 1000;
|
|
3960
|
+
const [command, ...args] = process.argv.slice(3);
|
|
3961
|
+
if (!command) process.exit(127);
|
|
3962
|
+
|
|
3963
|
+
let finished = false;
|
|
3964
|
+
let timedOut = false;
|
|
3965
|
+
const child = spawn(command, args, { stdio: ['ignore', 'ignore', 'ignore'] });
|
|
3966
|
+
const timer = setTimeout(() => {
|
|
3967
|
+
timedOut = true;
|
|
3968
|
+
child.kill('SIGTERM');
|
|
3969
|
+
setTimeout(() => child.kill('SIGKILL'), 1000).unref();
|
|
3970
|
+
}, timeoutMs);
|
|
3971
|
+
|
|
3972
|
+
child.once('error', (error) => {
|
|
3973
|
+
if (finished) return;
|
|
3974
|
+
finished = true;
|
|
3975
|
+
clearTimeout(timer);
|
|
3976
|
+
process.exit(error && error.code === 'ENOENT' ? 127 : 1);
|
|
3977
|
+
});
|
|
3978
|
+
|
|
3979
|
+
child.once('exit', (code, signal) => {
|
|
3980
|
+
if (finished) return;
|
|
3981
|
+
finished = true;
|
|
3982
|
+
clearTimeout(timer);
|
|
3983
|
+
if (timedOut) process.exit(124);
|
|
3984
|
+
if (typeof code === 'number') process.exit(code);
|
|
3985
|
+
process.exit(signal ? 1 : 0);
|
|
3986
|
+
});
|
|
3987
|
+
NODE
|
|
3988
|
+
}
|
|
3989
|
+
|
|
3990
|
+
run_trae_login_status_for_bin() {
|
|
3991
|
+
local trae_bin="$1"
|
|
3992
|
+
run_quiet_command_with_timeout "$AAMP_TRAE_LOGIN_STATUS_TIMEOUT_SECONDS" "$trae_bin" login status
|
|
3993
|
+
}
|
|
3994
|
+
|
|
3995
|
+
trae_login_status_timeout_message() {
|
|
3996
|
+
printf '%s' "traex login status 超时。请手动执行 'traex login status' 检查登录状态后重新运行脚本。"
|
|
3997
|
+
}
|
|
3998
|
+
|
|
3999
|
+
run_traex_login_status() {
|
|
4000
|
+
local trae_bin status
|
|
4001
|
+
trae_bin="$(find_traex_cli || true)"
|
|
4002
|
+
[ -n "$trae_bin" ] || return 127
|
|
4003
|
+
TRAE_CLI_BIN="$trae_bin"
|
|
4004
|
+
agent_detail "checking Trae CLI login status: $trae_bin"
|
|
4005
|
+
if run_trae_login_status_for_bin "$trae_bin"; then
|
|
4006
|
+
return 0
|
|
4007
|
+
else
|
|
4008
|
+
status=$?
|
|
4009
|
+
fi
|
|
4010
|
+
if [ "$status" -eq 124 ]; then
|
|
4011
|
+
agent_log "Trae CLI login status timed out after ${AAMP_TRAE_LOGIN_STATUS_TIMEOUT_SECONDS}s: $trae_bin"
|
|
4012
|
+
fi
|
|
4013
|
+
return "$status"
|
|
4014
|
+
}
|
|
4015
|
+
|
|
4016
|
+
run_traex_login() {
|
|
4017
|
+
local trae_bin
|
|
4018
|
+
trae_bin="$(resolve_trae_cli || true)"
|
|
4019
|
+
[ -n "$trae_bin" ] || return 127
|
|
4020
|
+
"$trae_bin" login
|
|
4021
|
+
}
|
|
4022
|
+
|
|
4023
|
+
trae_cli_is_traex() {
|
|
4024
|
+
[ -n "$TRAE_CLI_BIN" ] && [ "${TRAE_CLI_BIN##*/}" = "traex" ]
|
|
4025
|
+
}
|
|
4026
|
+
|
|
4027
|
+
confirm_trae_upgrade() {
|
|
4028
|
+
local answer
|
|
4029
|
+
printf '\n检测到 Trae CLI(内部版),飞书任务 Agent 暂不支持使用该版本建立连接。\n' >&2
|
|
4030
|
+
printf '建议升级到 Trae CLI Next(内部版)(traex)后继续。\n' >&2
|
|
4031
|
+
printf '升级过程中可自行选择是否保留 Trae CLI(内部版)。\n' >&2
|
|
4032
|
+
answer="$(read_tty_line "是否现在升级到 traex?[y/N] ")"
|
|
4033
|
+
case "$answer" in
|
|
4034
|
+
y|Y|yes|YES|Yes) return 0 ;;
|
|
4035
|
+
*) return 1 ;;
|
|
4036
|
+
esac
|
|
4037
|
+
}
|
|
4038
|
+
|
|
4039
|
+
run_traex_installer() {
|
|
4040
|
+
agent_log "正在安装/升级 traex..."
|
|
4041
|
+
curl -fsSL "$TRAEX_INSTALLER_URL" | sh
|
|
4042
|
+
}
|
|
4043
|
+
|
|
4044
|
+
maybe_upgrade_legacy_trae_cli() {
|
|
4045
|
+
if trae_cli_is_traex; then
|
|
4046
|
+
AGENT="traex"
|
|
4047
|
+
return 0
|
|
4048
|
+
fi
|
|
4049
|
+
|
|
4050
|
+
if confirm_trae_upgrade; then
|
|
4051
|
+
run_traex_installer || agent_fail "traex 安装失败。请手动执行:curl -fsSL $TRAEX_INSTALLER_URL | sh"
|
|
4052
|
+
hash -r 2>/dev/null || true
|
|
4053
|
+
TRAE_CLI_BIN=""
|
|
4054
|
+
find_traex_cli >/dev/null 2>&1 || agent_fail "traex 安装后仍未检测到。请确认 traex 在 PATH 中,然后重新运行脚本。"
|
|
4055
|
+
AGENT="traex"
|
|
4056
|
+
agent_log "已切换为 traex,继续启动。"
|
|
4057
|
+
return 0
|
|
4058
|
+
fi
|
|
4059
|
+
|
|
4060
|
+
AGENT_PREPARE_CANCELLED="true"
|
|
4061
|
+
AGENT_PREPARE_CANCEL_REASON="用户取消升级。飞书任务 Agent 暂不支持使用 Trae CLI(内部版)建立连接,本次未启动飞书任务连接。如需继续,请先安装 Trae CLI Next(内部版):curl -fsSL $TRAEX_INSTALLER_URL | sh"
|
|
4062
|
+
agent_log "已取消升级,本次未启动飞书任务连接。"
|
|
4063
|
+
}
|
|
4064
|
+
|
|
4065
|
+
ensure_traex_login() {
|
|
4066
|
+
local trae_login_status
|
|
4067
|
+
set +e
|
|
4068
|
+
run_traex_login_status
|
|
4069
|
+
trae_login_status=$?
|
|
4070
|
+
set -e
|
|
4071
|
+
if [ "$trae_login_status" -eq 124 ]; then
|
|
4072
|
+
agent_fail "$(trae_login_status_timeout_message)"
|
|
4073
|
+
fi
|
|
4074
|
+
if [ "$trae_login_status" -ne 0 ]; then
|
|
4075
|
+
agent_log "Trae CLI Next(内部版)未登录,正在启动登录流程。"
|
|
4076
|
+
run_traex_login || agent_fail "Trae CLI Next(内部版)登录失败。请先执行 'traex login' 完成登录后重新运行脚本。"
|
|
4077
|
+
set +e
|
|
4078
|
+
run_traex_login_status
|
|
4079
|
+
trae_login_status=$?
|
|
4080
|
+
set -e
|
|
4081
|
+
if [ "$trae_login_status" -eq 124 ]; then
|
|
4082
|
+
agent_fail "$(trae_login_status_timeout_message)"
|
|
4083
|
+
fi
|
|
4084
|
+
[ "$trae_login_status" -eq 0 ] || agent_fail "Trae CLI Next(内部版)仍未登录。请先执行 'traex login' 完成登录后重新运行脚本。"
|
|
4085
|
+
fi
|
|
4086
|
+
}
|
|
4087
|
+
|
|
3108
4088
|
print_cursor_gatekeeper_help() {
|
|
3109
4089
|
local cursor_bin
|
|
3110
4090
|
local cursor_real
|
|
@@ -3366,6 +4346,144 @@ ensure_codem_provider_ready() {
|
|
|
3366
4346
|
return "$status"
|
|
3367
4347
|
}
|
|
3368
4348
|
|
|
4349
|
+
traecode_readiness_helper_path() {
|
|
4350
|
+
if [ -n "$AAMP_TRAECODE_READINESS_HELPER" ]; then printf '%s\n' "$AAMP_TRAECODE_READINESS_HELPER"; return 0; fi
|
|
4351
|
+
local helper
|
|
4352
|
+
helper="$(task_agent_global_package_dir)/bin/traecode-readiness.mjs"
|
|
4353
|
+
[ -r "$helper" ] || return 1
|
|
4354
|
+
printf '%s\n' "$helper"
|
|
4355
|
+
}
|
|
4356
|
+
|
|
4357
|
+
run_traecode_check() {
|
|
4358
|
+
local action="$1" helper traecode_bin
|
|
4359
|
+
helper="$(traecode_readiness_helper_path)" || return 127
|
|
4360
|
+
traecode_bin="$(find_traecode_cli)" || return $?
|
|
4361
|
+
node "$helper" "$action" "$traecode_bin" "$AAMP_TRAECODE_CHECK_TIMEOUT_SECONDS"
|
|
4362
|
+
}
|
|
4363
|
+
|
|
4364
|
+
confirm_traecode_update() {
|
|
4365
|
+
local answer tty_path
|
|
4366
|
+
tty_path="${AAMP_TRAECODE_UPDATE_TTY:-/dev/tty}"
|
|
4367
|
+
if ! exec 6<>"$tty_path"; then return 2; fi
|
|
4368
|
+
printf '%s' '当前 TraeCode CLI 版本较旧,不支持 ACP。是否执行 `traecli update` 升级?[y/N] ' >&6
|
|
4369
|
+
if ! IFS= read -r answer <&6; then exec 6>&-; return 2; fi
|
|
4370
|
+
exec 6>&-
|
|
4371
|
+
case "$answer" in y|Y|yes|YES|Yes) return 0 ;; *) return 1 ;; esac
|
|
4372
|
+
}
|
|
4373
|
+
|
|
4374
|
+
run_traecode_update() {
|
|
4375
|
+
local traecode_bin="$1"
|
|
4376
|
+
[ -n "$traecode_bin" ] || return 127
|
|
4377
|
+
"$traecode_bin" update
|
|
4378
|
+
}
|
|
4379
|
+
|
|
4380
|
+
ensure_traecode_acp() {
|
|
4381
|
+
local probe_status confirm_status update_bin
|
|
4382
|
+
set +e; run_traecode_check probe-acp >/dev/null; probe_status=$?; set -e
|
|
4383
|
+
[ "$probe_status" -eq 0 ] && return 0
|
|
4384
|
+
[ "$probe_status" -ne 124 ] || agent_fail "TraeCode CLI ACP 能力检查超时,请稍后重试。"
|
|
4385
|
+
[ "$probe_status" -eq 3 ] || agent_fail "无法检查 TraeCode CLI 的 ACP 能力。"
|
|
4386
|
+
|
|
4387
|
+
set +e; confirm_traecode_update; confirm_status=$?; set -e
|
|
4388
|
+
if [ "$confirm_status" -eq 2 ]; then
|
|
4389
|
+
agent_fail "当前 TraeCode CLI 需要升级。请在交互式终端执行 'traecli update' 后重试。"
|
|
4390
|
+
fi
|
|
4391
|
+
if [ "$confirm_status" -ne 0 ]; then
|
|
4392
|
+
AGENT_PREPARE_CANCELLED="true"
|
|
4393
|
+
AGENT_PREPARE_CANCEL_REASON="用户取消升级 TraeCode CLI,本次未启动飞书任务连接。"
|
|
4394
|
+
return 0
|
|
4395
|
+
fi
|
|
4396
|
+
|
|
4397
|
+
update_bin="$(find_traecode_cli)" || agent_fail "升级前无法重新定位 TraeCode CLI,请手动执行 'traecli update' 后重试。"
|
|
4398
|
+
run_traecode_update "$update_bin" || agent_fail "TraeCode CLI 升级失败,请查看上方升级输出,或手动执行 'traecli update' 后重试。"
|
|
4399
|
+
hash -r 2>/dev/null || true
|
|
4400
|
+
TRAECODE_CLI_BIN=""
|
|
4401
|
+
find_traecode_cli >/dev/null 2>&1 || agent_fail "升级后仍未检测到 TraeCode CLI,请检查 PATH 后重试。"
|
|
4402
|
+
set +e; run_traecode_check probe-acp >/dev/null; probe_status=$?; set -e
|
|
4403
|
+
[ "$probe_status" -eq 0 ] || agent_fail "TraeCode CLI 升级后仍不支持 ACP,请确认升级结果后重试。"
|
|
4404
|
+
}
|
|
4405
|
+
|
|
4406
|
+
ensure_traecode_doctor() {
|
|
4407
|
+
local output doctor_status
|
|
4408
|
+
set +e; output="$(run_traecode_check doctor 2>&1)"; doctor_status=$?; set -e
|
|
4409
|
+
case "$doctor_status" in
|
|
4410
|
+
0) [ -z "$output" ] || printf 'TraeCode CLI 诊断警告:%s\n' "$output" ;;
|
|
4411
|
+
10) agent_fail "TraeCode CLI 尚未准备好。请打开 TraeCode CLI,按提示完成登录,并使用 /model 选择模型后重试。" ;;
|
|
4412
|
+
11) agent_fail "TraeCode CLI 诊断未通过:${output:-未知错误}" ;;
|
|
4413
|
+
65) agent_fail "TraeCode CLI 诊断结果无法解析,请执行 'traecli doctor --json' 检查后重试。" ;;
|
|
4414
|
+
124) agent_fail "TraeCode CLI 诊断超时,请稍后重试。" ;;
|
|
4415
|
+
*) agent_fail "TraeCode CLI 诊断执行失败:${output:-请执行 'traecli doctor --json' 检查。}" ;;
|
|
4416
|
+
esac
|
|
4417
|
+
}
|
|
4418
|
+
|
|
4419
|
+
ensure_traecode_ready() {
|
|
4420
|
+
ensure_traecode_acp
|
|
4421
|
+
[ "$AGENT_PREPARE_CANCELLED" = "true" ] && return 0
|
|
4422
|
+
ensure_traecode_doctor
|
|
4423
|
+
}
|
|
4424
|
+
|
|
4425
|
+
run_aime_auth_status() {
|
|
4426
|
+
"$(aime_acp_cli_path)" auth status --site "$(aime_acp_site)" --json
|
|
4427
|
+
}
|
|
4428
|
+
|
|
4429
|
+
run_aime_auth_login() {
|
|
4430
|
+
"$(aime_acp_cli_path)" auth login --site "$(aime_acp_site)"
|
|
4431
|
+
}
|
|
4432
|
+
|
|
4433
|
+
run_aime_doctor() {
|
|
4434
|
+
"$(aime_acp_cli_path)" doctor --site "$(aime_acp_site)" --json
|
|
4435
|
+
}
|
|
4436
|
+
|
|
4437
|
+
ensure_aime_ready() {
|
|
4438
|
+
local auth_output auth_status auth_kind login_status doctor_status
|
|
4439
|
+
set +e
|
|
4440
|
+
auth_output="$(run_aime_auth_status)"
|
|
4441
|
+
auth_status=$?
|
|
4442
|
+
set -e
|
|
4443
|
+
auth_kind="$(printf '%s' "$auth_output" | node -e '
|
|
4444
|
+
let input = "";
|
|
4445
|
+
process.stdin.setEncoding("utf8");
|
|
4446
|
+
process.stdin.on("data", (chunk) => { input += chunk; });
|
|
4447
|
+
process.stdin.on("end", () => {
|
|
4448
|
+
try {
|
|
4449
|
+
const value = JSON.parse(input.trim());
|
|
4450
|
+
if (value?.ok === true && value?.status === "authenticated") process.stdout.write("authenticated");
|
|
4451
|
+
else if (value?.ok === true && value?.status === "unauthenticated") process.stdout.write("unauthenticated");
|
|
4452
|
+
else process.stdout.write("error");
|
|
4453
|
+
} catch {
|
|
4454
|
+
process.stdout.write("error");
|
|
4455
|
+
}
|
|
4456
|
+
});
|
|
4457
|
+
')"
|
|
4458
|
+
if [ "$auth_status" -eq 0 ] && [ "$auth_kind" = "authenticated" ]; then
|
|
4459
|
+
agent_detail "AIME managed-user authentication is ready"
|
|
4460
|
+
elif [ "$auth_status" -eq 1 ] && [ "$auth_kind" = "unauthenticated" ]; then
|
|
4461
|
+
agent_log "AIME 尚未登录,正在启动独立登录流程。"
|
|
4462
|
+
set +e
|
|
4463
|
+
run_aime_auth_login
|
|
4464
|
+
login_status=$?
|
|
4465
|
+
set -e
|
|
4466
|
+
case "$login_status" in
|
|
4467
|
+
0) ;;
|
|
4468
|
+
2)
|
|
4469
|
+
agent_fail "AIME 登录仍在等待完成。请继续完成浏览器授权,或重新执行 'aime-acp auth login --site cn' 后重试。"
|
|
4470
|
+
;;
|
|
4471
|
+
*)
|
|
4472
|
+
agent_fail "AIME 登录失败。请执行 'aime-acp auth login --site cn' 后重试。"
|
|
4473
|
+
;;
|
|
4474
|
+
esac
|
|
4475
|
+
else
|
|
4476
|
+
agent_fail "AIME 认证状态检查失败。请执行 'aime-acp auth status --site cn --json' 查看安全诊断。"
|
|
4477
|
+
fi
|
|
4478
|
+
|
|
4479
|
+
set +e
|
|
4480
|
+
run_aime_doctor >/dev/null
|
|
4481
|
+
doctor_status=$?
|
|
4482
|
+
set -e
|
|
4483
|
+
[ "$doctor_status" -eq 0 ] \
|
|
4484
|
+
|| agent_fail "AIME doctor 未通过。请确认公司内网与账号状态,并执行 'aime-acp doctor --site cn --json' 查看安全诊断。"
|
|
4485
|
+
}
|
|
4486
|
+
|
|
3369
4487
|
ensure_agent_login() {
|
|
3370
4488
|
case "$AGENT" in
|
|
3371
4489
|
codex)
|
|
@@ -3386,6 +4504,35 @@ ensure_agent_login() {
|
|
|
3386
4504
|
run_cursor_login_status || agent_fail "cursor CLI 仍未登录。请先执行 'cursor login' 或 'agent login' 完成登录后重新运行脚本。"
|
|
3387
4505
|
fi
|
|
3388
4506
|
;;
|
|
4507
|
+
traex)
|
|
4508
|
+
ensure_traex_login
|
|
4509
|
+
;;
|
|
4510
|
+
traecli)
|
|
4511
|
+
ensure_traecode_ready
|
|
4512
|
+
;;
|
|
4513
|
+
coco)
|
|
4514
|
+
if find_traex_cli >/dev/null 2>&1; then
|
|
4515
|
+
AGENT="traex"
|
|
4516
|
+
ensure_traex_login
|
|
4517
|
+
elif find_coco_cli >/dev/null 2>&1; then
|
|
4518
|
+
maybe_upgrade_legacy_trae_cli
|
|
4519
|
+
if [ "$AGENT" = "traex" ]; then ensure_traex_login; fi
|
|
4520
|
+
elif find_traecode_cli >/dev/null 2>&1; then
|
|
4521
|
+
AGENT="traecli"
|
|
4522
|
+
ensure_traecode_ready
|
|
4523
|
+
else
|
|
4524
|
+
agent_fail "未检测到 Trae CLI(内部版)、Trae CLI Next(内部版)或 TraeCode CLI。"
|
|
4525
|
+
fi
|
|
4526
|
+
;;
|
|
4527
|
+
workbuddy)
|
|
4528
|
+
agent_detail "WorkBuddy authentication is managed by the desktop app"
|
|
4529
|
+
;;
|
|
4530
|
+
workbuddy_ai)
|
|
4531
|
+
agent_detail "WorkBuddy AI authentication is managed by the desktop app"
|
|
4532
|
+
;;
|
|
4533
|
+
aime)
|
|
4534
|
+
ensure_aime_ready
|
|
4535
|
+
;;
|
|
3389
4536
|
codem)
|
|
3390
4537
|
ensure_codem_local_bin_on_path
|
|
3391
4538
|
codem --version >/dev/null
|
|
@@ -3467,8 +4614,69 @@ resolve_codex_cli_for_acp() {
|
|
|
3467
4614
|
return 1
|
|
3468
4615
|
}
|
|
3469
4616
|
|
|
4617
|
+
acp_command_word() {
|
|
4618
|
+
local value="$1" single_quote_escape
|
|
4619
|
+
case "$value" in *$'\n'*|*$'\r'*) return 1 ;; esac
|
|
4620
|
+
single_quote_escape="'\"'\"'"
|
|
4621
|
+
value="${value//\'/$single_quote_escape}"
|
|
4622
|
+
printf "'%s'" "$value"
|
|
4623
|
+
}
|
|
4624
|
+
|
|
3470
4625
|
build_acp_agent_command() {
|
|
3471
4626
|
ACP_AGENT_COMMAND="$AGENT"
|
|
4627
|
+
if [ "$AGENT" = "workbuddy" ]; then
|
|
4628
|
+
local workbuddy_bin workbuddy_config_dir_word
|
|
4629
|
+
workbuddy_bin="$(find_workbuddy_cli)" \
|
|
4630
|
+
|| agent_fail "WorkBuddy 不可用。请确认已安装到 /Applications/WorkBuddy.app。"
|
|
4631
|
+
workbuddy_config_dir_word="$(acp_command_word "$HOME/.workbuddy")" \
|
|
4632
|
+
|| agent_fail "WorkBuddy 配置目录包含不受支持的换行符。"
|
|
4633
|
+
ACP_AGENT_COMMAND="env CODEBUDDY_CONFIG_DIR=$workbuddy_config_dir_word CODEBUDDY_SKIP_BUILTIN_MARKETPLACE=1 $workbuddy_bin --acp"
|
|
4634
|
+
agent_detail "using native WorkBuddy ACP command: $ACP_AGENT_COMMAND"
|
|
4635
|
+
return 0
|
|
4636
|
+
fi
|
|
4637
|
+
|
|
4638
|
+
if [ "$AGENT" = "workbuddy_ai" ]; then
|
|
4639
|
+
local workbuddy_ai_bin workbuddy_ai_word workbuddy_ai_config_dir_word
|
|
4640
|
+
workbuddy_ai_bin="$(find_workbuddy_ai_cli)" \
|
|
4641
|
+
|| agent_fail "WorkBuddy AI CLI 不可用:${WORKBUDDY_AI_APP_CLI}。请确认该文件存在且可执行。"
|
|
4642
|
+
workbuddy_ai_word="$(acp_command_word "$workbuddy_ai_bin")" \
|
|
4643
|
+
|| agent_fail "WorkBuddy AI 路径包含不受支持的换行符。"
|
|
4644
|
+
workbuddy_ai_config_dir_word="$(acp_command_word "$HOME/.workbuddy-ai")" \
|
|
4645
|
+
|| agent_fail "WorkBuddy AI 配置目录包含不受支持的换行符。"
|
|
4646
|
+
ACP_AGENT_COMMAND="env CODEBUDDY_CONFIG_DIR=$workbuddy_ai_config_dir_word CODEBUDDY_SKIP_BUILTIN_MARKETPLACE=1 $workbuddy_ai_word --acp"
|
|
4647
|
+
agent_detail "using native WorkBuddy AI ACP command: $ACP_AGENT_COMMAND"
|
|
4648
|
+
return 0
|
|
4649
|
+
fi
|
|
4650
|
+
|
|
4651
|
+
if [ "$AGENT" = "aime" ]; then
|
|
4652
|
+
local aime_bin aime_word
|
|
4653
|
+
aime_bin="$(aime_acp_cli_path)"
|
|
4654
|
+
[ -x "$aime_bin" ] || agent_fail "AIME_ACP_UNAVAILABLE: AIME ACP executable is unavailable after preparation."
|
|
4655
|
+
aime_word="$(acp_command_word "$aime_bin")" \
|
|
4656
|
+
|| agent_fail "AIME_ACP_PATH_INVALID: AIME ACP executable path is invalid."
|
|
4657
|
+
ACP_AGENT_COMMAND="$aime_word --site $(aime_acp_site)"
|
|
4658
|
+
agent_detail "AIME ACP configured for site $(aime_acp_site)"
|
|
4659
|
+
return 0
|
|
4660
|
+
fi
|
|
4661
|
+
|
|
4662
|
+
if [ "$AGENT" = "traecli" ]; then
|
|
4663
|
+
local traecode_bin traecode_word
|
|
4664
|
+
traecode_bin="$(find_traecode_cli)" || agent_fail "TraeCode CLI 不可用,无法启动 ACP 服务。"
|
|
4665
|
+
traecode_word="$(acp_command_word "$traecode_bin")" \
|
|
4666
|
+
|| agent_fail "TraeCode CLI 路径包含不受支持的换行符。"
|
|
4667
|
+
ACP_AGENT_COMMAND="$traecode_word acp serve"
|
|
4668
|
+
agent_detail "using native TraeCode ACP command: $ACP_AGENT_COMMAND"
|
|
4669
|
+
return 0
|
|
4670
|
+
fi
|
|
4671
|
+
|
|
4672
|
+
if [ "$AGENT" = "coco" ] || [ "$AGENT" = "traex" ]; then
|
|
4673
|
+
local trae_bin
|
|
4674
|
+
trae_bin="$(find_traex_cli)" || agent_fail "Trae CLI Next(内部版)(traex)不可用,无法启动飞书任务所需的 ACP 服务。"
|
|
4675
|
+
ACP_AGENT_COMMAND="$trae_bin acp serve"
|
|
4676
|
+
agent_detail "using native Trae ACP command: $ACP_AGENT_COMMAND"
|
|
4677
|
+
return 0
|
|
4678
|
+
fi
|
|
4679
|
+
|
|
3472
4680
|
if [ "$AGENT" != "codex" ]; then
|
|
3473
4681
|
return 0
|
|
3474
4682
|
fi
|
|
@@ -3689,6 +4897,22 @@ run_internal_discover_agents() {
|
|
|
3689
4897
|
if agent_cli_detected cursor; then
|
|
3690
4898
|
agents+=("cursor")
|
|
3691
4899
|
fi
|
|
4900
|
+
if agent_cli_detected traex; then
|
|
4901
|
+
agents+=("traex")
|
|
4902
|
+
elif agent_cli_detected coco; then
|
|
4903
|
+
agents+=("coco")
|
|
4904
|
+
elif agent_cli_detected traecli; then
|
|
4905
|
+
agents+=("traecli")
|
|
4906
|
+
fi
|
|
4907
|
+
if agent_cli_detected workbuddy; then
|
|
4908
|
+
agents+=("workbuddy")
|
|
4909
|
+
fi
|
|
4910
|
+
if agent_cli_detected workbuddy_ai; then
|
|
4911
|
+
agents+=("workbuddy_ai")
|
|
4912
|
+
fi
|
|
4913
|
+
if agent_cli_detected aime; then
|
|
4914
|
+
agents+=("aime")
|
|
4915
|
+
fi
|
|
3692
4916
|
local joined=""
|
|
3693
4917
|
if [ "${#agents[@]}" -gt 0 ]; then
|
|
3694
4918
|
joined="$(IFS=,; printf '%s' "${agents[*]}")"
|
|
@@ -3702,32 +4926,79 @@ process.stdout.write(JSON.stringify({ agents }));
|
|
|
3702
4926
|
prepare_internal_agent_environment() {
|
|
3703
4927
|
[ -n "$AGENT" ] || agent_fail "internal Agent preparation requires --agent"
|
|
3704
4928
|
validate_agent_name "$AGENT"
|
|
3705
|
-
|
|
4929
|
+
ensure_agent_selection_available "$AGENT"
|
|
4930
|
+
load_agent_metadata
|
|
4931
|
+
if [ "$AGENT_EXECUTION_LOCATION" = "local" ]; then
|
|
4932
|
+
source_lark_env
|
|
4933
|
+
fi
|
|
3706
4934
|
ensure_agent_cli
|
|
3707
4935
|
}
|
|
3708
4936
|
|
|
3709
4937
|
run_internal_register_binding() {
|
|
3710
4938
|
[ -n "$AGENT" ] || agent_fail "internal Bot registration requires --agent"
|
|
3711
4939
|
validate_agent_name "$AGENT"
|
|
3712
|
-
|
|
4940
|
+
ensure_agent_selection_available "$AGENT"
|
|
4941
|
+
load_agent_metadata
|
|
3713
4942
|
register_feishu_app
|
|
3714
|
-
|
|
4943
|
+
if [ "$AGENT_EXECUTION_LOCATION" = "remote" ]; then
|
|
4944
|
+
emit_internal_result "{\"app_id\":\"$(json_escape "$APP_ID")\",\"app_secret\":\"$(json_escape "$APP_SECRET")\",\"display_name\":\"$(json_escape "$BOT_NAME")\",\"auth_mode\":\"app-secret\"}"
|
|
4945
|
+
else
|
|
4946
|
+
emit_internal_result "{\"app_id\":\"$(json_escape "$APP_ID")\",\"app_secret\":\"$(json_escape "$APP_SECRET")\",\"display_name\":\"$(json_escape "$BOT_NAME")\",\"lark_cli_profile\":\"$(json_escape "$LARK_CLI_PROFILE")\",\"auth_mode\":\"lark-cli\"}"
|
|
4947
|
+
fi
|
|
3715
4948
|
}
|
|
3716
4949
|
|
|
3717
4950
|
run_internal_prepare_agent() {
|
|
3718
4951
|
prepare_internal_agent_environment
|
|
3719
4952
|
ensure_codex_cli_updated
|
|
3720
4953
|
ensure_agent_login
|
|
4954
|
+
if [ "$AGENT_PREPARE_CANCELLED" = "true" ]; then
|
|
4955
|
+
emit_internal_result "{\"cancelled\":true,\"agent_type\":\"$(json_escape "$AGENT")\",\"reason\":\"$(json_escape "$AGENT_PREPARE_CANCEL_REASON")\"}"
|
|
4956
|
+
return 0
|
|
4957
|
+
fi
|
|
3721
4958
|
maybe_mock_fail "agent-login"
|
|
3722
4959
|
ensure_acpx
|
|
3723
4960
|
build_acp_agent_command
|
|
3724
|
-
|
|
4961
|
+
if [ "$AGENT_EXECUTION_LOCATION" = "remote" ]; then
|
|
4962
|
+
emit_internal_result "{\"agent_type\":\"$(json_escape "$AGENT")\",\"acp_command\":\"$(json_escape "$ACP_AGENT_COMMAND")\"}"
|
|
4963
|
+
else
|
|
4964
|
+
emit_internal_result "{\"agent_type\":\"$(json_escape "$AGENT")\",\"acp_command\":\"$(json_escape "$ACP_AGENT_COMMAND")\",\"lark_cli_config_dir\":\"$(json_escape "${LARKSUITE_CLI_CONFIG_DIR:-}")\"}"
|
|
4965
|
+
fi
|
|
4966
|
+
}
|
|
4967
|
+
|
|
4968
|
+
run_internal_probe_profile() {
|
|
4969
|
+
AAMP_TASK_INTERNAL_BINDING_JSON=""
|
|
4970
|
+
IFS= read -r AAMP_TASK_INTERNAL_BINDING_JSON <&"$AAMP_TASK_INTERNAL_INPUT_FD" || true
|
|
4971
|
+
[ -n "$AAMP_TASK_INTERNAL_BINDING_JSON" ] || agent_fail "missing internal binding payload"
|
|
4972
|
+
AGENT="$(binding_json_field agent_type)"
|
|
4973
|
+
validate_agent_name "$AGENT"
|
|
4974
|
+
load_agent_metadata
|
|
4975
|
+
[ "$AGENT_EXECUTION_LOCATION" != "remote" ] || agent_fail "remote bindings do not use lark-cli profiles"
|
|
4976
|
+
APP_ID="$(binding_json_field bot.app_id)"
|
|
4977
|
+
LARK_CLI_PROFILE="$(binding_json_field bot.lark_cli_profile)"
|
|
4978
|
+
[ -n "$APP_ID" ] && [ -n "$LARK_CLI_PROFILE" ] || agent_fail "binding is missing Feishu app or profile"
|
|
4979
|
+
AAMP_TASK_INTERNAL_BINDING_JSON=""
|
|
4980
|
+
unset HTTPS_PROXY https_proxy HTTP_PROXY http_proxy ALL_PROXY all_proxy
|
|
4981
|
+
export LARKSUITE_CLI_CONFIG_DIR="$AAMP_LARK_CLI_CONFIG_DIR"
|
|
4982
|
+
unset LARK_CLI_NO_PROXY
|
|
4983
|
+
if ! select_lark_cli_bin_from_candidates; then
|
|
4984
|
+
emit_internal_result '{"ready":false}'
|
|
4985
|
+
return 0
|
|
4986
|
+
fi
|
|
4987
|
+
if with_lark_cli_config_lock probe_lark_cli_profile_locked "$LARK_CLI_PROFILE"; then
|
|
4988
|
+
emit_internal_result "{\"ready\":true,\"lark_cli_bin\":\"$(json_escape "$LARK_CLI_CMD")\",\"lark_cli_config_dir\":\"$(json_escape "${LARKSUITE_CLI_CONFIG_DIR:-}")\"}"
|
|
4989
|
+
else
|
|
4990
|
+
emit_internal_result '{"ready":false}'
|
|
4991
|
+
fi
|
|
3725
4992
|
}
|
|
3726
4993
|
|
|
3727
4994
|
run_internal_ensure_profile() {
|
|
3728
4995
|
AAMP_TASK_INTERNAL_BINDING_JSON=""
|
|
3729
4996
|
IFS= read -r AAMP_TASK_INTERNAL_BINDING_JSON <&"$AAMP_TASK_INTERNAL_INPUT_FD" || true
|
|
3730
4997
|
[ -n "$AAMP_TASK_INTERNAL_BINDING_JSON" ] || agent_fail "missing internal binding payload"
|
|
4998
|
+
AGENT="$(binding_json_field agent_type)"
|
|
4999
|
+
validate_agent_name "$AGENT"
|
|
5000
|
+
load_agent_metadata
|
|
5001
|
+
[ "$AGENT_EXECUTION_LOCATION" != "remote" ] || agent_fail "remote bindings do not use lark-cli profiles"
|
|
3731
5002
|
APP_ID="$(binding_json_field bot.app_id)"
|
|
3732
5003
|
APP_SECRET="$(binding_json_field bot.app_secret)"
|
|
3733
5004
|
BOT_NAME="$(binding_json_field bot.display_name)"
|
|
@@ -3742,7 +5013,9 @@ run_internal_ensure_profile() {
|
|
|
3742
5013
|
|
|
3743
5014
|
run_internal_action() {
|
|
3744
5015
|
AAMP_TASK_INTERNAL="true"
|
|
3745
|
-
|
|
5016
|
+
if [ "$AAMP_TASK_ACTION" != "__probe-profile" ]; then
|
|
5017
|
+
ensure_node_toolchain
|
|
5018
|
+
fi
|
|
3746
5019
|
case "$AAMP_TASK_ACTION" in
|
|
3747
5020
|
__discover-agents)
|
|
3748
5021
|
run_internal_discover_agents
|
|
@@ -3753,6 +5026,9 @@ run_internal_action() {
|
|
|
3753
5026
|
__prepare-agent)
|
|
3754
5027
|
run_internal_prepare_agent
|
|
3755
5028
|
;;
|
|
5029
|
+
__probe-profile)
|
|
5030
|
+
run_internal_probe_profile
|
|
5031
|
+
;;
|
|
3756
5032
|
__ensure-profile)
|
|
3757
5033
|
run_internal_ensure_profile
|
|
3758
5034
|
;;
|
|
@@ -3766,6 +5042,92 @@ task_agent_controller_path() {
|
|
|
3766
5042
|
printf '%s/bin/feishu-task-agent-controller.mjs\n' "$(task_agent_global_package_dir)"
|
|
3767
5043
|
}
|
|
3768
5044
|
|
|
5045
|
+
task_agent_metadata_path() {
|
|
5046
|
+
printf '%s/bin/agent-metadata.mjs\n' "$(task_agent_global_package_dir)"
|
|
5047
|
+
}
|
|
5048
|
+
|
|
5049
|
+
local_package_override_is_supported() {
|
|
5050
|
+
local spec local_path
|
|
5051
|
+
spec="${1:-}"
|
|
5052
|
+
case "$spec" in
|
|
5053
|
+
file:*)
|
|
5054
|
+
local_path="${spec#file:}"
|
|
5055
|
+
[ -n "$local_path" ] && [ -d "$local_path" ]
|
|
5056
|
+
;;
|
|
5057
|
+
*.tgz) [ -f "$spec" ] ;;
|
|
5058
|
+
*) return 1 ;;
|
|
5059
|
+
esac
|
|
5060
|
+
}
|
|
5061
|
+
|
|
5062
|
+
aime_local_package_override_path() {
|
|
5063
|
+
local directory filename spec
|
|
5064
|
+
spec="${1:-}"
|
|
5065
|
+
case "$spec" in
|
|
5066
|
+
-*|file:*|*://*) return 1 ;;
|
|
5067
|
+
esac
|
|
5068
|
+
if [[ "$spec" =~ (^|@)[[:alpha:]][[:alnum:]+.-]*: ]]; then
|
|
5069
|
+
return 1
|
|
5070
|
+
fi
|
|
5071
|
+
case "$spec" in
|
|
5072
|
+
*.tgz) [ -f "$spec" ] || return 1 ;;
|
|
5073
|
+
*) return 1 ;;
|
|
5074
|
+
esac
|
|
5075
|
+
case "$spec" in
|
|
5076
|
+
*/*)
|
|
5077
|
+
directory="${spec%/*}"
|
|
5078
|
+
filename="${spec##*/}"
|
|
5079
|
+
[ -n "$directory" ] || directory="/"
|
|
5080
|
+
;;
|
|
5081
|
+
*)
|
|
5082
|
+
directory="."
|
|
5083
|
+
filename="$spec"
|
|
5084
|
+
;;
|
|
5085
|
+
esac
|
|
5086
|
+
directory="$(cd -P -- "$directory" 2>/dev/null && pwd -P)" || return 1
|
|
5087
|
+
printf '%s/%s\n' "$directory" "$filename"
|
|
5088
|
+
}
|
|
5089
|
+
|
|
5090
|
+
apply_task_agent_package_override_policy() {
|
|
5091
|
+
local aime_override_path
|
|
5092
|
+
ACP_BRIDGE_PKG="$AAMP_TASK_DEFAULT_ACP_BRIDGE_PKG"
|
|
5093
|
+
FEISHU_BRIDGE_PKG="$AAMP_TASK_DEFAULT_FEISHU_BRIDGE_PKG"
|
|
5094
|
+
AIME_ACP_PKG="$AAMP_TASK_DEFAULT_AIME_ACP_PKG"
|
|
5095
|
+
[ "$AAMP_TASK_ALLOW_PACKAGE_OVERRIDES" = "true" ] || return 0
|
|
5096
|
+
|
|
5097
|
+
if [ -n "$AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG" ]; then
|
|
5098
|
+
local_package_override_is_supported "$AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG" \
|
|
5099
|
+
|| agent_fail "Local ACP Bridge package override is invalid."
|
|
5100
|
+
ACP_BRIDGE_PKG="$AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG"
|
|
5101
|
+
fi
|
|
5102
|
+
if [ -n "$AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG" ]; then
|
|
5103
|
+
local_package_override_is_supported "$AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG" \
|
|
5104
|
+
|| agent_fail "Local Feishu Bridge package override is invalid."
|
|
5105
|
+
FEISHU_BRIDGE_PKG="$AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG"
|
|
5106
|
+
fi
|
|
5107
|
+
if [ -n "$AAMP_TASK_REQUESTED_AIME_ACP_PKG" ]; then
|
|
5108
|
+
aime_override_path="$(aime_local_package_override_path "$AAMP_TASK_REQUESTED_AIME_ACP_PKG")" \
|
|
5109
|
+
|| agent_fail "AIME_ACP_PACKAGE_OVERRIDE_INVALID: AIME ACP package override must be an existing local .tgz file."
|
|
5110
|
+
AAMP_TASK_REQUESTED_AIME_ACP_PKG="$aime_override_path"
|
|
5111
|
+
AIME_ACP_PKG="$aime_override_path"
|
|
5112
|
+
fi
|
|
5113
|
+
}
|
|
5114
|
+
|
|
5115
|
+
load_agent_metadata() {
|
|
5116
|
+
local metadata_path metadata_json
|
|
5117
|
+
metadata_path="$(task_agent_metadata_path)"
|
|
5118
|
+
[ -r "$metadata_path" ] || agent_fail "Task Agent metadata is missing: $metadata_path"
|
|
5119
|
+
metadata_json="$(node "$metadata_path" --json "$AGENT")" \
|
|
5120
|
+
|| agent_fail "failed to resolve Task Agent metadata for $AGENT"
|
|
5121
|
+
AGENT_EXECUTION_LOCATION="$(node -e 'const value=JSON.parse(process.argv[1]);process.stdout.write(value.executionLocation)' "$metadata_json")"
|
|
5122
|
+
AGENT_ATTACHMENT_POLICY="$(node -e 'const value=JSON.parse(process.argv[1]);process.stdout.write(value.attachmentPolicy || "")' "$metadata_json")"
|
|
5123
|
+
AGENT_TASK_DISPATCH_CONCURRENCY="$(node -e 'const value=JSON.parse(process.argv[1]);process.stdout.write(value.taskDispatchConcurrency ? String(value.taskDispatchConcurrency) : "")' "$metadata_json")"
|
|
5124
|
+
if [ -n "$AAMP_TASK_INTERNAL_EXECUTION_LOCATION" ] \
|
|
5125
|
+
&& [ "$AAMP_TASK_INTERNAL_EXECUTION_LOCATION" != "$AGENT_EXECUTION_LOCATION" ]; then
|
|
5126
|
+
agent_fail "Task Agent execution metadata mismatch"
|
|
5127
|
+
fi
|
|
5128
|
+
AAMP_TASK_INTERNAL_EXECUTION_LOCATION="$AGENT_EXECUTION_LOCATION"
|
|
5129
|
+
}
|
|
5130
|
+
|
|
3769
5131
|
run_task_agent_controller() {
|
|
3770
5132
|
local controller bootstrap_path install_command
|
|
3771
5133
|
controller="$(task_agent_controller_path)"
|
|
@@ -3778,6 +5140,17 @@ run_task_agent_controller() {
|
|
|
3778
5140
|
export AAMP_TASK_NPX_BIN="$NPX_BIN"
|
|
3779
5141
|
export AAMP_TASK_ACP_BRIDGE_PKG="$ACP_BRIDGE_PKG"
|
|
3780
5142
|
export AAMP_TASK_FEISHU_BRIDGE_PKG="$FEISHU_BRIDGE_PKG"
|
|
5143
|
+
export AAMP_TASK_AIME_ACP_PKG="$AIME_ACP_PKG"
|
|
5144
|
+
export AAMP_TASK_ALLOW_PACKAGE_OVERRIDES
|
|
5145
|
+
if [ "$AAMP_TASK_ALLOW_PACKAGE_OVERRIDES" != "true" ]; then
|
|
5146
|
+
AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG=""
|
|
5147
|
+
AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG=""
|
|
5148
|
+
AAMP_TASK_REQUESTED_AIME_ACP_PKG=""
|
|
5149
|
+
fi
|
|
5150
|
+
export AAMP_TASK_PACKAGE_OVERRIDES_RESOLVED=true
|
|
5151
|
+
export AAMP_TASK_REQUESTED_ACP_BRIDGE_PKG
|
|
5152
|
+
export AAMP_TASK_REQUESTED_FEISHU_BRIDGE_PKG
|
|
5153
|
+
export AAMP_TASK_REQUESTED_AIME_ACP_PKG
|
|
3781
5154
|
export AAMP_TASK_CODEX_ACP_PKG="$CODEX_ACP_PKG"
|
|
3782
5155
|
export AAMP_TASK_AGENT_VERSION
|
|
3783
5156
|
export AAMP_TASK_DEFAULT_AGENT="$AGENT"
|
|
@@ -3835,13 +5208,19 @@ main() {
|
|
|
3835
5208
|
return 0
|
|
3836
5209
|
fi
|
|
3837
5210
|
|
|
5211
|
+
apply_task_agent_package_override_policy
|
|
5212
|
+
|
|
3838
5213
|
case "$AAMP_TASK_ACTION" in
|
|
3839
|
-
__discover-agents|__register-binding|__prepare-agent|__ensure-profile)
|
|
5214
|
+
__discover-agents|__register-binding|__prepare-agent|__probe-profile|__ensure-profile)
|
|
3840
5215
|
run_internal_action
|
|
3841
5216
|
return 0
|
|
3842
5217
|
;;
|
|
3843
5218
|
esac
|
|
3844
5219
|
|
|
5220
|
+
if [ "$AGENT" = "aime" ]; then
|
|
5221
|
+
ensure_agent_selection_available "$AGENT"
|
|
5222
|
+
fi
|
|
5223
|
+
|
|
3845
5224
|
trap cleanup EXIT INT TERM HUP
|
|
3846
5225
|
init_log_run
|
|
3847
5226
|
validate_mock_fail_stage
|