@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.
@@ -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
- FEISHU_APP_SCOPES_TENANT="${FEISHU_APP_SCOPES_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:delete,task:attachment:file:download,task:attachment:read,task:attachment:upload,task:attachment:write,task:comment:delete,task:comment:read,task:comment:write,task:comment:writeonly,task:task:delete,task:task:read,task:task:write,task:task:writeonly,task:tasklist:delete,task:tasklist:read,task:tasklist:write,task:tasklist:writeonly,search:docs:read,base:app:copy,base:app:create,base:app:read,base:app:update,base:block:create,base:block:delete,base:block:read,base:block:update,base:dashboard:create,base:dashboard:delete,base:dashboard:read,base:dashboard:update,base:field:create,base:field:delete,base:field:read,base:field:update,base:form:create,base:form:delete,base:form:read,base:form:update,base:history:read,base:record:create,base:record:delete,base:record:read,base:record:update,base:role:create,base:role:delete,base:role:read,base:role:update,base:table:create,base:table:delete,base:table:read,base:table:update,base:view:read,base:view:write_only,base:workflow:create,base:workflow:read,base:workflow:update,board:whiteboard:node:create,board:whiteboard:node:read,calendar:calendar.event:create,calendar:calendar.event:delete,calendar:calendar.event:read,calendar:calendar.event:reply,calendar:calendar.event:update,calendar:calendar.free_busy:read,calendar:calendar:create,calendar:calendar:delete,calendar:calendar:read,calendar:calendar:update,contact:user.base:readonly,contact:user.basic_profile:readonly,docs:document.media:download,docs:document.media:upload,docs:document:export,docs:document:import,docx:document:create,docx:document:readonly,docx:document:write_only,drive:drive.metadata:readonly,drive:file:download,drive:file:upload,im:chat.managers:write_only,im:chat.members:read,im:chat.members:write_only,im:chat.moderation:read,im:chat:moderation:write_only,im:message.pins:read,im:message.pins:write_only,im:message.reactions:read,im:message.reactions:write_only,im:message:recall,mail:user_mailbox.event.mail_address:read,mail:user_mailbox.mail_contact:read,mail:user_mailbox.message.address:read,mail:user_mailbox.message.body:read,mail:user_mailbox.message.subject:read,mindnote:node:create,mindnote:node:read,minutes:minutes.basic:read,minutes:minutes.media:export,minutes:minutes:readonly,sheets:spreadsheet.meta:read,sheets:spreadsheet.meta:write_only,sheets:spreadsheet:create,sheets:spreadsheet:read,sheets:spreadsheet:write_only,slides:presentation:create,slides:presentation:read,slides:presentation:update,slides:presentation:write_only,task:custom_field:read,task:custom_field:write,task:section:read,task:section:write,vc:meeting.bot.join:write,vc:meeting.meetingevent:read,vc:meeting.message:write,vc:record:readonly,wiki:member:create,wiki:member:retrieve,wiki:member:update,wiki:node:copy,wiki:node:create,wiki:node:move,wiki:node:read,wiki:node:retrieve,wiki:space:read,wiki:space:retrieve,wiki:space:write_only}"
58
- FEISHU_APP_SCOPES_USER="${FEISHU_APP_SCOPES_USER:-im:message,im:message:readonly,im:resource,cardkit:card:write,task:task,task:comment,task:task:readonly,task:comment:readonly,task:attachment:delete,task:attachment:file:download,task:attachment:read,task:attachment:upload,task:attachment:write,task:comment:delete,task:comment:read,task:comment:write,task:comment:writeonly,task:task:delete,task:task:read,task:task:write,task:task:writeonly,task:tasklist:delete,task:tasklist:read,task:tasklist:write,task:tasklist:writeonly,search:docs:read,search:message,base:app:copy,base:app:create,base:app:read,base:app:update,base:block:create,base:block:delete,base:block:read,base:block:update,base:dashboard:create,base:dashboard:delete,base:dashboard:read,base:dashboard:update,base:field:create,base:field:delete,base:field:read,base:field:update,base:form:create,base:form:delete,base:form:read,base:form:update,base:history:read,base:record:create,base:record:delete,base:record:read,base:record:update,base:role:create,base:role:delete,base:role:read,base:role:update,base:table:create,base:table:delete,base:table:read,base:table:update,base:view:read,base:view:write_only,base:workflow:create,base:workflow:read,base:workflow:update,board:whiteboard:node:create,board:whiteboard:node:read,calendar:calendar.event:create,calendar:calendar.event:delete,calendar:calendar.event:read,calendar:calendar.event:reply,calendar:calendar.event:update,calendar:calendar.free_busy:read,calendar:calendar:create,calendar:calendar:delete,calendar:calendar:read,calendar:calendar:update,contact:user.base:readonly,contact:user.basic_profile:readonly,contact:user:search,docs:document.media:download,docs:document.media:upload,docs:document:export,docs:document:import,docx:document:create,docx:document:readonly,docx:document:write_only,drive:drive.metadata:readonly,drive:file:download,drive:file:upload,im:chat.managers:write_only,im:chat.members:read,im:chat.members:write_only,im:chat.moderation:read,im:chat.nickname:read,im:chat.nickname:write,im:chat.user_setting:read,im:chat.user_setting:write,im:chat:read,im:chat:update,im:chat:create_by_user,im:chat:moderation:write_only,im:feed.flag:read,im:feed.flag:write,im:feed.shortcut:read,im:feed.shortcut:write,im:feed_group_v1:read,im:feed_group_v1:write,im:message.group_msg:get_as_user,im:message.p2p_msg:get_as_user,im:message.pins:read,im:message.pins:write_only,im:message.reactions:read,im:message.reactions:write_only,im:message:recall,mail:event,mail:user_mailbox.event.mail_address:read,mail:user_mailbox.mail_contact:read,mail:user_mailbox.message.address:read,mail:user_mailbox.message.body:read,mail:user_mailbox.message.subject:read,mindnote:node:create,mindnote:node:read,minutes:minutes.artifacts:read,minutes:minutes.basic:read,minutes:minutes.media:export,minutes:minutes.search:read,minutes:minutes.upload:write,minutes:minutes:readonly,minutes:minutes:update,profile:user_profile:read,sheets:spreadsheet.meta:read,sheets:spreadsheet.meta:write_only,sheets:spreadsheet:create,sheets:spreadsheet:read,sheets:spreadsheet:write_only,slides:presentation:create,slides:presentation:read,slides:presentation:update,slides:presentation:write_only,task:custom_field:read,task:custom_field:write,task:section:read,task:section:write,vc:meeting.bot.join:write,vc:meeting.meetingevent:read,vc:meeting.message:write,vc:meeting.search:read,vc:note:read,vc:record:readonly,wiki:member:create,wiki:member:retrieve,wiki:member:update,wiki:node:copy,wiki:node:create,wiki:node:move,wiki:node:read,wiki:node:retrieve,wiki:space:read,wiki:space:retrieve,wiki:space:write_only}"
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
- FEISHU_USER_AUTH_DOMAINS="${FEISHU_USER_AUTH_DOMAINS:-base,calendar,contact,docs,im,mail,mindnotes,minutes,note,sheets,slides,task,vc,wiki}"
62
- FEISHU_USER_AUTH_EXCLUDES="${FEISHU_USER_AUTH_EXCLUDES:-im:message.send_as_user,mail:user_mailbox.message:send,mail:user_mailbox.rule:read,mail:user_mailbox.folder:write,mail:user_mailbox.rule:write,mail:user_mailbox.message:modify,mail:user_mailbox.message:readonly,mail:user_mailbox.folder:read,mail:user_mailbox.mail_contact:write,mail:user_mailbox:readonly}"
63
- FEISHU_USER_AUTH_REQUIRED_SCOPES="${FEISHU_USER_AUTH_REQUIRED_SCOPES:-im:message im:message:readonly im:resource cardkit:card:write task:task task:comment task:task:readonly task:comment:readonly task:attachment:delete task:attachment:file:download task:attachment:read task:attachment:upload task:attachment:write task:comment:delete task:comment:read task:comment:write task:comment:writeonly task:task:delete task:task:read task:task:write task:task:writeonly task:tasklist:delete task:tasklist:read task:tasklist:write task:tasklist:writeonly search:docs:read search:message base:app:copy base:app:create base:app:read base:app:update base:block:create base:block:delete base:block:read base:block:update base:dashboard:create base:dashboard:delete base:dashboard:read base:dashboard:update base:field:create base:field:delete base:field:read base:field:update base:form:create base:form:delete base:form:read base:form:update base:history:read base:record:create base:record:delete base:record:read base:record:update base:role:create base:role:delete base:role:read base:role:update base:table:create base:table:delete base:table:read base:table:update base:view:read base:view:write_only base:workflow:create base:workflow:read base:workflow:update board:whiteboard:node:create board:whiteboard:node:read calendar:calendar.event:create calendar:calendar.event:delete calendar:calendar.event:read calendar:calendar.event:reply calendar:calendar.event:update calendar:calendar.free_busy:read calendar:calendar:create calendar:calendar:delete calendar:calendar:read calendar:calendar:update contact:user.base:readonly contact:user.basic_profile:readonly contact:user:search docs:document.media:download docs:document.media:upload docs:document:export docs:document:import docx:document:create docx:document:readonly docx:document:write_only drive:drive.metadata:readonly drive:file:download drive:file:upload im:chat.managers:write_only im:chat.members:read im:chat.members:write_only im:chat.moderation:read im:chat.nickname:read im:chat.nickname:write im:chat.user_setting:read im:chat.user_setting:write im:chat:read im:chat:update im:chat:create_by_user im:chat:moderation:write_only im:feed.flag:read im:feed.flag:write im:feed.shortcut:read im:feed.shortcut:write im:feed_group_v1:read im:feed_group_v1:write im:message.group_msg:get_as_user im:message.p2p_msg:get_as_user im:message.pins:read im:message.pins:write_only im:message.reactions:read im:message.reactions:write_only im:message:recall mail:event mail:user_mailbox.event.mail_address:read mail:user_mailbox.mail_contact:read mail:user_mailbox.message.address:read mail:user_mailbox.message.body:read mail:user_mailbox.message.subject:read mindnote:node:create mindnote:node:read minutes:minutes.artifacts:read minutes:minutes.basic:read minutes:minutes.media:export minutes:minutes.search:read minutes:minutes.upload:write minutes:minutes:readonly minutes:minutes:update profile:user_profile:read sheets:spreadsheet.meta:read sheets:spreadsheet.meta:write_only sheets:spreadsheet:create sheets:spreadsheet:read sheets:spreadsheet:write_only slides:presentation:create slides:presentation:read slides:presentation:update slides:presentation:write_only task:custom_field:read task:custom_field:write task:section:read task:section:write vc:meeting.bot.join:write vc:meeting.meetingevent:read vc:meeting.message:write vc:meeting.search:read vc:note:read vc:record:readonly wiki:member:create wiki:member:retrieve wiki:member:update wiki:node:copy wiki:node:create wiki:node:move wiki:node:read wiki:node:retrieve wiki:space:read wiki:space:retrieve wiki:space:write_only}"
64
- ACP_BRIDGE_PKG="${ACP_BRIDGE_PKG:-@zengxingyuan/aamp-acp-bridge@0.1.28-dev.20}"
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:-@zengxingyuan/aamp-feishu-bridge@0.1.51}"
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.0-dev.174"
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 Use this Agent for every new binding in the command.
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
- npm_install_global() {
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 "$NPM_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 "$NPM_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 cursor" ;;
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 "暂未检测到本地智能体。请先安装并登录 Codex 或 Cursor CLI 后重试。"
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" FEISHU_USER_AUTH_DOMAINS="$FEISHU_USER_AUTH_DOMAINS" node -e '
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.FEISHU_USER_AUTH_DOMAINS || "").split(",").map((item) => item.trim()).filter(Boolean),
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="$FEISHU_USER_AUTH_REQUIRED_SCOPES" FEISHU_USER_AUTH_EXCLUDES="$FEISHU_USER_AUTH_EXCLUDES" node -e '
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
- console.log([...new Set(excludes)].join(","));
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
- auth_excludes="$(normalize_lark_cli_auth_excludes "$auth_excludes")"
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 --domain "$FEISHU_USER_AUTH_DOMAINS" --scope "$FEISHU_USER_AUTH_REQUIRED_SCOPES"
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 --domain "$FEISHU_USER_AUTH_DOMAINS" --scope "$FEISHU_USER_AUTH_REQUIRED_SCOPES" --exclude "$auth_excludes"
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
- agent_detail "ensuring lark-cli user auth domains for profile: $profile"
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 lark_cli_user_auth_satisfied "$profile"; then
2275
- agent_detail "lark-cli user auth already has required scopes for profile: $profile"
2276
- else
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
- LARK_CLI_PROFILE="$(task_profile_name_for_app_id "$APP_ID")"
2527
- ensure_lark_cli_profile "$APP_ID" "$APP_SECRET" "$LARK_CLI_PROFILE"
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="$(resolve_latest_codex_cli_version || true)"
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
- source_lark_env
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
- source_lark_env
4940
+ ensure_agent_selection_available "$AGENT"
4941
+ load_agent_metadata
3713
4942
  register_feishu_app
3714
- 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")\"}"
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
- 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:-}")\"}"
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
- ensure_node_toolchain
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