@larktask/aamp-feishu-task-agent 0.1.0-dev.173 → 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 +1433 -48
- 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}"
|
|
@@ -47,25 +51,67 @@ CODEM_INSTALLER_URL="${CODEM_INSTALLER_URL:-https://sf-unpkg-src.bytedance.net/@
|
|
|
47
51
|
CODEM_INSTALLER_CONFIRM="${CODEM_INSTALLER_CONFIRM:-}"
|
|
48
52
|
CODEM_PROVIDER_PREFLIGHT="${CODEM_PROVIDER_PREFLIGHT:-true}"
|
|
49
53
|
CODEM_PROVIDER_PREFLIGHT_TIMEOUT_SECONDS="${CODEM_PROVIDER_PREFLIGHT_TIMEOUT_SECONDS:-60}"
|
|
54
|
+
CODEX_CHATGPT_APP_CLI="/Applications/ChatGPT.app/Contents/Resources/codex"
|
|
50
55
|
CODEX_APP_CLI="/Applications/Codex.app/Contents/Resources/codex"
|
|
51
56
|
CODEX_AUTO_UPDATE="${CODEX_AUTO_UPDATE:-true}"
|
|
52
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}"
|
|
53
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"
|
|
54
70
|
LARK_REGISTER_APP_SDK="${LARK_REGISTER_APP_SDK:-@larksuiteoapi/node-sdk@1.68.0}"
|
|
55
71
|
LARK_CLI_MIN_VERSION="${LARK_CLI_MIN_VERSION:-1.0.64}"
|
|
56
|
-
|
|
57
|
-
|
|
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"
|
|
58
79
|
FEISHU_APP_EVENTS_TENANT="${FEISHU_APP_EVENTS_TENANT:-task.task.update_user_access_v2}"
|
|
59
80
|
FEISHU_APP_EVENTS_USER="${FEISHU_APP_EVENTS_USER:-task.task.update_user_access_v2}"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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}"
|
|
64
107
|
CLI_BRIDGE_PKG="${CLI_BRIDGE_PKG:-@zengxingyuan/aamp-cli-bridge@0.1.7-dev.14}"
|
|
65
|
-
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"
|
|
66
112
|
AAMP_TASK_AGENT_NAME="${AAMP_TASK_AGENT_NAME:-@larktask/aamp-feishu-task-agent}"
|
|
67
113
|
AAMP_TASK_AGENT_LEGACY_NAME="${AAMP_TASK_AGENT_LEGACY_NAME:-@zengxingyuan/aamp-feishu-task-agent}"
|
|
68
|
-
AAMP_TASK_AGENT_VERSION="0.1.
|
|
114
|
+
AAMP_TASK_AGENT_VERSION="0.1.1-dev.2"
|
|
69
115
|
AAMP_TASK_AGENT_CHANNEL="${AAMP_TASK_AGENT_CHANNEL:-dev}"
|
|
70
116
|
AAMP_STALE_PROCESS_CLEANUP="${AAMP_STALE_PROCESS_CLEANUP:-false}"
|
|
71
117
|
AAMP_STALE_PROCESS_SECONDS="${AAMP_STALE_PROCESS_SECONDS:-86400}"
|
|
@@ -85,8 +131,12 @@ CODEM_SERVICE_START_OUTPUT=""
|
|
|
85
131
|
CODEM_AUTO_UPDATE_DONE="false"
|
|
86
132
|
CODEM_FORCE_LOGIN_DONE="false"
|
|
87
133
|
CODEM_PROVIDER_RECOVERY_DONE="false"
|
|
134
|
+
TRAE_CLI_BIN=""
|
|
135
|
+
TRAECODE_CLI_BIN=""
|
|
88
136
|
PAIRING_URL=""
|
|
89
137
|
ACP_AGENT_COMMAND=""
|
|
138
|
+
AGENT_PREPARE_CANCELLED="false"
|
|
139
|
+
AGENT_PREPARE_CANCEL_REASON=""
|
|
90
140
|
STARTED_BRIDGE_PID=""
|
|
91
141
|
ONE_CLICK_RUN_ID="$(date +%s)-$$"
|
|
92
142
|
BOT_RESERVED="false"
|
|
@@ -115,6 +165,13 @@ sanitize_inherited_npm_exec_env() {
|
|
|
115
165
|
done < <(env)
|
|
116
166
|
}
|
|
117
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
|
+
|
|
118
175
|
usage() {
|
|
119
176
|
cat <<'USAGE'
|
|
120
177
|
Usage:
|
|
@@ -131,7 +188,8 @@ Running the standalone one-click script without a subcommand is the same as
|
|
|
131
188
|
"feishu-task-agent install".
|
|
132
189
|
|
|
133
190
|
Options:
|
|
134
|
-
--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.
|
|
135
193
|
--aamp-host URL AAMP service URL. Default: https://meshmail.ai
|
|
136
194
|
--debug Enable debug mode for bridge processes
|
|
137
195
|
-h, --help Show this help
|
|
@@ -791,6 +849,7 @@ task_agent_global_install_is_complete() {
|
|
|
791
849
|
[ -x "$NPM_GLOBAL_PREFIX/bin/aamp-logs" ] || return 1
|
|
792
850
|
[ -r "$package_dir/bootstrap/aamp-feishu-task-agent-bootstrap.sh" ] || return 1
|
|
793
851
|
[ -r "$package_dir/bin/feishu-task-agent-controller.mjs" ] || return 1
|
|
852
|
+
[ -r "$package_dir/bin/traecode-readiness.mjs" ] || return 1
|
|
794
853
|
}
|
|
795
854
|
|
|
796
855
|
task_agent_global_install_is_current() {
|
|
@@ -815,7 +874,7 @@ ensure_task_agent_global_install_locked() {
|
|
|
815
874
|
fi
|
|
816
875
|
remove_legacy_task_agent_global_install || return 1
|
|
817
876
|
agent_detail "synchronizing task-agent npm package: current=${installed_version:-missing} expected=$expected_version"
|
|
818
|
-
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
|
|
819
878
|
task_agent_global_install_is_current "$expected_version"
|
|
820
879
|
}
|
|
821
880
|
|
|
@@ -829,12 +888,16 @@ ensure_task_agent_global_install() {
|
|
|
829
888
|
|
|
830
889
|
short_command_is_current() {
|
|
831
890
|
local target="$1"
|
|
891
|
+
local source_file="${2:-}"
|
|
832
892
|
local installed_name installed_version
|
|
833
893
|
[ -x "$target" ] || return 1
|
|
834
894
|
installed_name="$(script_file_task_agent_name "$target" || true)"
|
|
835
895
|
installed_version="$(script_file_task_agent_version "$target" || true)"
|
|
836
896
|
[ "$installed_name" = "$AAMP_TASK_AGENT_NAME" ] || return 1
|
|
837
|
-
[ "$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
|
|
838
901
|
}
|
|
839
902
|
|
|
840
903
|
write_task_update_cache() {
|
|
@@ -912,13 +975,13 @@ install_short_command_current() {
|
|
|
912
975
|
local target="$1"
|
|
913
976
|
local source_file="${BASH_SOURCE[0]:-}"
|
|
914
977
|
local tmp="${target}.tmp.$$"
|
|
915
|
-
if short_command_is_current "$target"; then
|
|
916
|
-
agent_detail "short command already current: $target"
|
|
917
|
-
return 0
|
|
918
|
-
fi
|
|
919
978
|
case "$source_file" in
|
|
920
979
|
""|/dev/fd/*|/private/dev/fd/*|/proc/*) source_file="" ;;
|
|
921
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
|
|
922
985
|
if [ -n "$source_file" ] && [ -f "$source_file" ] && [ -r "$source_file" ]; then
|
|
923
986
|
mkdir -p "$(dirname "$target")"
|
|
924
987
|
cp "$source_file" "$tmp" 2>>"$ONE_CLICK_LOG" || return 1
|
|
@@ -949,6 +1012,7 @@ ensure_selected_agent_for_start() {
|
|
|
949
1012
|
select_agent_interactively
|
|
950
1013
|
fi
|
|
951
1014
|
validate_agent_name "$AGENT"
|
|
1015
|
+
ensure_agent_selection_available "$AGENT"
|
|
952
1016
|
}
|
|
953
1017
|
|
|
954
1018
|
resolve_latest_task_agent_version() {
|
|
@@ -1033,12 +1097,109 @@ run_task_agent_update_command() {
|
|
|
1033
1097
|
install_aamp_logs_bin "$installed_version" || agent_fail "failed to synchronize aamp-logs $installed_version"
|
|
1034
1098
|
printf '当前版本:%s\n' "$installed_version"
|
|
1035
1099
|
}
|
|
1036
|
-
|
|
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
|
|
1037
1198
|
local npm_log
|
|
1038
1199
|
npm_log="$(mktemp "${TMPDIR:-/tmp}/aamp-npm-install.XXXXXX")"
|
|
1039
1200
|
sanitize_inherited_npm_exec_env
|
|
1040
1201
|
if "$NPM_BIN" install -g \
|
|
1041
|
-
--registry "$
|
|
1202
|
+
--registry "$registry" \
|
|
1042
1203
|
--cache "$NPM_CACHE_DIR" \
|
|
1043
1204
|
--prefix "$NPM_GLOBAL_PREFIX" \
|
|
1044
1205
|
"$@" >"$npm_log" 2>&1; then
|
|
@@ -1056,7 +1217,7 @@ npm_install_global() {
|
|
|
1056
1217
|
npm_log="$(mktemp "${TMPDIR:-/tmp}/aamp-npm-install-retry.XXXXXX")"
|
|
1057
1218
|
sanitize_inherited_npm_exec_env
|
|
1058
1219
|
"$NPM_BIN" install -g \
|
|
1059
|
-
--registry "$
|
|
1220
|
+
--registry "$registry" \
|
|
1060
1221
|
--cache "$NPM_CACHE_DIR" \
|
|
1061
1222
|
--prefix "$NPM_GLOBAL_PREFIX" \
|
|
1062
1223
|
"$@" >"$npm_log" 2>&1 || {
|
|
@@ -1067,6 +1228,10 @@ npm_install_global() {
|
|
|
1067
1228
|
hash -r 2>/dev/null || true
|
|
1068
1229
|
}
|
|
1069
1230
|
|
|
1231
|
+
npm_install_global() {
|
|
1232
|
+
npm_install_global_from_registry "$NPM_REGISTRY" "$@"
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1070
1235
|
npx_package() {
|
|
1071
1236
|
local npm_log
|
|
1072
1237
|
local status
|
|
@@ -1176,13 +1341,32 @@ npm_install_register_helper() {
|
|
|
1176
1341
|
}
|
|
1177
1342
|
}
|
|
1178
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
|
+
|
|
1179
1353
|
validate_agent_name() {
|
|
1180
1354
|
case "$1" in
|
|
1181
|
-
codex|cursor) ;;
|
|
1182
|
-
*) 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" ;;
|
|
1183
1357
|
esac
|
|
1184
1358
|
}
|
|
1185
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
|
+
|
|
1186
1370
|
read_tty_line() {
|
|
1187
1371
|
local prompt="$1"
|
|
1188
1372
|
local value
|
|
@@ -1202,6 +1386,24 @@ agent_cli_detected() {
|
|
|
1202
1386
|
cursor)
|
|
1203
1387
|
find_cursor_agent_cli >/dev/null 2>&1
|
|
1204
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
|
+
;;
|
|
1205
1407
|
*)
|
|
1206
1408
|
return 1
|
|
1207
1409
|
;;
|
|
@@ -1216,9 +1418,25 @@ discover_interactive_agents() {
|
|
|
1216
1418
|
if agent_cli_detected cursor; then
|
|
1217
1419
|
DETECTED_AGENTS+=("cursor")
|
|
1218
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
|
|
1219
1437
|
|
|
1220
1438
|
if [ "${#DETECTED_AGENTS[@]}" -eq 0 ]; then
|
|
1221
|
-
agent_fail "
|
|
1439
|
+
agent_fail "暂未检测到本地智能体。请先安装 Codex、Cursor、Trae CLI、WorkBuddy 或 WorkBuddy AI 后重试。"
|
|
1222
1440
|
fi
|
|
1223
1441
|
}
|
|
1224
1442
|
|
|
@@ -1237,9 +1455,9 @@ render_agent_menu() {
|
|
|
1237
1455
|
for index in "${!DETECTED_AGENTS[@]}"; do
|
|
1238
1456
|
printf '\033[2K\r' >&3
|
|
1239
1457
|
if [ "$index" -eq "$selected" ]; then
|
|
1240
|
-
printf ' > %s\n' "${DETECTED_AGENTS[$index]}" >&3
|
|
1458
|
+
printf ' > %s\n' "$(agent_display_name "${DETECTED_AGENTS[$index]}")" >&3
|
|
1241
1459
|
else
|
|
1242
|
-
printf ' %s\n' "${DETECTED_AGENTS[$index]}" >&3
|
|
1460
|
+
printf ' %s\n' "$(agent_display_name "${DETECTED_AGENTS[$index]}")" >&3
|
|
1243
1461
|
fi
|
|
1244
1462
|
done
|
|
1245
1463
|
printf '\033[2K\r使用 ↑/↓ 选择,回车确认。也可按数字键或 j/k。\n' >&3
|
|
@@ -1253,7 +1471,7 @@ select_agent_interactively() {
|
|
|
1253
1471
|
discover_interactive_agents
|
|
1254
1472
|
|
|
1255
1473
|
if ! exec 3<>/dev/tty; then
|
|
1256
|
-
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"
|
|
1257
1475
|
fi
|
|
1258
1476
|
|
|
1259
1477
|
tty_state="$(stty -g <&3)"
|
|
@@ -1439,7 +1657,7 @@ parse_args() {
|
|
|
1439
1657
|
fi
|
|
1440
1658
|
|
|
1441
1659
|
case "${1:-}" in
|
|
1442
|
-
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)
|
|
1443
1661
|
AAMP_TASK_ACTION="$1"
|
|
1444
1662
|
shift
|
|
1445
1663
|
;;
|
|
@@ -1794,7 +2012,7 @@ save_bot_config() {
|
|
|
1794
2012
|
local profile="$3"
|
|
1795
2013
|
local app_secret="${4:-}"
|
|
1796
2014
|
mkdir -p "$(dirname "$BOT_CONFIG_FILE")"
|
|
1797
|
-
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 '
|
|
1798
2016
|
const fs = require("fs");
|
|
1799
2017
|
const file = process.env.BOT_CONFIG_FILE;
|
|
1800
2018
|
const appSecret = String(process.env.BOT_APP_SECRET || "").trim();
|
|
@@ -1805,7 +2023,9 @@ const next = {
|
|
|
1805
2023
|
profile: process.env.BOT_PROFILE,
|
|
1806
2024
|
auth_mode: "lark-cli",
|
|
1807
2025
|
capabilities: ["im", "task"],
|
|
1808
|
-
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",
|
|
1809
2029
|
updated_at: new Date().toISOString(),
|
|
1810
2030
|
};
|
|
1811
2031
|
let parsed = { version: 1, profiles: [] };
|
|
@@ -2038,15 +2258,65 @@ ensure_lark_cli_min_version() {
|
|
|
2038
2258
|
select_lark_cli_bin
|
|
2039
2259
|
}
|
|
2040
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
|
+
|
|
2041
2307
|
lark_cli_user_auth_satisfied() {
|
|
2042
2308
|
local profile="$1"
|
|
2309
|
+
local required_scopes="${2:-${FEISHU_USER_AUTH_REQUIRED_SCOPES:-${FEISHU_USER_AUTH_CORE_SCOPES:-}}}"
|
|
2043
2310
|
local status_json auth_check
|
|
2044
2311
|
LARK_CLI_CMD="${LARK_CLI_CMD:-lark-cli}"
|
|
2045
2312
|
|
|
2313
|
+
if [ -z "${required_scopes//[[:space:],]/}" ]; then
|
|
2314
|
+
return 0
|
|
2315
|
+
fi
|
|
2046
2316
|
status_json="$("$LARK_CLI_CMD" --profile "$profile" auth status --json 2>/dev/null)" || return 1
|
|
2047
2317
|
|
|
2048
2318
|
set +e
|
|
2049
|
-
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 '
|
|
2050
2320
|
function parseJsonOutput(value, label) {
|
|
2051
2321
|
const raw = String(value || "");
|
|
2052
2322
|
const start = raw.indexOf("{");
|
|
@@ -2087,10 +2357,76 @@ console.log("ok");
|
|
|
2087
2357
|
return 1
|
|
2088
2358
|
}
|
|
2089
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
|
+
|
|
2090
2425
|
normalize_lark_cli_auth_excludes() {
|
|
2091
|
-
EXCLUDES="$1" node -e '
|
|
2426
|
+
EXCLUDES="$1" REQUESTED_SCOPES="${2:-${FEISHU_USER_AUTH_REQUESTED_SCOPES:-}}" node -e '
|
|
2092
2427
|
const excludes = String(process.env.EXCLUDES || "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
2093
|
-
|
|
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(","));
|
|
2094
2430
|
'
|
|
2095
2431
|
}
|
|
2096
2432
|
|
|
@@ -2237,12 +2573,14 @@ run_lark_cli_auth_login_with_browser_open() {
|
|
|
2237
2573
|
run_lark_cli_auth_login() {
|
|
2238
2574
|
local profile="$1"
|
|
2239
2575
|
local auth_excludes="$2"
|
|
2576
|
+
local requested_scopes="${FEISHU_USER_AUTH_REQUESTED_SCOPES:-}"
|
|
2240
2577
|
|
|
2241
|
-
|
|
2578
|
+
[ -n "${requested_scopes//[[:space:],]/}" ] || return 0
|
|
2579
|
+
auth_excludes="$(normalize_lark_cli_auth_excludes "$auth_excludes" "$requested_scopes")"
|
|
2242
2580
|
if [ -z "$auth_excludes" ]; then
|
|
2243
|
-
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"
|
|
2244
2582
|
else
|
|
2245
|
-
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"
|
|
2246
2584
|
fi
|
|
2247
2585
|
}
|
|
2248
2586
|
|
|
@@ -2251,11 +2589,43 @@ ensure_lark_cli_profile() {
|
|
|
2251
2589
|
with_lark_cli_config_lock ensure_lark_cli_profile_locked "$@"
|
|
2252
2590
|
}
|
|
2253
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
|
+
|
|
2254
2620
|
ensure_lark_cli_profile_locked() {
|
|
2255
2621
|
local app_id="$1"
|
|
2256
2622
|
local app_secret="$2"
|
|
2257
2623
|
local profile="$3"
|
|
2258
2624
|
local auth_excludes
|
|
2625
|
+
local required_scopes
|
|
2626
|
+
|
|
2627
|
+
initialize_feishu_scope_manifest
|
|
2628
|
+
validate_feishu_user_auth_mode
|
|
2259
2629
|
|
|
2260
2630
|
if "$LARK_CLI_CMD" profile list 2>/dev/null | grep -F "\"$profile\"" >/dev/null 2>&1; then
|
|
2261
2631
|
agent_detail "lark-cli profile already exists: $profile"
|
|
@@ -2268,13 +2638,38 @@ ensure_lark_cli_profile_locked() {
|
|
|
2268
2638
|
--app-secret-stdin
|
|
2269
2639
|
fi
|
|
2270
2640
|
|
|
2271
|
-
|
|
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
|
+
|
|
2272
2647
|
auth_excludes="$FEISHU_USER_AUTH_EXCLUDES"
|
|
2273
|
-
if
|
|
2274
|
-
|
|
2275
|
-
|
|
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"
|
|
2276
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"
|
|
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"
|
|
2277
2671
|
fi
|
|
2672
|
+
persist_lark_cli_auth_capability_snapshot "$profile"
|
|
2278
2673
|
}
|
|
2279
2674
|
|
|
2280
2675
|
forget_current_bot_after_feishu_start_failure() {
|
|
@@ -2375,6 +2770,8 @@ register_feishu_app() {
|
|
|
2375
2770
|
local default_name
|
|
2376
2771
|
local bot_name
|
|
2377
2772
|
|
|
2773
|
+
initialize_feishu_scope_manifest
|
|
2774
|
+
|
|
2378
2775
|
workdir="$(mktemp -d "${TMPDIR:-/tmp}/aamp-register-feishu-app.XXXXXX")"
|
|
2379
2776
|
register_script="$workdir/register-app.mjs"
|
|
2380
2777
|
if [ -n "$AAMP_RUN_LOG_DIR" ]; then
|
|
@@ -2522,8 +2919,13 @@ NODE
|
|
|
2522
2919
|
|
|
2523
2920
|
bot_name="${bot_name:-$default_name}"
|
|
2524
2921
|
BOT_NAME="$bot_name"
|
|
2525
|
-
|
|
2526
|
-
|
|
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
|
|
2527
2929
|
if [ "$AAMP_TASK_INTERNAL" = "true" ]; then
|
|
2528
2930
|
agent_detail "Feishu Bot 已授权:$BOT_NAME ($APP_ID)"
|
|
2529
2931
|
return 0
|
|
@@ -2677,6 +3079,338 @@ find_cursor_agent_cli() {
|
|
|
2677
3079
|
return 1
|
|
2678
3080
|
}
|
|
2679
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
|
+
|
|
2680
3414
|
resolve_cursor_cli_for_acp() {
|
|
2681
3415
|
command -v cursor
|
|
2682
3416
|
}
|
|
@@ -2719,6 +3453,50 @@ ensure_agent_cli() {
|
|
|
2719
3453
|
return 0
|
|
2720
3454
|
fi
|
|
2721
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
|
+
|
|
2722
3500
|
command -v "$AGENT" >/dev/null 2>&1 || agent_fail "未检测到 $AGENT CLI。请先安装后重新运行脚本。"
|
|
2723
3501
|
}
|
|
2724
3502
|
|
|
@@ -2866,6 +3644,68 @@ resolve_latest_codex_cli_version() {
|
|
|
2866
3644
|
--prefix "$NPM_GLOBAL_PREFIX" 2>>"$ONE_CLICK_LOG" | tail -n 1 | tr -d '[:space:]'
|
|
2867
3645
|
}
|
|
2868
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
|
+
|
|
2869
3709
|
codex_cli_update_available() {
|
|
2870
3710
|
CODEX_CURRENT_VERSION="$1" CODEX_LATEST_VERSION="$2" node -e '
|
|
2871
3711
|
function parse(value) {
|
|
@@ -3004,7 +3844,13 @@ ensure_codex_cli_updated() {
|
|
|
3004
3844
|
codex_bin="$(resolve_codex_cli_for_acp || true)"
|
|
3005
3845
|
[ -n "$codex_bin" ] || return 0
|
|
3006
3846
|
version_before="$(codex_cli_version_number "$codex_bin" || true)"
|
|
3007
|
-
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
|
|
3008
3854
|
version_line="当前 Codex CLI 版本是:${version_before:-未知},最新版本是:${latest_version:-获取失败}"
|
|
3009
3855
|
write_one_click_log "[aamp-one-click] $version_line"
|
|
3010
3856
|
agent_detail "checking Codex CLI update: path=$codex_bin version=${version_before:-unknown}"
|
|
@@ -3015,7 +3861,6 @@ ensure_codex_cli_updated() {
|
|
|
3015
3861
|
comparison_status=$?
|
|
3016
3862
|
fi
|
|
3017
3863
|
if [ "$comparison_status" -eq 1 ]; then
|
|
3018
|
-
printf '%s\n' "$version_line"
|
|
3019
3864
|
agent_detail "Codex CLI is already current: current=${version_before:-unknown} latest=${latest_version:-unknown}"
|
|
3020
3865
|
return 0
|
|
3021
3866
|
fi
|
|
@@ -3104,6 +3949,142 @@ run_codex_login() {
|
|
|
3104
3949
|
return "$status"
|
|
3105
3950
|
}
|
|
3106
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
|
+
|
|
3107
4088
|
print_cursor_gatekeeper_help() {
|
|
3108
4089
|
local cursor_bin
|
|
3109
4090
|
local cursor_real
|
|
@@ -3365,6 +4346,144 @@ ensure_codem_provider_ready() {
|
|
|
3365
4346
|
return "$status"
|
|
3366
4347
|
}
|
|
3367
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
|
+
|
|
3368
4487
|
ensure_agent_login() {
|
|
3369
4488
|
case "$AGENT" in
|
|
3370
4489
|
codex)
|
|
@@ -3385,6 +4504,35 @@ ensure_agent_login() {
|
|
|
3385
4504
|
run_cursor_login_status || agent_fail "cursor CLI 仍未登录。请先执行 'cursor login' 或 'agent login' 完成登录后重新运行脚本。"
|
|
3386
4505
|
fi
|
|
3387
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
|
+
;;
|
|
3388
4536
|
codem)
|
|
3389
4537
|
ensure_codem_local_bin_on_path
|
|
3390
4538
|
codem --version >/dev/null
|
|
@@ -3453,6 +4601,11 @@ resolve_codex_cli_for_acp() {
|
|
|
3453
4601
|
return 0
|
|
3454
4602
|
fi
|
|
3455
4603
|
|
|
4604
|
+
if is_macos && [ -x "$CODEX_CHATGPT_APP_CLI" ]; then
|
|
4605
|
+
printf '%s\n' "$CODEX_CHATGPT_APP_CLI"
|
|
4606
|
+
return 0
|
|
4607
|
+
fi
|
|
4608
|
+
|
|
3456
4609
|
if is_macos && [ -x "$CODEX_APP_CLI" ]; then
|
|
3457
4610
|
printf '%s\n' "$CODEX_APP_CLI"
|
|
3458
4611
|
return 0
|
|
@@ -3461,8 +4614,69 @@ resolve_codex_cli_for_acp() {
|
|
|
3461
4614
|
return 1
|
|
3462
4615
|
}
|
|
3463
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
|
+
|
|
3464
4625
|
build_acp_agent_command() {
|
|
3465
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
|
+
|
|
3466
4680
|
if [ "$AGENT" != "codex" ]; then
|
|
3467
4681
|
return 0
|
|
3468
4682
|
fi
|
|
@@ -3484,7 +4698,7 @@ validate_codex_acp_command() {
|
|
|
3484
4698
|
if [ "$status" -ne 0 ]; then
|
|
3485
4699
|
agent_log "Codex ACP command validation failed:"
|
|
3486
4700
|
printf '%s\n' "$output" >&2
|
|
3487
|
-
agent_fail "codex ACP command is unavailable; check Codex.app or reinstall codex CLI"
|
|
4701
|
+
agent_fail "codex ACP command is unavailable; check ChatGPT.app, Codex.app, or reinstall codex CLI"
|
|
3488
4702
|
fi
|
|
3489
4703
|
}
|
|
3490
4704
|
|
|
@@ -3683,6 +4897,22 @@ run_internal_discover_agents() {
|
|
|
3683
4897
|
if agent_cli_detected cursor; then
|
|
3684
4898
|
agents+=("cursor")
|
|
3685
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
|
|
3686
4916
|
local joined=""
|
|
3687
4917
|
if [ "${#agents[@]}" -gt 0 ]; then
|
|
3688
4918
|
joined="$(IFS=,; printf '%s' "${agents[*]}")"
|
|
@@ -3696,32 +4926,79 @@ process.stdout.write(JSON.stringify({ agents }));
|
|
|
3696
4926
|
prepare_internal_agent_environment() {
|
|
3697
4927
|
[ -n "$AGENT" ] || agent_fail "internal Agent preparation requires --agent"
|
|
3698
4928
|
validate_agent_name "$AGENT"
|
|
3699
|
-
|
|
4929
|
+
ensure_agent_selection_available "$AGENT"
|
|
4930
|
+
load_agent_metadata
|
|
4931
|
+
if [ "$AGENT_EXECUTION_LOCATION" = "local" ]; then
|
|
4932
|
+
source_lark_env
|
|
4933
|
+
fi
|
|
3700
4934
|
ensure_agent_cli
|
|
3701
4935
|
}
|
|
3702
4936
|
|
|
3703
4937
|
run_internal_register_binding() {
|
|
3704
4938
|
[ -n "$AGENT" ] || agent_fail "internal Bot registration requires --agent"
|
|
3705
4939
|
validate_agent_name "$AGENT"
|
|
3706
|
-
|
|
4940
|
+
ensure_agent_selection_available "$AGENT"
|
|
4941
|
+
load_agent_metadata
|
|
3707
4942
|
register_feishu_app
|
|
3708
|
-
|
|
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
|
|
3709
4948
|
}
|
|
3710
4949
|
|
|
3711
4950
|
run_internal_prepare_agent() {
|
|
3712
4951
|
prepare_internal_agent_environment
|
|
3713
4952
|
ensure_codex_cli_updated
|
|
3714
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
|
|
3715
4958
|
maybe_mock_fail "agent-login"
|
|
3716
4959
|
ensure_acpx
|
|
3717
4960
|
build_acp_agent_command
|
|
3718
|
-
|
|
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
|
|
3719
4992
|
}
|
|
3720
4993
|
|
|
3721
4994
|
run_internal_ensure_profile() {
|
|
3722
4995
|
AAMP_TASK_INTERNAL_BINDING_JSON=""
|
|
3723
4996
|
IFS= read -r AAMP_TASK_INTERNAL_BINDING_JSON <&"$AAMP_TASK_INTERNAL_INPUT_FD" || true
|
|
3724
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"
|
|
3725
5002
|
APP_ID="$(binding_json_field bot.app_id)"
|
|
3726
5003
|
APP_SECRET="$(binding_json_field bot.app_secret)"
|
|
3727
5004
|
BOT_NAME="$(binding_json_field bot.display_name)"
|
|
@@ -3736,7 +5013,9 @@ run_internal_ensure_profile() {
|
|
|
3736
5013
|
|
|
3737
5014
|
run_internal_action() {
|
|
3738
5015
|
AAMP_TASK_INTERNAL="true"
|
|
3739
|
-
|
|
5016
|
+
if [ "$AAMP_TASK_ACTION" != "__probe-profile" ]; then
|
|
5017
|
+
ensure_node_toolchain
|
|
5018
|
+
fi
|
|
3740
5019
|
case "$AAMP_TASK_ACTION" in
|
|
3741
5020
|
__discover-agents)
|
|
3742
5021
|
run_internal_discover_agents
|
|
@@ -3747,6 +5026,9 @@ run_internal_action() {
|
|
|
3747
5026
|
__prepare-agent)
|
|
3748
5027
|
run_internal_prepare_agent
|
|
3749
5028
|
;;
|
|
5029
|
+
__probe-profile)
|
|
5030
|
+
run_internal_probe_profile
|
|
5031
|
+
;;
|
|
3750
5032
|
__ensure-profile)
|
|
3751
5033
|
run_internal_ensure_profile
|
|
3752
5034
|
;;
|
|
@@ -3760,6 +5042,92 @@ task_agent_controller_path() {
|
|
|
3760
5042
|
printf '%s/bin/feishu-task-agent-controller.mjs\n' "$(task_agent_global_package_dir)"
|
|
3761
5043
|
}
|
|
3762
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
|
+
|
|
3763
5131
|
run_task_agent_controller() {
|
|
3764
5132
|
local controller bootstrap_path install_command
|
|
3765
5133
|
controller="$(task_agent_controller_path)"
|
|
@@ -3772,6 +5140,17 @@ run_task_agent_controller() {
|
|
|
3772
5140
|
export AAMP_TASK_NPX_BIN="$NPX_BIN"
|
|
3773
5141
|
export AAMP_TASK_ACP_BRIDGE_PKG="$ACP_BRIDGE_PKG"
|
|
3774
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
|
|
3775
5154
|
export AAMP_TASK_CODEX_ACP_PKG="$CODEX_ACP_PKG"
|
|
3776
5155
|
export AAMP_TASK_AGENT_VERSION
|
|
3777
5156
|
export AAMP_TASK_DEFAULT_AGENT="$AGENT"
|
|
@@ -3829,13 +5208,19 @@ main() {
|
|
|
3829
5208
|
return 0
|
|
3830
5209
|
fi
|
|
3831
5210
|
|
|
5211
|
+
apply_task_agent_package_override_policy
|
|
5212
|
+
|
|
3832
5213
|
case "$AAMP_TASK_ACTION" in
|
|
3833
|
-
__discover-agents|__register-binding|__prepare-agent|__ensure-profile)
|
|
5214
|
+
__discover-agents|__register-binding|__prepare-agent|__probe-profile|__ensure-profile)
|
|
3834
5215
|
run_internal_action
|
|
3835
5216
|
return 0
|
|
3836
5217
|
;;
|
|
3837
5218
|
esac
|
|
3838
5219
|
|
|
5220
|
+
if [ "$AGENT" = "aime" ]; then
|
|
5221
|
+
ensure_agent_selection_available "$AGENT"
|
|
5222
|
+
fi
|
|
5223
|
+
|
|
3839
5224
|
trap cleanup EXIT INT TERM HUP
|
|
3840
5225
|
init_log_run
|
|
3841
5226
|
validate_mock_fail_stage
|